先问你一个很现实的问题:去面试 C 语言岗位,笔试里出现int a[5];和int *p = a;,接着问你sizeof(a)和sizeof(p)分别是多少,你能立刻答对吗?
很多人的第一反应是“数组名不就是指针吗,那 sizeof 结果应该一样”。结果在sizeof、&a+1、函数传参这几个经典考点上反复丢分。
能理解为什么这么容易混淆。因为 C 语言里数组名和指针确实“长得太像了”:数组名可以赋值给指针,可以参与指针运算,连a[i]都可以等价写成*(a + i)。于是很多人干脆背一个粗暴口诀:数组名就是指针。这个口诀在 90% 的代码场景里都能用,但剩下 10% 恰恰是笔试和面试最喜欢挖坑的地方。
准确的说法是:数组名不是指针,它只是在大多数表达式中会自动转换成指向首元素的指针。这个转换在 C 语言里叫“数组退化”(array decay)。真正决定面试答案的,不是你背了多少结论,而是你搞清楚哪些场景会触发退化,哪些场景不会。
本文会从 C 语言类型系统的角度,把数组名和指针的核心差异拆开讲清楚,覆盖sizeof、取地址、指针加减、二维数组、函数形参这些高频考点,并给出一套可以直接编译运行的示例代码。读完你会形成一种判断方法:看到任何数组名表达式,先确定它的类型,再往下推导结果。
1. 先从一道面试题说起:数组名到底是不是指针
先放一道非常经典的题目。定义如下:
int a[5] = {1, 2, 3, 4, 5}; int *p = a;现在有四个问题:
sizeof(a)和sizeof(p)分别是多少?a == p成立吗?a++和p++都能编译吗?&a和&p的类型分别是什么?
很多人第一眼会觉得:a 和 p 都能表示数组首元素地址,那这四个问题应该差不多。但正确答案是:
sizeof(a)是20(假设 int 占 4 字节),sizeof(p)是8(64 位平台上指针占 8 字节)。a == p在大多数表达式里成立,因为a会退化成int*,和p的值相等。但要注意,这个比较成立的前提是“a 发生了退化”。a++