数组名其实就是一个地址——数组第一个元素的地址,也就是"一排柜子的开头"。所以 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 说"数组不能整体赋值"的深层原因。
五、常见误区
- "数组名就是指针"——接近但不等于。数组名代表首地址,但它不是变量:不能
a++、不能被赋值,sizeof的含义也完全不同。 - "
sizeof(a)和sizeof(p)应该一样"——差得远。前者是整排容量,后者是指针大小。这个差别在 7.8.3 会变成一个非常实用的坑(数组传进函数后sizeof就失效了)。 - "
a[i]里的 i 是第几个元素"——是偏移量。第一个元素是a[0]。 - "
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++ 不允许给它赋值或自增。- ☐ 7.5.1 指针与一维数组(下一节)
相关笔记
- 一维数组 —— "下标本质是偏移量"的出处
- 指针运算 —— 指针加 1 跳一格
- sizeof运算符 —— 数组名与指针的 sizeof 差别
- 🔷 C++基础 · 索引 —— 全课路线图