不可行——sigsegv不能被安全地“捕获并继续执行”,因栈和寄存器状态可能已损坏,从信号处理函数返回属未定义行为;唯一安全做法是记录现场后调用 _exit() 终止,或借助 asan、gdb 等工具预防与定位。

Linux下用signal()捕获SIGSEGV可行吗?
不可行——SIGSEGV不能被安全地“捕获并继续执行”。你调用signal()或sigaction()注册处理函数后,一旦发生分段错误,程序进入信号处理函数,但此时栈、寄存器甚至堆状态都可能已损坏。从该处理函数返回(哪怕只写一句return)属于未定义行为,大概率触发二次崩溃或静默数据破坏。
真正能做的只有两件事:记录现场后终止,或尝试在可控上下文中提前规避。
- 用
sigaction()设置SIGSEGV处理函数时,必须设SA_RESETHAND(避免重复触发)和SA_NODEFER(确保信号不被屏蔽) - 处理函数内只能调用异步信号安全函数(如
write()、_exit()),禁止用printf()、malloc()、std::cout等 - 别试图在处理函数里修复指针或跳转回原代码——这不是异常处理,是灾难响应
用gdb快速定位SIGSEGV源头
绝大多数分段错误根本不需要“捕获”,而是该立刻定位。直接运行gdb ./your_program,然后run,崩溃时bt(backtrace)就能看到非法内存访问发生在哪一行、哪个函数调用栈。
常见触发点包括:nullptr解引用、vector越界访问(operator[]不检查)、std::string移动后再次使用、释放后仍访问堆内存(use-after-free)、栈溢出(递归过深或大数组局部变量)。
- 加
-g编译选项,否则gdb无法显示源码行号 -
set follow-fork-mode child用于调试fork()后的子进程崩溃 - 用
watch *0xdeadbeef监视特定地址(需知道可疑地址)
AddressSanitizer比信号捕获更实用
开发阶段首选AddressSanitizer(ASan),它不是捕获信号,而是在编译期插桩,运行时实时检测非法内存操作,并给出精确到行的报告,包括读/写类型、内存块分配/释放位置。
启用只需两步:g++ -fsanitize=address -g your_code.cpp,然后直接运行。它会拦截malloc/free、栈分配、全局变量访问,对use-after-free、heap-buffer-overflow、stack-use-after-return等场景覆盖远超信号处理。
- ASan会显著降低运行速度(2–3倍)且增加内存占用,仅用于开发和测试
- 不兼容
libasan与某些低级内存操作(如自定义allocator未适配ASan API) - Windows下需用MSVC的
/fsanitize=address(Clang for Windows支持,MSVC原生不支持)
std::optional和智能指针能减少空指针崩溃吗?
能,但只解决一部分问题。用std::unique_ptr替代裸指针可避免忘记delete,用std::optional<t></t>明确表达“可能无值”语义,配合has_value()检查,确实能堵住很多nullptr解引用路径。
但它们对数组越界、迭代器失效、多线程竞态访问、栈溢出完全无效。例如:std::vector<int> v(10); v[100] = 42;</int>依旧崩,std::optional也无法防止v.data()拿到指针后被v析构。
-
at()替代operator[]可抛std::out_of_range异常,但性能开销明显 -
std::span(C++20)提供带长度检查的视图,但不自动做边界校验,仍需手动调用size() - 静态分析工具(如clang++
-Wall -Wextra -Wuninitialized)能在编译期发现部分潜在空指针路径
真正关键的不是“怎么捕获”,而是理解分段错误本质:它是操作系统内核对非法内存访问的强制干预,不是C++异常,也不该被当成可恢复错误。把精力放在预防(ASan + 静态检查 + RAII)和快速定位(gdb)上,远比写一个看似“捕获成功”的信号处理函数靠谱得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











