前言
很多初学 C 语言的同学学到数组和指针时都会一头雾水,明明写法不同,arr[2]和*(arr+2)结果却完全一致。数组和指针看似高度相似,实则有着本质区别,而这块知识点,正是后续顺序表、链表底层访问的基础。
一、arr [2] 和 *(arr+2) 为什么等价?
我们先定义一个整型数组:
int arr[] = {1, 3, 5, 7, 9};数组名arr在绝大多数场景下,代表数组首元素arr[0]的地址。 编译器底层有一条固定规则:下标访问语法 arr [i] 永远会被翻译为 *(arr + i)。
arr为首元素地址;arr + 2:地址向后偏移 2 个 int 长度,指向第三个元素;*(arr + 2):对偏移后的地址解引用,取出里面的值;
printf("%d", arr[2]); //输出5 printf("%d", *(arr+2)); //输出5二、数组名的两个特殊例外
数组名不是普通指针,存在两条特殊规则:
sizeof(arr):此时数组名不会退化为指针,计算整个数组全部字节大小;&arr:取出整个数组的地址,地址数值和 arr 相同,但类型为数组指针。
除此之外,函数参数赋值、算术运算时,数组名都会退化为首元素地址。
三、指针变量与数组名的核心区别
- 能否修改指向数组名是地址常量,不能执行
arr++; 指针变量是变量,可以自由p++、修改指向。 - 内存占用不同数组占用一整块连续内存,存储所有有效数据; 指针变量仅占用 4 字节(32 位)/8 字节(64 位),只保存一个地址。
- 函数传参行为不同函数形参写作
int arr[],本质等价于int* arr,数组彻底退化成指针,函数内部无法用 sizeof 获取数组真实长度。
四、对数据结构的铺垫
顺序表本质就是动态数组,依靠首地址 + 偏移量随机访问任意位置元素; 链表遍历依靠指针依次向后移动。熟练掌握地址偏移和解引用,看懂数据结构底层代码将毫无障碍。
小结数组底层永远是地址偏移 + 解引用;数组是连续内存实体,指针只是存放地址的容器。