课程笔记 / 指针与内存管理
指针与内存管理
地址关系与生命周期
从变量、地址和指针开始,配合短小的 C++ 示例说明对象的生命周期和所有权。
变量和地址
变量名对应一块存储空间,存储空间有地址。取地址运算符 & 可以得到变量地址,解引用运算符 * 可以读取或修改这个地址中的值。
int value = 42;
int* pointer = &value;
std::cout << value << '\n';
std::cout << *pointer << '\n';
*pointer = 43;
// value == 43pointer 保存地址,*pointer 才是地址对应的对象。指针类型决定了读取多少字节,以及指针运算如何移动。
指针和引用
函数参数的写法需要表达调用条件。引用绑定到一个已经存在的对象,调用时不需要额外判断。指针可以是空值,适合表示对象可能不存在的情况。
void swap_values(int& left, int& right) {
const int old = left;
left = right;
right = old;
}
int first = 3;
int second = 5;
swap_values(first, second);如果函数接收指针,需要在解引用前检查 nullptr。接口中是否允许空指针,应当在函数名、参数类型或文档中保持一致。
动态对象的所有权
使用 new 创建对象后,程序必须在合适的位置调用 delete。手动管理容易出现忘记释放、重复释放和异常路径泄漏。现代 C++ 优先使用智能指针表达所有权。
#include <memory>
auto lesson = std::make_unique<Lesson>();
lesson->set_title("Pointers and memory");
// lesson 离开作用域后自动销毁对象std::unique_ptr 表示单一所有者,不能复制,只能移动。确实需要多个所有者时再考虑 std::shared_ptr,并确认共享关系不会形成循环引用。
悬空指针
对象销毁后,原来的地址仍然可能保存在指针中,这个指针称为悬空指针。继续使用它属于未定义行为。
int* dangling = nullptr;
{
int local = 10;
dangling = &local;
}
// local 已经销毁,不能再使用 dangling返回局部变量地址、保存临时对象引用、删除对象后继续使用原指针,都会产生同类问题。将对象的生命周期限制在使用范围内,并让所有权类型负责销毁,可以减少这类错误。
练习
写一个函数,接收两个整数的引用并交换它们。再写一个版本,接收两个指针并处理空指针。分别传入有效地址和 nullptr,记录输出结果。
完成后使用 GDB 设置断点,观察两个参数的地址和值。再把动态创建的对象改写为 std::unique_ptr,比较程序退出前后的析构调用。