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

指针变量的声明格式是 类型 *指针名;,赋值时给它一个地址:int *p = &a;。读作"p 是一个指针,它指向一个 int"。类型必须和目标一致——int * 只能指向 int,不能指向 double。另外,指针自己也有地址(可以用 &p 取到),别和它装着的地址搞混。

📝 本节和前面几节的关系

7.4 讲了"指针是记门牌号的小纸条"。这一节学"小纸条怎么写、怎么填"。声明指针的零件全是旧朋友:类型(第二章)、* 号(第三章的乘法符号,在这里换了身份)、标识符规则(2.3.1)。下一节 7.4.2 才讲"照着门牌号找上门"的动作(* 解引用)——那是本节最重要的一步。

一、声明一个指针变量

int  a = 10;
int *p = &a;          // 声明 p 是指针,并让它指向 a

逐字拆开读:

片段 读作
int p 指向的那户人家,里面装的是 int
* "p 是个指针"(这个星号属于声明语法,不是乘法)
p 指针变量自己的名字
= &a 初始化:把 a 的地址填进 p

一句话翻译:int *p = &a; = "p 是一张纸条,上面抄着 a 家的门牌号"。

星号写哪儿都行,但有个著名的坑

int *p;      // ✅ 常见写法
int* p;      // ✅ 也合法(有人觉得这样更能体现"p 的类型是 int*")
int * p;     // ✅ 也合法
int *p, q;   // ⚠️ 陷阱!p 是 int*,但 q 是普普通通的 int
int *p, *q;  // ✅ 两个都是指针

最后一个坑必须记住:星号只跟紧它后面的那一个名字。写 int *p, q; 时,q 得到一个 int,不是指针。一行声明多个指针,每个前面都要有 *。

二、指针里有地址,指针自己也有地址

int  a = 10;
int *p = &a;

// p 里装着 a 的地址
cout << p  << endl;      // 0x61ff0c  ← a 的地址

// p 自己也是一块内存,也有地址
cout << &p << endl;      // 0x61ff08  ← p 自己的地址

图示(两个门牌号、一张纸条):

      p(一张纸条)              a(一个盒子)
   ┌───────────────┐         ┌──────────┐
   │  0x61ff0c     │ ──────► │    10    │
   └───────────────┘         └──────────┘
    0x61ff08                  0x61ff0c
    (纸条自己在哪)           (纸条上写的地址)

"指向"的准确含义:p 里的那个数,恰好就是 a 的地址。所以顺着 p 找过去,就能找到 a。

别混淆三个东西:

写法 是什么
a a 的值(10)
&a a 的地址(和 p 里的内容相同)
p 一个变量,它的值是 a 的地址
&p p 这个变量自己的地址

三、实例05:输出变量的地址值

#include <iostream>
using namespace std;

int main() {
    int  a = 10;
    int *p = &a;                  // p 指向 a

    cout << "a 的值      = " << a  << endl;
    cout << "a 的地址    = " << &a << endl;
    cout << "p 里存的    = " << p  << endl;   // 与 &a 相同
    cout << "p 自己的地址 = " << &p << endl;

    // 判断 p 是否真的指向 a
    if (p == &a) cout << "p 确实指向 a" << endl;
    return 0;
}

看到什么算成功:

a 的值      = 10
a 的地址    = 0x61ff0c
p 里存的    = 0x61ff0c      ← 和上一行一模一样
p 自己的地址 = 0x61ff08      ← 另一个地址
p 确实指向 a

中间两行是本节的核心证据:p 和 &a 打印出来完全相同——这就是"p 指向 a"的字面含义。而 &p 是另一个地址,说明指针自己也占地儿。

30 秒实验:加一句 int *q = &a;,再打印 q——和 p 打印出的地址完全一样。这叫"多个指针可以指向同一户人家",后面 7.6.1 讲函数传地址时正是靠这个特性。

四、常见误区

  1. "int *p = &a; 里的 * 是乘法"——不是。这是声明语法里的星号,意思是"p 是指针"。不过它确实和 3.1.1 的乘号是同一个字符——看位置和上下文辨认(7.4.2 会把三种身份一次讲清)。
  2. "int *p, q; 声明了两个指针"——只声明了 p 一个指针,q 是普通的 int。星号只跟紧后一个名字。
  3. "指针不初始化也能用"——极其危险。int *p; 里的内容是垃圾值(2.3.2),它可能指向任何地方;这时去用它,就是 7.4.4 要讲的"野指针"。
  4. "指针类型随便写,反正都是地址"——必须匹配。double *p = &a;(a 是 int)会编译报错。因为类型决定了"顺着这个地址该怎么读、读几个字节"。
  5. "cout << p 打印出的是它指向的值"——不是,打印的是地址。想取值要写 *p(下一节)。
自测三问(点开对答案) 1. 怎么声明一个指向 `double` 的指针 `pd`,并让它指向变量 `d`?——`double *pd = &d;` 2. `int *p, q;` 里 `q` 是什么类型?——普通的 `int`(不是指针)。星号只跟紧 `p`。 3. `p` 和 `&p` 有什么区别?——`p` 里装着**别人**的地址;`&p` 是 **p 自己**在内存里的地址。

相关笔记