细到极致的数组讲解,确定不点开看看?

本文详细介绍了数组的概念、创建和初始化,包括一维数组和二维数组。强调了数组在内存中的存储方式,指出一维数组和二维数组在内存中都是连续存储的。此外,还讨论了数组越界问题和数组作为函数参数时的注意事项。通过实例解析了数组的使用和访问方式,帮助读者深入理解数组操作。
摘要由CSDN通过智能技术生成

目录

一维数组的创建和初始化

一维数组的使用

一维数组在内存中的存储

二维数组的创建和初始化

二维数组的使用

二维数组在内存中的存储

数组越界

数组作为函数参数


一维数组的创建和初始化

数组的概念:一组同类型的元素的集合。

数组是为了方便储存一堆同类数据而被创建的。比如现在要输入100个同学的成绩,总不能定义100个变量,一个一个输入吧,太麻烦了,这时候用到数组就要方便得多。

数组也是有维度的,一维、二维、三维... 二维乃至后面的可以存更多的数据,我们先从一维的看起。

数组的创建方式:数组有数组名,数据类型, 常量表达式(用来指定数组大小)

  来看三种形式的数组创建:

	int n = 0;
	int arr[10] = { 0 };
	int arr[1 + 5] = { 0 };
	int arr[n] = { 0 };

首先,第一种int arr[10]就是上面提到的;第二种int arr[1+5],这种也是可行的,因为里面还是整型常量嘛;

但是注意,第三种,int arr[n] 里面是变量,即使给变量赋了值,它还是变量,在C99编译器底下是通的过的,但是C99之前的编译器底下就会报错。

创建说完了,下面来看初始化。初始化也就是给数组赋初始值,如:

	int arr[5] = { 1,2,3,4,5 };

还有一种初始化,叫不完全初始化

 调试起来可以看到,不完全初始化,其余元素默认为0 。

当然,初始化不只有整型数组,字符数组也有初始化。

先来看个例子:

现在有两个字符数组,其中一个是放的单个字符,一个是字符串。

	char arr1[5] = { 'a','b','c' };
	char arr2[5] = "abc";

就结果来说,它们不完全初始化里面放的东西是一样的,都是 \0 ,但其实本质不同。

 arr1里面就放了a b c三个字符,arr2里面还有末尾的 \0 ,这要注意。

还有一种初始化,就是不加 [ ] 里的整型常量。

	char arr1[] = { 'a','b','c' };              
	char arr2[] = "abc";

调试来看:

arr1里面有3个元素,arr2里面有4个,说明编译器会自己去数有几个元素,同时也应证了上文。

一维数组的使用

这部分大家都已经熟透了,我也就不多嚼舌了。

一般就是通过循环的形式输入/打印数组元素,注意数组下标从0开始,并且数组大小可以通过sizeof计算得到。

一维数组在内存中的存储

来说存储问题,这一部分就比较重要了。

首先要知道,一维数组在内存中是连续存储的。怎么理解,打印一个数组地址来看一下:

 可以发现,每个地址之间差了4个字节,并且由低到高存放 。

因此,一维数组在内存中存储的形式可以如下图表示:

二维数组的创建和初始化

一维数组是基石,学好了一维数组后,我们才能接着使用后面的二维、三维数组。

二维数组无非就是能存储更多的数据,创建形式就像是个矩阵

形如 arr[3][4]; 3代表行,4代表列。 因此,它可以存放3*4=12个元素。

创建方式可以是直接输入12个元素:

int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

也可以自己分行列:

	int arr[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };

一维数组中存在不完全初始化,同样的,二维数组中也存在。

如:

 这时,只有6个元素,一列有4个,所以第二行放2个,其余默认补0.

如果我们想自己决定元素放哪,可以加 { }

此外,这种初始化是不行的。

	int arr[][] = { 1,2,3,4,5,6 };

二维数组中行可以省略,列不能省略!

比如这个数组中:

	int arr[][3] = { 1,2,3,4,5,6,7,8};

如果有列:就知道一行是放3个元素,所以,第一行放3个:1,2,3;第二行放3个:4,5,6;第三行不够3个放2个:7,8 。所以就是3行3列确定了。

如果只有行:我们只知道有3行,却不知道每行放几个元素,可能是1个,2个,3个......编译器就跑不过去了。这和二维数组在内存中存放也有关,后面会提到。

二维数组除了上述理解外,还有一种理解。

就是将二维数组看成是一维数组。

 二维数组的第一行看成是一维数组的第一个元素,  二维数组的第二行看成是一维数组的第二个元素,二维数组的第三行看成是一维数组的第三个元素。

也就是说,可以把二维数组理解为 “一维数组的数组”。

在访问一维数组arr1[9]每个元素时,是arr1[0],arr1[1]的形式,同样的,访问二维数组每行元素时,第一行arr[0][i] , 第二行arr[1][i] , 第三行arr[2][i],这里的arr[0] ,arr[1],arr[2]就相当于数组名,后面是下标,以此形式来访问。

二维数组的使用

使用和一维数组类似,只是输入数据和打印数据时多了一层循环,因为二维数组有行和列。

int arr[][3] = { 1,2,3,4,5,6,7,8};
	int i, j;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			scanf("%d", &arr[i][j]);
		}
	}

 关于里面深层的问题,如二维数组传参等内容,涉及到二级指针,我们后面再讲。

二维数组在内存中的存储

遇事不决,代码解决

 观察二维数组的地址,不难发现和一维数组中一样,二维数组中的地址也是连续存放的,并且也都是由低到高相差4个字节。

再画个图来解释一下,具体存放情况

 

 其实二维数组在内存中是如图存放的,像是把矩阵切层后一条往后排。与一维数组类似,只是访问形式有差别。

数组越界

数组越界通俗来说就是数组的下标超出了范围。

比如:

 这里数组大小是[3][3],但是访问的时候超出了这个界限,就称为数组越界。但是此时编译器没有报错,而是继续向后访问,但是具体的值我们就不知道了。

此外,二维数组越界时,会访问到我们自己数组里面的数据。

举个例子:这里访问arr[0][3](实际越界了)时,就是访问的数组里的第4个元素arr[1][0],也就是4 。同样的,访问arr[0][4](实际越界),访问的就是数组的第5个元素,arr[1][1],也就是5 。

数组作为函数参数

数组作为函数参数传参时,传的是数组首元素地址。

可以看到,两种形式地址是一样的。

所以说,arr就代表了数组首元素的地址,但是有两个特例。

1、sizeof(arr)里面,代表的是整个数组,计算的是整个数组的大小

2、&arr代表的也是整个数组的地址。

下面来看:

	printf("%p\n", &arr[0]);
	printf("%p\n", arr);
	printf("%p\n", &arr);

这三条打印的是什么呢?

第一句:毫无疑问,是首元素地址。

第二句:根据上面的解释,也是首元素地址。

第三句:应该是整个数组的地址(不是说打印整个数组每个数据的地址)

来看结果:

 三条语句打印结果一样,有的人可能要问了,上面不是说第三句是打印整个数组的地址吗,怎么也是一样的结果,因为整个数组的地址也是从arr[0]开始的呀。不是说打印每个元素的地址,而是整个数组起始位置,也就是arr[0]的地址。

再对比下面的情形你就完全懂了

 对比来看,第一句到第四句,因为&arr[0]+1所以打印的是arr[1]的地址;

第二句到第五句,arr代表的是数组首元素地址,所以和上面一样

关键是第三句到第六句,因为&arr是整个数组的地址,所以,&arr+1跳过了整个数组的长度(40字节),因此,地址增加了40 。

上面都是以一维数组举例的,下面我们来看二维数组。

其实也是类似的。二维数组的首元素地址就是第一行元素的地址,也就是arr[0][0]

看似是第一行第一列元素的地址,其实本质上还是有区别的,虽然它们都指向[0][0],但是意义不同。     这里就可以结合我们上面说的“将二维数组想象成一维数组”来理解。

 搞清楚这一点,就可以计算二维数组的行列数

行:sizeof(arr)/sizeof(arr[0])

列:sizdof(arr[0])/sizeof(arr[0][0])

那么关于数组的内容也就到这里了,感谢大家!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值