第 7 课
指针
一句话答案:指针是装着内存地址的变量。&x 给出 x 的地址;*p 给出 p 所存地址上的值。
指针就是这样一个地址
#include <iostream>
using namespace std;
int main() {
int x = 42;
int *p = &x;
cout << "x = " << x << endl;
cout << "*p = " << *p << endl;
return 0;
}
p 装着 x 的地址,所以 *p 透过指针读到的还是 42。地址那个数字本身在本站的浏览器引擎里无法用 cout 打印 —— 真实编译器会按十六进制打出来。
存下来,再透过它读写
#include <iostream>
using namespace std;
int main() {
int x = 42;
int *p = &x; // p 装着 x 的地址
cout << *p << endl; // 42 —— 透过指针读
*p = 99; // 透过指针写
cout << x << endl; // 99 —— x 变了
return 0;
}
机制就这么点:p 指向 x,所以透过 p 写就是写到 x。这正是「一个函数能改另一个函数的变量」的原理。
谁需要它
要改调用方的变量,就把它的地址传进去:
#include <iostream>
using namespace std;
void doubleIt(int *p) {
*p = *p * 2;
}
int main() {
int n = 21;
doubleIt(&n);
cout << n << endl; // 42
return 0;
}
因为参数是地址,函数改的是调用方的 n。按值传 n 只会改一份拷贝。三种传法对比:传值、指针、引用。
数组和指针是表亲
#include <iostream>
using namespace std;
int main() {
int a[3] = {10, 20, 30};
int *p = a; // 数组名会退化成首元素地址
cout << *p << endl; // 10
cout << *(p + 1) << endl; // 20 —— 指针算术按元素移动
return 0;
}
这就是数组「天然按引用传」的原因:传过去的其实是首元素地址。
两条规则
- 绝不解引用未初始化的指针。
int *p; *p = 5;会写到随机地址。先让它指向某个东西,或设成nullptr。 - 绝不使用指向已消失对象的指针。指向已离开作用域的局部变量的指针是悬垂指针 —— 那块内存可能已被复用。
指针就是 C++ 名声的来源。现代 C++ 里多数裸指针已被引用和智能指针取代 —— 该用什么,见现代 C++ 路线图。