引言
本篇文章不讲数组的定义,只讲讲数组在内存中的地址及表现形式。如果你对数组不是那么地了解,可以先看看以下两篇博主的文章
C语言一维数组
C语言二维数组
一维数组
一维数组地址
首先我们先定义一个简单一维数组arr1[4]
。
int arr1[4] = {1, 2, 3, 4};
接着分别打印arr1
、&arr1
、*arr1
。
printf("address1 is %d.\naddress2 is %d.\nvalue is %d.\n", arr1, &arr1, *arr1);
输出:
从输出结果可以发现,这里的address1和address2的值是一样的。下面我们再分别打印arr1+1
和&arr1+1
。
printf("address1 is %d.\naddress2 is %d.\nvalue is %d.\n", arr1+1, &arr1+1, *arr1);
输出:
这时的address1和address2的值是不一样的。都是进行了+1
操作,为什么两者的结果会不一致呢?这就和取地址符号&
有关。
大家都知道,对于一个一维数组,数组名代表的是数组首元素的地址,在以上的例子里,这个地址就是数组名arr1
对应的地址,即6422032
。&arr1
代表的是arr1[4]
这一整个数组的地址,而不是数组中某个元素的地址。
通过输出结果我们可以发现,整个数组的地址和数组首元素的地址是一样的,不同点就是两者所表示的范围不同。对于arr1
,对其进行+1
操作后,对应的地址是数组第二个元素的地址,即6422036
。对于&arr1
,对其进行+1
操作后,对应的地址是该数组最后一个元素的最后一个字节的地址+1,即6422048
。(在这里数组为int类型,数组每个元素占用4个字节。)
一维数组的值
分析完地址,我们来看看数组的值。运行以下代码。
printf("value1 is %d.\n", *arr1);
printf("value2 is %d.\n", *arr1+5);
printf("value3 is %d.\n", *(arr1+1));
printf("value4 is %d.\n", *(arr1+4));
输出:
因为arr1
是数组首元素的地址,所以对arr1
进行取值操作后,得到的结果为1
。
arr1+5
表示的是1+5
,即对取值操作后的数值加5。
arr1
是数组首元素的地址,所以arr1+1
对应的是数组第二个元素的地址,取值操作后结果为2
。
因为数组arr1
只有四个元素,arr1+4
对应的地址内容为空,并没有被定义和赋值,所以对应的输出结果为0或乱码(此处为乱码)。
接着我们再展示一段代码:
printf("value1 is %d.\n", *arr1);
printf("value2 is %d.\n", *(&arr1));
你可能会想,上文中提到了arr1
和&arr1
的值是一样的,那对这两个值进行取值取值操作,结果肯定也是一样的吧?输出如下:
看到输出的结果,你可能会疑惑为什么*(&arr1)
的值是一个地址。但其实你仔细观察*(&arr1)
会发现,*(&arr1)
相当于对arr1
进行了取地址操作然后再解引用(取值),所以实际上还是arr1
,而arr1
表示的是数组首元素的地址,所以输出为一个地址值。
二维数组
二维数组地址
首先先定义一个二维数组
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
在C语言中,二维数组是按行排列的,即按行排序存放。先存放 arr[0]
行,再存放 arr[1]
行,接着存放 arr[2]
行。每行有 3 个元素,也是其依次存放的。在定义数组时,可以省略行,但是列不能省略。
和一维数组类似,我们先打印arr
和&arr
的值
printf("address1 is %d\n",arr);
printf("address2 is %d\n",&arr);
输出:
我们可以发现和一维数组一样,arr
和&arr
的值是相同的。接着我们打印arr+1
和&arr+1
的值。
printf("address1 is %d\n",arr+1);
printf("address2 is %d\n",&arr+1);
输出:
从输出的结果我们可以发现,arr+1
和&arr+1
的值是不一样的,这很合理,因为一维数组中的arr1+1
和&arr1+1
的值也是不一样的。
但是仔细观察一下我们会发现,二维数组里arr+1
的值和一维数组里arr1+1
的值在原来基础上增加的幅度并不一样。二维数组的arr+1
地址值比arr
地址值增加了12,而一维数组的arr1+1
地址值比arr1
地址值只增加了4。
造成这个差异的原因是因为,在二维数组里,数组名代表的不再是数组首元素的地址,而是数组首行元素的地址。在这里,可以将二维数组理解为三个一维数组的组合,数组名代表的就是第一行(第一个一维数组)的地址。
而对于&arr+1
,&arr
代表的是整个二维数组,&arr+1
对应的地址跨过了整个arr
数组,所以是在原地址的基础上加36。
这个时候你可能会问,数组首元素的地址要怎么表示呢?
这里直接公布其中一个答案:二维数组用*arr
表示首元素的地址。
代码如下:
printf("address1 is %d\n",arr);
printf("address2 is %d\n",*arr);
printf("value1 is %d\n",*(*arr));
printf("address3 is %d\n",*arr+1);
printf("value2 is %d\n",*(*arr+1));
输出如下:
对*arr
进行加1操作后,得到的*arr+1
对应的值为6422004
,即数组第二个元素的地址。
二维数组的首元素地址还有另外一种表示方法,待会会提到。
对于二维数组,arr[0]
、arr[1]
、arr[2]
分别表示的是第一行元素的首元素地址、第二行元素的首元素地址、第三行元素的首元素地址。&arr[0]
、&arr[1]
、&arr[2]
分别表示的是整个第一行元素的地址(即{1,2,3}的地址)、整个第二行元素的地址、整个第三行元素的地址。
代码如下:
printf("address1 is %d\n", arr[0]);
printf("address2 is %d\n", arr[1]);
printf("address3 is %d\n", arr[2]);
printf("address4 is %d\n", &arr[0]);
printf("address5 is %d\n", &arr[1]);
printf("address6 is %d\n", &arr[2]);
printf("after +1 :\n");
printf("address1 is %d\n", arr[0]+1);
printf("address2 is %d\n", arr[1]+1);
printf("address3 is %d\n", arr[2]+1);
printf("address4 is %d\n", &arr[0]+1);
printf("address5 is %d\n", &arr[1]+1);
printf("address6 is %d\n", &arr[2]+1);
输出:
二维数组的值
代码如下:
printf("value1 is %d\n",*(*arr));
printf("value1 is %d\n",*arr[0]);
printf("value1 is %d\n",*arr[1]);
printf("value1 is %d\n",*arr[2]);
printf("value1 is %d\n",arr[0][0]);
printf("value1 is %d\n",(*arr)[0]);
输出:
*arr[0]
、*arr[1]
、*arr[2]
表示的是第一行、第二行、第三行数组首元素的值。arr[0][0]
就是最基本和直观的二维数组对应值的表示方法。由上面的内容可知*arr
表示的是二维数组首元素的地址,可以和一维数组类比一下,“首元素的地址(数组名)+索引”,对应的就是数组的值,在这里*(arr)[0]
表示的就是该二维数组的第一个元素,同理*(arr)[8]
对应的就是该二维数组的第九个元素。
总结
C语言的数组还是很有趣的,之后有时间就来写写指针相关的内容。