指针是 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;