C语言数组详解

一维数组的创建和初始化

数组的创建

数组创建的格式:

type_t	 arr_name	[const_n]
//type_t 是指数组的元素类型
//arr_name是自定义的数组的名称
//const_n是一个常量表达式,用来指定数组的大小

数组创建的具体方式:

//正确的方式
int arr1[10];	//类型是int、名称是arr1、大小是10

char arr2[1+2];	//类型是char、名称是arr2、大小是3

#define N 5		//N是一个标识符常量
float arr3[N];	//类型是float、名称是arr3、大小是5

int a = 8;
double arr4[a];	//该方式在C99标准之前不支持,因为[]中该放一个常量
				//该方式在C99标准中支持该方式,称为变长数组
.
.
.
//错误的方式
int arr5[];		//只创建数组时,没有定义数组大小

数组的初始化

数组的初始化是指,在创建数组的同时往数组里放几个合理元素

//正确的方式
int arr1[5] = {1, 2, 3, 4, 5};//数组大小为5,放入了五个元素

int arr2[10] = {1, 2, 3, 4, 5, 6};//数组大小为10,放入了6个元素,其余元素自动为0
				 //当初始化的元素小于数组大小时,称为不完全初始化,其余元素自动全为0

//定义字符串时,只能用char,正确的字符串末尾是应有一个'\0'的
char arr3[5] = "abcd";//数组大小是5,放入了一个字符串,看似是4个字母,结尾默认有一个'\0'
					 //所以该方法数组的大小至少要比想填入字符串的字母个数大于1

char arr4[] = "abcdef";//该方法定义字符串也是可以的,相当于把字符串的地址给了数组名arr4

int arr5[5] = {'a', 'b', 'c', 'd', 'e'};//该方法后面没有自动补'\0'

//错误的方式
int arr6[] = {1, 2, 3};//没有说明数组的大小就初始化,因为不知道大小怎么知道放几个元素呢

int arr[3] = {1, 2, 3, 4, 5, 6};//放入的元素数大于了数组的大小

char arr[5] = "abcde"//因为字符串默认有一个'\0',所以数组的空间大小不够了

一维数组的使用

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };//创建了一个数组,里面存放10个int类型的元素,元素全为0
    int size = sizeof(arr) / sizeof(arr[0]);//计算数组的大小
    //通过下标引用操作符[]访问数组的每个空间
    //对数组内容进行赋值
    for(int i = 0; i < size; ++i)
    {
        arr[i] = i;
	}
    //输出数组的内容
    for(int i = 0; i < size; ++i)
    {
        printf("%d ", arr[i]);
	}
    return 0;
}

总结:

1. 数组是使用下标来访问的,下标从0开始
1. 使用操作符[ ]和下标,可以访问数组的每块空间

一维数组在内存中的存储

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    int size = sizeof(arr) / sizeof(arr[0]);
    for(int i = 0; i < size; ++i)
    {
        printf("&arr[%d] = %p\n", i, &arr[i]);
	}
    return 0;
}

image-20220514100815332

可以看到随着数组下标的增长,元素的地址也在有规律的递增。

由此可以得出结论:数组在内存中是连续存放的,这也是为什么数组可以用下标来访问每一个空间的原因

image-20220514101009429

二维数组的创建和初始化

一维数组我们知道是在一条直线上存储数据,也就是在一条轴上

那么二维数组可以想象成在x轴上和y轴上都存储数据

二维数组的创建

//跟一维数组大同小异
int arr1[3][4];		//第一个[]代表有几行元素,第二个[]代表有几列元素
double arr2[5][6];
.
.
.

二维数组的初始化

//正确的方法

//使用{}初始化时,初始化了指定行和列的元素
int arr1[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
//初始化了第一行的前两列元素和第二行的前三列元素,其余元素自动为0
int arr2[3][4] = {{1, 2}, {3, 4, 5}};

//不使用{}初始化时,先初始化第一行的每一列,再初始化第二行的每一列,以此类推
int arr3[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
//只初始化了第一行的前四列和第二行的前两列,其余元素自动为0
int arr4[3][4] = {{1, 2, 3, 4, 56};

//也可以只指定列数,不指定行数,默认从第一行开始初始化,列数满了自动换行
int arr5[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
//第一行只初始化了前两列,后两列自动为0,然后换行初始化了前三列,后一列自动为0
int arr6[][4] = {{1, 2}, {3, 4, 5}};

//错误的方法
//只指定行数时,是不可以的,因为不知道每一列放多少元素,所以没法初始化
int arr6[3][] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

二维数组的使用

跟一维数组极其相似

#include <stdio.h>
int main()
{
    int arr[3][4] = { 0 };//创建了一个三行四列的数组,元素大小为12个,全为0
    for (int i = 0; i < 3; ++i)//按行初始化
    {
        for (int j = 0; j < 4; ++j)//按列初始化
        {
            arr[i][j] = i * 4 + j;
        }
    }
    for (int i = 0; i < 3; ++i)//按行输出
    {
        for (int j = 0; j < 4; ++j)//按列输出
        {
            printf("%-2d ", arr[i][j]);
        }
        printf("\n");//输出完每一行,换行
    }
    return 0;
}

image-20220514103848970

二维数组在内存中的存储

像一维数组一样,打印出每一个元素的地址进行观察

#include <stdio.h>
int main()
{
	int arr[3][4];
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

image-20220514104105604

我们可以看出,随着下标的增长,元素的地址也在有规律的增大,由此我们可以得出二维数组在内存中也是连续存储的

image-20220514104243516

数组越界

数组是有大小的,也就是说数组的下标是有范围限制的

数组的下标规定是从0开始的,假设数组有n个元素,那么最后一个元素下标为n - 1

#include <stdio.h>
int main()
{
	int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  	for(int i = 0; i <= 10; ++i)
 	{
   	 	printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
	}
	return 0;
}

那么当然二维数组也会存在是否越界的问题

数组作为函数参数

在写代码时,数组经常用来作为参数传给某个函数,比如冒泡排序

正确的写法

#include <stdio.h>
void bubble_sort(int arr[], int size)
{
    for(int i = 0; i < size - 1; ++i)
    {
        for(int j = 0; j < size - 1 - i; ++j)
        {
            if(arr[j] < arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 3, 1, 7, 5, 8, 9, 0, 2, 4, 6 };
	int size = sizeof(arr) / sizeof(arr[0]);
	printf("before:\n");
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", arr[i]);
	}
	bubble_sort(arr, size);
	printf("\nafter:\n");
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

image-20220514110035901

错误的写法

#include <stdio.h>
void bubble_sort(int arr[])
{
	int size = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < size - 1; ++i)
	{
		for (int j = 0; j < size - 1 - i; ++j)
		{
			if (arr[j] < arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 3, 1, 7, 5, 8, 9, 0, 2, 4, 6 };
	int size = sizeof(arr) / sizeof(arr[0]);
	printf("before:\n");
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", arr[i]);
	}
	bubble_sort(arr);
	printf("\nafter:\n");
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

image-20220514110201505

可以看到错误的写法没有产生排序的效果,

两种方法的区别就是数组的大小一个是在传参前计算好传过去的,另一个是在传参之后在函数内部计算的。那么就极有可能是这个区别导致了方法二没有产生效果

image-20220514111023381

image-20220514111034001

image-20220514111101333

image-20220514111111078

我们可以看到size的大小发生了变化,还可以看到第二个sizeof(arr)下有警告提示

我们先看下面

数组名是什么

#include <stdio.h>
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5 };
	printf("%p\n", &arr[0]);
	printf("%p\n", arr);
	printf("%d\n", arr[0]);
	printf("%d\n", *arr);
	printf("%d\n", sizeof(arr[0]));
	printf("%d\n", sizeof(arr));
	printf("%p\n", arr);
	printf("%p\n", arr + 1);
	printf("%p\n", &arr);
	printf("%p\n", &arr + 1);
	return 0;
}

image-20220514112724620

可以看到

数组首元素arr[0]的地址和数组名arr的地址相同

数组首元素arr[0]的内容和*arr的内容相同

由此可以得出,数组名就是首元素地址,解引用数组名就是数组的第一个元素

还可以看到

sizeof(arr[0])计算数组第一个元素的大小为4

sizeof(arr)计算数组名的大小为4

arr + 1的地址比arr的地址大4

&arr + 1的地址比&arr的地址大40

由此可以得出,在两种情况下,数组名arr不是数组首元素的地址,*arr不是数组首元素

  1. sizeof(数组名),这时,数组名是整个数组
  2. &arr,这时,数组名是整个数组的地址

这时,我们就可以回答上面遗留的问题,为什么冒泡排序的方法二是错的

因为数组名是首元素地址(不包括那两种特殊情况),在传参时,形参接收到的是一个地址,而当我们对形参指针sizeof时,计算的是一个指针的大小,而指针大小在32位平台下是4个字节,在64位平台下是8个字节,而上面我们用的是32位平台。
还可以看到

sizeof(arr[0])计算数组第一个元素的大小为4

sizeof(arr)计算数组名的大小为4

arr + 1的地址比arr的地址大4

&arr + 1的地址比&arr的地址大40

由此可以得出,在两种情况下,数组名arr不是数组首元素的地址,*arr不是数组首元素

  1. sizeof(数组名),这时,数组名是整个数组
  2. &arr,这时,数组名是整个数组的地址

这时,我们就可以回答上面遗留的问题,为什么冒泡排序的方法二是错的

因为数组名是首元素地址(不包括那两种特殊情况),在传参时,形参接收到的是一个地址,而当我们对形参指针sizeof时,计算的是一个指针的大小,而指针大小在32位平台下是4个字节,在64位平台下是8个字节,而上面我们用的是32位平台。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值