指针简介

指针是 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
}
  • 对多字节对象,& 返回其首字节地址。
  • & 的三重含义:
    1. 类型后:int& 表示引用
    2. 一元表达式中:&obj 表示取地址
    3. 二元表达式中: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 左值引用

  • 相似:都提供间接访问。
  • 差异:
    1. 指针可重新指向、可为空、可算术运算;引用不可。
    2. 指针须显式取址与解引用;引用隐式完成。
    3. 指针是对象,引用不是。
    4. 指针“危险”需手动管理;引用“安全”(除非悬垂)。

取址运算符的返回值

&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;

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"