AddressSanitizer(ASan)是Google开发的运行时内存错误检测工具,通过编译和链接时添加-fsanitize=address启用,可捕获堆/栈/全局缓冲区越界、use-after-free等错误,需配合-g和-fno-omit-frame-pointer以精确定位问题,但不支持静态链接,且仅适用于调试阶段。

运行时内存错误用-fsanitize=address检查
直接加 -fsanitize=address 就能捕获越界读写、堆栈缓冲区溢出、使用已释放内存(use-after-free)、内存泄漏等典型问题。它依赖 ASan(AddressSanitizer)运行时库,在编译和链接阶段都需参与,不是仅靠编译选项就能生效的“开关”。
常见误操作是只在编译时加,忘了链接时也要带同一参数。正确写法是:
gcc -fsanitize=address -g main.c -o main
注意必须加 -g,否则报错位置只能显示到函数级,无法精确定位行号。
- ASan 会显著增加内存开销(2 倍左右)和运行时性能损耗(约 2×),**仅用于调试,不可上线**
- 不支持静态链接:
-static与-fsanitize=address冲突,会报错undefined reference to __asan_init - 对未初始化内存(如栈上局部变量)的检测能力有限,这类问题更适合用
-fsanitize=memory(但 GCC 默认不支持,Clang 才完整支持)
整数溢出和未定义行为用-fsanitize=undefined
-fsanitize=undefined(简称 UBSan)专抓 C/C++ 标准里明确定义为“未定义行为”(UB)的操作,比如有符号整数溢出、除零、空指针解引用、移位超界、类型别名违规(strict aliasing violation)等。
它比 ASan 轻量得多,运行开销小(通常
gcc -fsanitize=undefined -g main.c -o main
- 默认只报告第一处 UB,加
-fno-sanitize-recover=undefined可让程序崩溃(便于定位) - 某些检查项可单独开关,例如关闭浮点异常检查:
-fsanitize=undefined -fno-sanitize=float-divide-by-zero - 注意:
sizeof、_Alignof等运算符不会触发 UBSan,它们本身是良定义的
多线程竞态用-fsanitize=thread
检测数据竞争(data race)必须用 -fsanitize=thread(TSan)。它要求所有参与线程同步的代码(包括 pthread、std::thread、OpenMP)都经 TSan 插桩,因此整个程序——包括所链接的第三方静态库——都得用相同选项编译,否则漏报率极高。
典型命令:
gcc -fsanitize=thread -g -O2 main.c -lpthread -o main
- 必须搭配
-O2或更高优化级,否则 TSan 的运行时开销会暴增(因缺少指令重排抑制) - 不能和 ASan/MSan 同时启用(GCC 不支持 sanitizer 组合),报错
error: cannot specify multiple sanitizers - TSan 报告中的 “Previous write” 和 “Current read” 时间戳是逻辑时钟,不是真实时间,重点看调用栈路径
容易被忽略的链接与环境细节
sanitizer 不是纯编译期机制,它依赖运行时库(如 libasan.so、libubsan.so)。所以即使编译通过,运行时仍可能失败:
- 动态链接失败:提示
libasan.so.8: cannot open shared object file→ 运行前执行export LD_LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:$LD_LIBRARY_PATH(路径依 GCC 版本而变) - 静态二进制无法使用 sanitizer:
-static会跳过动态 sanitizer 库,导致功能失效 - 交叉编译时,目标平台必须安装对应 sanitizer 运行时,宿主机的库不能复用
最麻烦的是混合构建:如果项目里一部分 .o 来自预编译的 SDK(没加 sanitizer),那整个可执行文件就失去检测能力——sanitizer 必须端到端一致,漏一个环节就归零。


















