0

0

C++调试技巧升级:利用Core Guidelines减少未定义行为【UB检测】

穿越時空

穿越時空

发布时间:2026-01-24 15:35:03

|

699人浏览过

|

来源于php中文网

原创

启用 /std:c++17 和 /permissive- 可激活 MSVC 对未定义行为的早期拦截,强制标准合规并支持 GSL 工具(如 gsl::span、gsl::not_null),使越界访问、空指针解引用等在编译期或 debug 断言阶段暴露,而非运行时崩溃。

c++调试技巧升级:利用core guidelines减少未定义行为【ub检测】

/std:c++17 + /permissive- 激活编译器对UB的早期拦截

MSVC 默认宽松兼容旧代码,很多未定义行为(比如数组越界读、reinterpret_cast 跨类型别名)只发 warning 甚至不报。启用 /permissive- 强制标准合规后,std::vector::operator[] 在 debug 模式下会自动检查索引,而 /std:c++17 是启用 Core Guidelines 支持的前提——否则 gsl::spangsl::not_null 根本无法使用。

  • /permissive- 会让类似 int arr[3]; arr[5] = 0; 这种直接数组访问在编译期就报错(如果配合静态分析),而不是等到运行崩溃
  • 必须搭配 /D "_ITERATOR_DEBUG_LEVEL=2"(Windows Debug)才能让 STL 容器触发越界断言
  • Clang/LLVM 用户对应的是 -std=c++17 -fsanitize=undefined,但注意它不能替代编译器级约束,只是运行时补漏

把裸指针换成 gsl::not_nullgsl::span

Core Guidelines 的核心价值不是“多写几行”,而是把隐含假设变成编译期契约。比如函数参数声明为 gsl::not_null,调用方传入 nullptr 会直接编译失败;用 gsl::span 替代 int* + size_t 组合,能防止长度与指针脱节导致的越界。

  • gsl::span 构造时若传入空指针且 size > 0,debug 版本会断言失败——这比 segfault 提前几十个调试周期
  • 不要对 gsl::spanreinterpret_cast(span.data()) 类操作,它不保证底层内存连续性语义(虽然当前实现是,但规范没承诺)
  • gsl::not_null 不增加运行时开销,但禁止你写 if (p == nullptr) 这类防御逻辑——这不是省事,是删掉错误前提

clang++ -fsanitize=address,undefined 捕获运行时 UB

编译器静态检查再严,也覆盖不了所有路径分支。ASan(AddressSanitizer)和 UBSan(UndefinedBehaviorSanitizer)是 Windows / Linux / macOS 通用的运行时探测手段,尤其擅长发现:释放后使用、缓冲区溢出、有符号整数溢出、未初始化变量读取。

绘蛙
绘蛙

电商场景的AI创作平台,无需高薪聘请商拍和文案团队,使用绘蛙即可低成本、批量创作优质的商拍图、种草文案

下载
clang++ -std=c++17 -fsanitize=address,undefined -g main.cpp -o main
./main
==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x00010d9a8c3e bp 0x7ffee3b4a9a0 sp 0x7ffee3b4a998
READ of size 4 at 0x602000000010 thread T0
  • UBSan 对 int x = INT_MAX; ++x; 这类操作会打印明确提示,而不是静默翻转
  • ASan 会显著拖慢执行速度(2~3倍),但它是目前最可靠的堆/栈内存 UB 探测器
  • 注意:Windows 上需用 Clang for Windows 或 MSVC 17.5+ 才支持完整 UBSan;MinGW 不支持 ASan

禁用 std::shared_ptr::reset() 隐式转换构造,堵住循环引用漏洞

Core Guidelines 规则 R.32 明确反对用 std::shared_ptr 管理对象图中的双向引用,因为 reset() 允许临时绕过所有权契约。一个典型陷阱是:parent->children.push_back(std::shared_ptr(child));——这里 child 是裸指针,构造 shared_ptr 时会新建控制块,导致 parent 和 child 各自持有一份独立引用计数。

立即学习C++免费学习笔记(深入)”;

  • 改用 std::enable_shared_from_this,并统一通过 shared_from_this() 获取子节点的 shared_ptr
  • 在 CMake 中加 add_compile_options(-Wno-unused-variable) 之前先确认:这个 warning 很可能暴露了你忘了用 shared_from_this() 的地方
  • 更彻底的做法是删掉所有裸 new Node,改用 std::make_shared() ——它能避免控制块与对象内存分离带来的额外分配开销,也天然杜绝了裸指针误传
C++ 的未定义行为不会报错,只会让程序在某个看似无关的函数里突然跳转到错误地址。Core Guidelines 不是风格指南,它是把“靠人记住规则”变成“靠编译器拦住错误”的转换器。最容易被忽略的一点是:这些工具必须同时启用才有效——只开 ASan 不设 /permissive-,或只用 gsl::span 却放任裸指针在其他模块流通,UB 依然会在缝隙里存活。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

768

2023.08.22

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

381

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

542

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

53

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

197

2025.08.29

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

394

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

574

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

394

2023.07.18

c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

25

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.7万人学习

Git 教程
Git 教程

共21课时 | 2.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号