指针是 C++ 历史上最令人生畏的话题之一,也是许多初学者卡壳的地方。但很快你就会发现,指针其实并不可怕——它们的行为与左值引用非常相似。在深入之前,我们先做些铺垫。
(相关阅读:如果你对左值引用尚不熟悉,请先复习 《左值引用》、《指向常量的左值引用》和《通过左值引用传参》。)
变量、地址与引用
考虑一个普通变量:
char x {};   // 占 1 字节内存
编译器会为 x 分配一段内存,例如地址 140。此后凡用到 x,程序都到地址 140 取值;我们无需关心具体地址或字节数,编译器会负责转换。
同样地:
int main() {
    char x {};
    char& ref { x };   // ref 是 x 的别名
}
使用 ref 时,程序仍到地址 140 取值,地址转换由编译器隐式完成。
取址运算符 &
虽然默认不暴露内存地址,但我们可以用取址运算符 & 获取:
#include <iostream>
int main() {
    int x{ 5 };
    std::cout << x << '\n';   // 打印值 5
    std::cout << &x << '\n';  // 打印地址,如 0027FEA0
}
- 对多字节对象,&返回其首字节地址。
- &的三重含义:- 类型后:int&表示引用;
- 一元表达式中:&obj表示取地址;
- 二元表达式中:a & b表示按位与。
 
- 类型后:
解引用运算符 *
拿到地址后,可用解引用运算符 * 访问该地址的值(作为左值):
std::cout << *(&x) << '\n'; // 再次打印 5
- *与- &互为逆运算:- &取地址 →- *解地址得值。
- *是一元运算符,与乘法区分。
指针(pointer)
指针是一种对象,专门用来保存内存地址(通常是另一变量的地址)。现代 C++ 中,此类指针常被称为“裸指针”或“原始指针”,以区别于“智能指针”(第 22 章)。
声明语法
指针类型以 * 标记:
int;   // 普通 int
int&;  // 左值引用
int*;  // 指向 int 的指针
定义指针变量:
int main() {
    int x{ 5 };
    int* ptr;          // 未初始化(野指针)
    int* ptr2{};       // 空指针(下节详述)
    int* ptr3{ &x };   // 指向 x
}
最佳实践
- 声明时把 *写在类型侧:int* ptr;
- 一行多变量须对每个变量写 *:int* p1, *p2;
- 始终初始化指针,避免野指针。
使用
一旦指针保存了地址,即可用 * 解引用:
int main() {
    int x{ 5 };
    int* ptr{ &x };
    std::cout << *ptr << '\n'; // 5
}
概念图:ptr 保存 x 的地址,因此说“ptr 指向 x”。
指针与类型
指针必须与指向对象类型匹配,否则非法(下一课讨论例外)。
指针与赋值
指针可用赋值改变指向或改变所指对象的值:
int main() {
    int x{5}, y{6};
    int* ptr{&x};
    std::cout << *ptr << '\n'; // 5
    ptr = &y;                   // 改指向
    std::cout << *ptr << '\n'; // 6
    *ptr = 9;                   // 改 y 的值
    std::cout << y << '\n';    // 9
}
指针 vs 左值引用
- 相似:都提供间接访问。
- 差异:- 指针可重新指向、可为空、可算术运算;引用不可。
- 指针须显式取址与解引用;引用隐式完成。
- 指针是对象,引用不是。
- 指针“危险”需手动管理;引用“安全”(除非悬垂)。
 
取址运算符的返回值
&x 返回 int*(指向 int 的指针),而非地址字面量。
指针大小
- 32 位程序:指针 4 字节。
- 64 位程序:指针 8 字节。
 与所指对象大小无关。
悬垂指针(dangling pointer)
若对象已销毁,指针仍保存其地址,则成悬垂指针。解引用悬垂指针导致未定义行为。
结论
指针保存地址,可用 * 解引用。解引用野/悬垂/空指针均致未定义行为。
指针比引用更灵活也更危险,后续课程将继续深入。
测验
问题 1
假设 short 占 2 字节,32 位机器,预测输出:
#include <iostream>
int main() {
    short value{7}, otherValue{3};
    short* ptr{ &value };
    std::cout << &value << '\n';   // 地址
    std::cout << value << '\n';    // 7
    std::cout << ptr << '\n';      // 同上地址
    std::cout << *ptr << '\n';     // 7
    *ptr = 9;
    std::cout << value << '\n';    // 9
    ptr = &otherValue;
    std::cout << *ptr << '\n';     // 3
    std::cout << sizeof(ptr) << '\n'; // 4
    std::cout << sizeof(*ptr) << '\n';// 2
}
问题 2
指出下面代码的问题:
int v1{45};
int* ptr{ &v1 };
int v2{78};
*ptr = &v2; // 错误:*ptr 是 int,&v2 是 int*,类型不匹配
应改为:ptr = &v2;
