未定义行为(UB)是C++标准完全不约束后果的行为,编译器可任意优化或生成错误代码;常见于数组/指针越界、解引用空/野/已释放指针等场景,极易引发安全漏洞。

未定义行为(Undefined Behavior,UB)不是“随机出错”,而是标准完全不管后果——编译器可以优化、忽略、崩溃、静默改值,甚至生成看似正常但实际错得离谱的代码。 它是C++中最危险也最容易被忽视的安全隐患,尤其在底层开发、嵌入式、安全敏感场景中,一个UB可能直接导致内存破坏、提权漏洞或逻辑失控。
访问越界:数组、指针、容器
超出合法索引范围读写,是UB最高发的来源之一:
- int a[3] = {1,2,3}; a[5] = 42; —— 数组下标越界(即使没崩溃,也不保证安全)
- int* p = new int[2]; p[3] = 0; —— new分配后越界写,UB,不触发std::bad_alloc
-
std::vector
v{1,2}; v.at(5); —— at()会抛异常,但v[5]是UB(operator[]不检查) - char buf[10]; strcpy(buf, "hello world"); —— 缓冲区溢出,经典UB,也是栈溢出漏洞根源
解引用空/野/已释放指针
指针失效后继续使用,结果不可预测:
- int* p = nullptr; *p = 1; —— 解引用空指针,UB(不是“一定段错误”,优化后可能被整个删掉)
- int* p = new int(42); delete p; cout —— 使用已释放内存(dangling pointer),UB
- int* p; cout —— 未初始化指针解引用,UB(哪怕只是读,也可能触发陷阱值)
- int x = 10; int* p = &x; } /* x析构 */ cout —— 访问已离开作用域的局部对象,UB
有符号整数溢出与未定义运算
无符号整数溢出是定义良好的(自动回绕),但有符号整数溢出是UB:
立即学习“C++免费学习笔记(深入)”;
- int x = INT_MAX; x++; —— 有符号加法溢出,UB(编译器可假设它永不发生,进而删除相关分支)
- int y = -1; y —— 左移导致符号位变化或溢出,UB(右移负数也是UB)
- int a = 1, b = 0; a / b; —— 除零,UB(浮点除零是特殊值,整数除零是纯UB)
- int x; cout —— 读取未初始化的非静态局部变量,UB(静态变量会零初始化,但自动存储期变量不会)
违反严格别名规则与类型双关
C++禁止用不兼容类型指针访问同一块内存(防止编译器优化失准):
- float f = 3.14f; int* p = (int*)&f; cout —— C风格强制转换绕过类型系统,UB
- union { int i; float f; } u; u.f = 3.14f; cout —— 在C++中(C++17前)读取非最后写入成员是UB;C++17起允许“活跃成员切换”,但仍有严格限制
-
char buf[sizeof(int)]; int* p = reinterpret_cast
(buf); *p = 42; —— 未满足对齐要求或未正确构造对象,UB(需用placement new或std::bit_cast(C++20))
基本上就这些。UB不复杂,但容易忽略;它不总当场报错,却可能在优化后、换平台后、或攻击者精心构造输入时才爆发。用AddressSanitizer(ASan)、UndefinedBehaviorSanitizer(UBSan)、Clang-Tidy和静态分析工具主动捕获,比靠运气更可靠。











