数组的有趣现象
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 5 int main() 6 { 7 int a[5]; 8 9 printf("sizeof(a) = %d\n", sizeof(a)); 10 printf("sizeof(a[0]) = %d\n", sizeof(a[0])); 11 printf("sizeof(&a) = %d\n", sizeof(&a)); 12 printf("sizeof(&a[0])= %d\n", sizeof(&a[0])); 13 return 0; 14 } 15 /** 运行结果 16 sizeof(a) = 20 17 sizeof(a[0]) = 4 18 sizeof(&a) = 4 19 sizeof(&a[0])= 4 20 */
分析:
- 第9行: a是数组的名称,sizeof(a)是求数组长度(数组占用内存几个字节):5 * sizeof(int) = 20;所以计算结果为20
- 第10行:a[0]是数组的第一个元素,sizeof(a[0])是对元素大小进行计算(元素占用内存几个字节),win32下int数据类型点4个字节,所以计算结果为4
- 第11行:&a是数组a的首地址,但是sizeof(&a)是对地址大小进行计算,在32位系统下,地址和指针一样都是占4个字节,所以计算结果为4
- 第12行:&a[0]是数组a的第一个元素a[0]的地址,sizeof(&a[0])是对a[0]地址大小进行计算,在32位系统下,地址和指针一样都是占4个字节,所以计算结果为4
- &a[0]与&a的区别:虽然sizeof(&a)与sizeof(&a[0])计算结果一样,但意义不同,&a[0]是数组元素a[0]的地址,&a是数组a的首地址.
printf("sizeof(a[10]) = %d\n", sizeof(a[10])); //sizeof(a[10]) = 4
注意,上面这个printf打印语句可以正常运行,加为关键字sizeof(它是一个关键字,而不是个函数)求值是在编译的时候,虽然并不存在a[10]这个元素,但是这是也没有去真正访问a[10]这个元素,而是仅仅根据数组元素的类型来确定其值。所以编译器并没有报错。
2. 数组名a作为左值和右值的区别
首先,数组名a是不能作为左值的(a[i]可以作为在左值)。
a作为右值时,它并不是代表数组的首地址,而是与&a[0]意义一样,都是表示数组首元素的地址,因此对a+1等价于&a[1];
1 int main() 2 { 3 int a[5]; 4 5 *(a+1) = 5; 6 printf("a[1] = %d\n", a[1]); 7 return 0; 8 }
3. a 与 &a 的区别
1 int main() 2 { 3 int i, j; 4 int a[5] = {1,2,3,4,5}; 5 6 i = *(a+1); 7 int *ptr = (int *)(&a+1); 8 j = *(ptr-1); 9 printf("*(a+1) = %d,\t*(ptr-1) = %d\n", i, j); 10 11 return 0; 12 }
知识点:对指针进行加1的操作,得到的是下一个元素的地址,而不是在原有的地址值直接加1。所以一个类型为T的指针的移动,以sizeof(T)为移动单位.
分析:
- 第6行:前面说过数组名a作为右值时,代表的是数组首元素的地址(等价于&a[0]),所以a+1为&a[1],*(a+1) = a[1],所以输出的是2
- 第7行:&a的值是数组的首地址(0x0016fd54),所以&a+1是下一个数组的地址(0x0016fd68, 计算方法:0x0016fd54+5*4(5*4化作十六进制0x00000014) = 0x0016fd68),ptr指针指向的是&a[4](0x0016fd64)地址后面的那个地址(0x0016fd68),(ptr - 1)指向的是a[4](0x0016fd64),所以输出的是5.
- 各地址如下(vc2010的一次运行情况):
- + &a+1 0x0016fd68 int [5]*
+ ptr 0x0016fd68 int *
+ ptr-1 0x0016fd64 int *
*(ptr-1) 5 int
*(a+1) 2 int
+ a 0x0016fd54 int [5]
本文大部分摘自《C语言深度剖析》
浙公网安备 33010602011771号