指针与数组的恩怨:数组名真的是指针吗?&arr 和 arr 的区别

说实话,这个问题我面试过不少人,十有八九都会栽在这里。

很多人张口就来:「数组名就是指针啊,不就是指向第一个元素的地址吗?」

嗯,这话说对了一半,但另一半——恰恰是面试官想听的——全错了。

我自己刚入行那会儿也犯过这个错。有一次调试一个通信协议栈,数组传参传得飞起,结果某个模块死活拿不到正确的数组长度。查了两天,最后发现是 sizeof(arr) 在函数内部返回了 4,而不是我期望的 1024。那一刻我才真正意识到:数组名和指针,根本不是一回事。

先看一个最经典的例子

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    
    printf("arr  = %p\n", arr);      // 输出:0x7ffc12345670
    printf("&arr = %p\n", &arr);     // 输出:0x7ffc12345670
    
    printf("arr + 1  = %p\n", arr + 1);   // 输出:0x7ffc12345674
    printf("&arr + 1 = %p\n", &arr + 1);  // 输出:0x7ffc12345684
    
    return 0;
}

看到了吗?arr&arr 打印出来的地址值一模一样。但加 1 之后,结果完全不同。

为什么会这样?

说白了,数组名在大多数表达式中会「退化」成指向首元素的指针,但 &arr 取的是整个数组的地址,类型是 int (*)[5],也就是「指向包含 5 个 int 的数组的指针」。

所以:

  • arr + 1 跳过一个 int(4 字节)
  • &arr + 1 跳过一个完整的数组(5 × 4 = 20 字节)

这个区别,在指针运算和内存操作时特别要命。

数组名什么时候「不是」指针?

我总结了一下,数组名真正「像指针」的场景其实很有限。下面这几种情况,数组名就是数组名,别把它当指针用:

  1. sizeof 运算符sizeof(arr) 返回整个数组的字节数,不是指针的大小
  2. & 取地址符&arr 得到的是整个数组的地址,类型是数组指针
  3. 数组初始化char str[] = "hello"; 这里的 str 是数组,不是指针

除此之外,在函数传参时,数组名确实会退化为指针。这也是为什么你在函数里用 sizeof 拿不到数组长度的原因。

⚠️ 注意: 函数参数列表中的 int arr[]int *arr 是完全等价的。编译器会把前者当作后者处理。所以别指望在函数内部用 sizeof 获取数组长度——你拿到的永远是 4 或 8(指针大小)。

&arr 和 arr 的类型差异

这个知识点,我建议你死记硬背也要背下来。因为一旦搞混,指针偏移就会全盘出错。

表达式 类型 含义 +1 偏移量
arr int* 指向首元素的指针 4 字节
&arr[0] int* 指向首元素的指针 4 字节
&arr int (*)[5] 指向整个数组的指针 20 字节

你看,arr&arr[0] 是等价的,但 &arr 完全是另一回事。

用 SVG 理清关系

下面这张图,把数组名、指针、地址的关系画清楚了。我建议你多看几遍,把这张图刻在脑子里。

数组名 vs &arr 内存布局示意图 arr[0] arr[1] arr[2] arr[3] arr[4] 0x1000 0x1004 0x1008 0x100C 0x1010 arr 指向 arr[0] 类型: int* &arr 指向整个数组 类型: int (*)[5] arr+1: 偏移4字节 &arr+1: 偏移20字节

避坑指南:我踩过的那些坑

我曾经在写一个环形缓冲区时,用 &arr 做指针偏移,结果缓冲区索引全乱了。调试了一下午才发现,&arr + 1 跳过了整个数组,而不是一个元素。

从那以后,我给自己定了个规矩:

  • 如果只是想遍历数组,用 arr + i&arr[i]
  • 如果确实需要整个数组的地址(比如传给某个需要二维数组指针的函数),才用 &arr
  • 永远不要在函数内部对数组参数用 sizeof——你拿到的永远是 4 或 8
💡 小技巧: 如果你需要在函数内部知道数组长度,要么把长度作为参数传进去,要么用宏定义 + 全局数组。我个人习惯用第一种,清晰且不容易出错。

总结一下

数组名不是指针,但大多数时候表现得像指针。记住三点:

  1. arr&arr 的值相同,但类型不同,导致指针运算结果不同
  2. sizeof(arr) 返回整个数组大小,sizeof(ptr) 返回指针大小
  3. 函数参数中的数组声明会退化为指针,别指望在里面用 sizeof 拿长度

嗯,这个知识点就讲到这里。下次面试再被问到「数组名是不是指针」,你应该知道怎么答了吧?


公众号:蓝海资料掘金营,微信deep3321