AddressSanitizer 必须在编译和链接阶段均使用 -fsanitize=address 且禁用优化(-O0),否则不生效;它默认仅检测堆和全局内存越界,不检测栈数组越界;配合 std::vector::at() 反而可能掩盖问题,应改回 operator[] 并确保未定义 _GLIBCXX_DEBUG。

AddressSanitizer 编译时必须加 -fsanitize=address 且禁用优化
不加这个编译选项,ASan 完全不会生效,哪怕你链接了相关运行时库。常见错误是只加了 -lasan 或只在链接阶段加标志——没用。ASan 是编译器插桩机制,必须在编译和链接两个阶段都启用:-fsanitize=address 要出现在所有 g++ 或 clang++ 的编译命令(包括 .cpp 到 .o)和最终链接命令中。同时必须关闭优化(-O0),因为 -O1 及以上会触发变量重排、内联或死代码消除,导致 ASan 插入的检查被绕过或报错位置偏移。
推荐写法:
g++ -O0 -g -fsanitize=address -fno-omit-frame-pointer main.cpp -o main
注意:-fno-omit-frame-pointer 不是可选的——它让堆栈回溯可读,否则报错时只显示 ???。
运行时报 ERROR: AddressSanitizer: heap-buffer-overflow 怎么定位
这类错误说明你访问了堆上分配内存的边界外地址,比如 new int[10] 后访问了 a[10] 或 a[-1]。ASan 会在终端直接打印完整调用栈、越界偏移量、分配现场和释放现场(如果已释放)。
立即学习“C++免费学习笔记(深入)”;
关键信息看三处:
-
READ of size 4 at 0x602000000018 thread T0→ 表明是读操作,大小 4 字节,地址在堆上 -
#0 0x... in foo() example.cpp:12→ 出问题的代码行(最顶上那行) -
allocated by thread T0 here:→ 显示new或malloc发生的位置
若调用栈被截断,确认是否漏了 -fno-omit-frame-pointer;若看到 unknown module,说明对应对象文件没加 -fsanitize=address。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
静态数组和栈变量越界不会被 ASan 检测到
ASan 默认只检测堆内存(malloc/new)和全局变量,对栈上局部数组(如 int a[10]; a[11] = 1;)不报错。这不是 bug,是设计取舍:栈插桩开销太大,且 GCC/Clang 默认禁用栈保护。
要检测栈越界,需额外加编译选项:
g++ -O0 -g -fsanitize=address -fsanitize-address-use-after-scope -fstack-protector-strong main.cpp -o main
但注意:-fsanitize-address-use-after-scope 主要用于检测悬垂引用(如返回局部变量地址),对纯数组越界仍有限;真正可靠的栈数组越界检测得靠 UBSan(用 -fsanitize=undefined)或编译器内置的 -Warray-bounds 警告(仅编译期)。
和 std::vector::at() 混用容易掩盖问题
有人以为开了 ASan 就不用手动检查下标,于是把所有 v[i] 换成 v.at(i)。这反而可能干扰 ASan:因为 at() 抛异常前会先做一次边界检查,而 ASan 的插桩是在底层内存访问层面。一旦 at() 先抛出 std::out_of_range,程序就终止了,ASan 根本没机会触发。
更糟的是,某些 STL 实现(如 libstdc++ 的 debug mode)会主动禁用部分 ASan 插桩。所以真实调试时建议:
- 临时改回
v[i]写法,让越界直接落到内存访问层 - 确保没定义
_GLIBCXX_DEBUG(它会让 vector 自检并提前 abort) - 用
std::span(C++20)配合 ASan 更安全,它不带运行时检查,纯粹依赖底层插桩
ASan 的价值在于暴露那些“看似合法”的越界——比如指针算术偏移、结构体尾部柔性数组访问、跨 new[] 边界的读写。这些地方,人写的边界检查很容易漏,而 ASan 会稳稳抓住。

















