6.数组名-vs-指针.md 4.0 KB

数组名 vs 指针

判别规则(核心,唯一要记的)

  • 数组名出现在 sizeof& 中 → 保持"数组"身份(sizeof 得整块大小,&a 是指向整块的指针)
  • 出现在函数传参、算术运算、赋值中 → 退化为指向首元素的指针(&a[0]
  • 函数形参里写的 int a[] 本质就是 int *a

一句话:"原地是数组,移动是指针;sizeof/&a 留在原地,传参/运算被搬走。"

抽象描述(一句话本质)

数组名在声明它的语境里是整块数组;一旦"移动"(传参、算术、赋值)就退化成指向首元素的指针。C 函数按值传参,传地址是 O(1) 成本、与数组大小无关,这是退化设计的根本原因。

正反例(建立直觉)

  • 正例1(sizeof 差别)int a[10]; sizeof(a) → 40(整块);void f(int arr[]){ sizeof(arr); } → 4/8(形参已退化成指针)
  • 正例2(传参退化)f(a); → a 退化成 &a[0],函数里只能拿指针,不复制数组
  • 正例3(算术退化)a + 1 → 指针运算,指向 a[1]
  • 反例1&a 不退化,类型是 int(*)[10](指向整个数组),和 a+1 跨的步长完全不同
  • 反例2a++ 非法——数组名是常量地址不是指针变量;int *p = a; p++; 才合法

易混对比

数组名(声明语境) 退化成指针(移动语境)
sizeof 整块大小(如 40) 指针大小(4/8)
类型 int[10] int*
自增 a++ 非法(常量地址) 合法(指针变量 p++
a vs &a &a = 指向整块 int(*)[10] a = 指向首元素 int*

数组名 vs 指针关键对比表

数组名 a 指针 p
类型 int[10] int*
sizeof 整块(40) 指针大小(4/8)
a++ / p++ 非法 合法
a = ... 非法(常量地址) 合法(变量)
形参 int a[] 等价于 int *a 就是指针

变体验证(3 题,全过=学会)

  1. int arr[5];sizeof(arr) = 20(整块),sizeof(&arr[0]) = 4/8(int*)
  2. 陷阱题:void f(int a[10]) → 函数里 sizeof(a) = 4/8;形参 [10] 不限制长度,只是给程序员看的注释——传长度 3 的数组也不报错
  3. int a[10]; int *p = a;a++ 不合法(常量地址)、p++ 合法sizeof(a) = 40sizeof(p) = 4/8

口述要点(面试怎么讲)

  • 结论先行:数组名在 sizeof/& 里是一整块,传参/运算/赋值里退化成首元素指针
  • 为什么设计成退化:C 按值传参,传整块要复制,大数组一次调用就卡死;传地址是 O(1) 成本、与数组大小无关
  • 数组名 vs 指针不是一回事:像——都拿地址、a[i]/p[i] 下标都能用、算术都跨元素;不像——类型不同、sizeof 不同、数组名不能自增/被赋值(常量地址)、指针是变量
  • 嵌入式要点:函数形参 uint8_t buf[]uint8_t *buf 完全等价——[] 在形参中直接退化成指针
  • 易错点:传参后还 sizeof 想拿数组长度(拿到的是指针大小);&a 类型是 int(*)[10] 不退化

关系网络

相邻概念 和本主题的关系 孤立理解会犯的错
sizeof vs strlen sizeof 编译期算整块,strlen 运行期数到 \0;数组名退化成指针后 sizeof 就错了 传参后还 sizeof 想拿数组长度
数组指针 vs 指针数组 int (*p)[N] 是"指向数组的指针"(配 &a),int *p[N] 是"指针数组" 混用退化和取地址
&a &a 不退化,类型 int(*)[10],跨一整块 以为 &aa 类型一样
结构体 结构体里的数组成员 sizeof 算整块,拷贝结构体 = 拷贝整块数组 以为成员数组也会"退化"

学习日期

2026-08-03


每主题一页,复习时只翻本目录。