🔷 第七章 数组、指针和引用
📄 一句话概括

数组名其实就是一个地址——数组第一个元素的地址,也就是"一排柜子的开头"。所以 a[i] 可以写成 *(a + i),两种写法一个字都不差。数组和指针是天生一对:数组保证"连续存放"(7.1),指针保证"加减按元素跳"(7.4.3),两条合起来,就是"用指针走遍一整排柜子"。

📝 本节和前面几节的关系

这一节是本章前半段的会师点:7.1 的数组、7.4 的指针,在这里拼成一块。你会发现前面埋的伏笔一条条回收了——7.1.1 说"数组名代表首地址"、7.1.3 说"数组不能整体赋值"、7.4.3 说"指针加 1 跳一格",全都指向本节的结论。学完它,7.6 的函数传地址和第 8 章的引用就都有了着落。

一、一句话:数组名 = 首地址

int a[5] = {10, 20, 30, 40, 50};

cout << a    << endl;      // 0x61ff00  ← 一串地址
cout << &a[0] << endl;     // 0x61ff00  ← 一模一样

a 和 &a[0] 打印结果完全相同——这就是"数组名代表首元素地址"的字面证据。

所以下面两种写法是同一个意思:

int *p = a;         // ✅ 常见写法
int *p = &a[0];     // ✅ 完全等价,更"字面"

但数组名不是普通的指针变量,有两处关键区别:

对比 数组名 a 指针变量 p
能不能重新指向别处(a = ...) ❌ 不能(它不是变量) ✅ 能
能不能 a++ ❌ 不能 ✅ 能
sizeof(a) 整排的字节数(如 20) 指针自己的大小(如 4 或 8)

最后一行是本节的重量级坑(也在 3.1.7 埋过):sizeof(a) 是 20,sizeof(p) 只有 4——同一个数组,用数组名量是"全排",用指针量是"一张纸条"。

二、四种写法,一个意思

对一维数组,下面四种取第 i 个元素的方法完全等价:

a[i]        // ① 下标法(最常用)
*(a + i)    // ② 指针法:a 是首地址,+i 走到第 i 格,再解引用
p[i]        // ③ 把指针当数组用(下标法)
*(p + i)    // ④ 指针法的指针版

为什么 a[i] 等于 *(a+i)?把 7.4.3 的规律套进来就明白了:

a 是首地址 → a + i 是"往后 i 格"的地址 → 前面加 * 就是"取那一格的值"。

而 a[i] 里的 i 从来就不是"第几个",它是"偏移几格"——这正是 7.1 说的"下标本质是偏移量"。

还能反着写(编译器也认,因为 + 满足交换律):

*(a + i)   ≡   *(i + a)   ≡   i[a]      // 最后这个能编过,但别写,会被同事打

三、本节三篇分工

小节 覆盖什么
7.5.1 指针与一维数组 上面这套等价关系 + 指针遍历
7.5.2 指针与二维数组 "行指针"与"元素指针"的分工
7.5.3 指针与字符数组 靠 '\0' 自动停下的遍历

四、动手 Demo:先看看它们是不是同一个地址

#include <iostream>
using namespace std;

int main() {
    int a[5] = {10, 20, 30, 40, 50};
    int *p = a;

    cout << "a        = " << a       << endl;
    cout << "&a[0]    = " << &a[0]   << endl;
    cout << "p        = " << p       << endl;
    cout << "---- 四种写法 ----" << endl;
    for (int i = 0; i < 5; i++) {
        cout << a[i] << " " << *(a + i) << " "
             << p[i] << " " << *(p + i) << endl;
    }

    cout << "sizeof(a) = " << sizeof(a) << endl;   // 20
    cout << "sizeof(p) = " << sizeof(p) << endl;   // 4(或 8)
    return 0;
}

看到什么算成功:前三个地址完全相同;五行输出里每行的四个数字都一样(10 10 10 10、20 20 20 20…);最后两行是 20 和 4(或 8)。

这一屏把全节的结论一次证完:数组名就是地址;四种写法完全等价;但 sizeof 上,数组名是全排、指针是纸条。

30 秒实验:加一句 a++;(或 a = p;)再编译——编译器报错(类似 lvalue required as increment operand)。数组名是"地址",但不是"可以改的变量",这正是 7.1.3 说"数组不能整体赋值"的深层原因。

五、常见误区

  1. "数组名就是指针"——接近但不等于。数组名代表首地址,但它不是变量:不能 a++、不能被赋值,sizeof 的含义也完全不同。
  2. "sizeof(a) 和 sizeof(p) 应该一样"——差得远。前者是整排容量,后者是指针大小。这个差别在 7.8.3 会变成一个非常实用的坑(数组传进函数后 sizeof 就失效了)。
  3. "a[i] 里的 i 是第几个元素"——是偏移量。第一个元素是 a[0]。
  4. "p = a; 之后 p 和 a 就一体了"——只是当时指向同一处。p 可以被改去别处(p = &b[0];),a 永远在原地。
自测三问(点开对答案) 1. `a[2]` 用指针写法怎么写?——`*(a + 2)`。 2. `int a[5];` 里 `sizeof(a)` 和 `sizeof(&a[0])` 分别是多少(int 4 字节)?——`20` 和 `4`。 3. 为什么数组名不能 `a++`?——因为数组名代表首地址但**不是变量**,C++ 不允许给它赋值或自增。

相关笔记