共计 1521 个字符,预计需要花费 4 分钟才能阅读完成。
从段错误说起:那些年我们踩过的坑
刚学 C ++ 时,最让人崩溃的莫过于程序突然报 Segmentation fault 然后退出。这种函数调用访问异常往往由以下原因导致:

- 空指针解引用(经典
nullptr->method()) - 访问已释放的内存(悬垂指针)
- 栈溢出(比如无限递归)
- 数组越界访问
举个真实案例:某次我用 GDB 分析 core dump 文件时发现崩溃发生在 vector 的operator[]调用处。通过 bt 命令查看调用栈,发现是迭代器失效导致的越界访问。
# GDB 调试示例
gdb ./a.out core.12345
(gdb) bt
#0 0x00007f... in std::vector<int>::operator[] ()
#1 0x0804871 in process_data ()
防御性编程实战手册
方案 1:传统异常处理
try {risky_operation();
} catch (const std::exception& e) {std::cerr << "Caught:" << e.what() << std::endl;
}
但 try-catch 有性能开销(约 2 - 3 倍函数调用成本),且无法捕获内存访问错误。
方案 2:RAII+ 智能指针
更推荐用资源获取即初始化 (RAII) 模式:
void safe_function() {auto ptr = std::make_unique<Resource>(); // 自动管理内存
if (!validate_params()) { // 前置检查
throw std::invalid_argument("bad params");
}
// ... 业务逻辑
} // 自动调用~unique_ptr()
方案 3:静态分析助攻
Clang 静态分析器能提前发现问题:
clang --analyze -Xanalyzer -analyzer-output=text main.cpp
会输出类似这样的诊断信息:
warning: Dereference of null pointer
ptr->method();
^~~
完整示例:异常安全的文件处理
#include <memory>
#include <fstream>
void process_file(const std::string& path) {
// 1. 参数校验
if (path.empty()) {throw std::invalid_argument("empty path");
}
// 2. RAII 管理资源
std::ifstream file(path);
if (!file) {throw std::runtime_error("open failed");
}
// 3. 安全读取
std::string line;
while (std::getline(file, line)) {// 处理每行数据}
// 4. 自动调用 file.close()}
用 Valgrind 检测内存问题:
valgrind --leak-check=full ./a.out
==12345== ERROR SUMMARY: 0 errors
生产环境最佳实践
- 性能权衡:在实时系统中,可用错误码替代异常
- 多线程注意:异常不能跨线程传播,需要在线程函数内捕获
- 编译选项 :
-fsanitize=address能捕获更多运行时错误
g++ -fsanitize=address -g main.cpp
调用栈变化图示
+----------------+
| main() |
| ↓ |
| foo() |
| ↓ |
| bar() → 触发异常
+----------------+
思考题
- 如何设计异常安全的回调接口?
- 在多继承体系中,异常处理的顺序如何确定?
- 什么时候应该自定义异常类型?
通过规范代码习惯 + 工具链组合拳,能有效减少 90% 的访问异常问题。剩下的 10% 就需要靠调试技巧和经验积累了。
正文完
