数组名本质与指针运算揭秘
目录
一、数组名的本质
二、特殊情况分析
三、深入理解数组地址
结果分析
四、总结
一、数组名的本质
在上一章节我们使用指针访问数组内容时,有这样的代码:
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
这里我们使用&arr[0]
的方式获取了数组第一个元素的地址。但实际上,数组名本身就是数组首元素的地址。我们可以通过以下测试验证:
#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n", arr);return 0;
}
输出结果显示,数组名和数组首元素的地址打印结果完全相同,这证实了数组名就是数组首元素的地址:
二、特殊情况分析
有同学可能会产生疑问:如果数组名是首元素地址,那么下面的代码该如何解释?
#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};printf("%d\n", sizeof(arr));return 0;
}
输出结果是40(假设int类型占4字节),而不是预期的4或8(指针的大小)。这似乎与前面的结论矛盾。
实际上,数组名作为首元素地址的规则有两个例外情况:
-
sizeof(数组名)
:当数组名单独放在sizeof运算符中时,它表示整个数组,计算的是整个数组的大小(字节数) -
&数组名
:当取地址运算符作用于数组名时,它表示整个数组,取出的是整个数组的地址
除这两种情况外,其他任何地方使用数组名,数组名都表示首元素的地址。
三、深入理解数组地址
有同学可能会进一步测试以下代码:
#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n", arr);printf("&arr = %p\n", &arr);return 0;
}
三个打印结果看起来完全相同,这让人困惑:arr
和&arr
到底有什么区别?
要理解这一点,我们需要观察指针运算的行为:
#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};printf("&arr[0] = %p\n", &arr[0]);printf("&arr[0]+1 = %p\n", &arr[0]+1);printf("arr = %p\n", arr);printf("arr+1 = %p\n", arr+1);printf("&arr = %p\n", &arr);printf("&arr+1 = %p\n", &arr+1);return 0;
}
可能的输出结果:(我们切换到32位环境下观察,64位环境比较难观察)
结果分析
-
&arr[0]
和&arr[0]+1
相差4个字节(一个int的大小) -
arr
和arr+1
也相差4个字节,因为&arr[0]
和arr
都表示首元素的地址,+1
操作跳过一个元素 -
&arr
和&arr+1
相差40个字节(整个数组的大小,10个int × 4字节),因为&arr
表示整个数组的地址,+1
操作跳过整个数组
四、总结
-
指针类型决定了指针的差异!!!所以指针运算的结果也不相同!!!
-
数组名本质:在大多数情况下,数组名表示数组首元素的地址
-
两个例外:
-
sizeof(数组名)
:计算整个数组的大小 -
&数组名
:获取整个数组的地址
-
-
指针运算差异:
-
首元素地址+1:跳过一个元素
-
数组地址+1:跳过整个数组
-
理解这些概念对于正确使用数组和指针至关重要,特别是在处理多维数组和指针运算时。