几乎所有C语言入门教程都会说“数组名就是指向首元素的指针”,但这是对C标准最大的误读。
C标准明确规定:除了3个豁免场景,数组名会在表达式中隐式转换为指向首元素的指针。而这3个不转换的场景,正是你遇到的sizeof结果异常、编译报错、指针运算错乱的核心根源。
铁则1:作为sizeof操作数时,绝不转换
int arr[5] = {
1,2,3,4,5};
printf("%zu", sizeof(arr)); // 输出20(5*4字节),而非64位系统下的8字节指针大小
本质:此处数组名不会退化为指针,sizeof计算的是整个数组对象的总字节数,这也是数组名与指针最核心的本质区别。
高频踩坑:函数传参时,数组形参会强制退化为指针,函数内用sizeof永远只能拿到指针大小,无法获取数组总长度。
铁则2:作为取地址&操作数时,绝不转换
int arr[5];
printf("%p\n", arr); // 首元素地址,类型int*
printf("%p\n", &arr); // 整个数组的地址,类型int(*)[5]
// 地址值相同,但指针运算步长天差地别
printf("%p\n", arr+1); // 地址偏移4字节(1个int)
printf("%p\n", &arr+1); // 地址偏移20字节(整个数组)
本质:此处数组名不会退化为指针,&arr取的是完整数组对象的地址,而非指针的地址,二者的类型等级、运算规则完全不同。
高频踩坑:把&arr强转为int*传递,会导致指针运算越界,破坏相邻内存。
铁则3:字符串字面量初始化字符数组时,绝不转换
char str[] = "hello";
printf("%zu", sizeof(str)); // 输出6(含末尾\0),而非指针大小
本质:此处作为初始化器的字符串字面量(字符数组)不会退化为指针,编译器会把完整的字符串内容拷贝到数组的栈内存中,而非只拷贝一个指针地址。
高频踩坑:混淆char str[] = "hello"与char *str = "hello":前者是可修改的栈上数组,后者是指向只读数据段的指针,修改后者会直接触发段错误。
总结
数组名从来都不是指针,只是C标准给它加了一层“隐式转换为指针”的语法糖,方便数组的遍历与操作。
记住这3个豁免规则,你就能避开90%的数组与指针相关的低级错误,彻底搞懂二者的本质边界。