## 数组名 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` 跨的步长完全不同 - **反例2**:`a++` 非法——数组名是常量地址不是指针变量;`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)` = **40**、`sizeof(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]`,跨一整块 | 以为 `&a` 和 `a` 类型一样 | | 结构体 | 结构体里的数组成员 sizeof 算整块,拷贝结构体 = 拷贝整块数组 | 以为成员数组也会"退化" | ## 学习日期 2026-08-03 --- _每主题一页,复习时只翻本目录。_