Undefined behavior
未定义行为
如果语言的某些规则被违反,整个程序就会变得毫无意义。
解释
C++标准精确地定义了可观察行为对于不属于以下类之一的每个C++程序:
畸形
-程序有语法错误或可诊断的语义错误。一个符合标准的C++编译器需要发出诊断,即使它定义了一个语言扩展,该扩展为这样的代码%28指定了意义,比如使用可变长度数组%29。标准文本使用应
,,,不得
,和畸形
以表明这些要求。
不需要诊断
-程序有语义错误,一般情况下不能诊断。违反ODR或仅在链接时间%29可检测到的其他错误。如果执行这样的程序,则该行为是未定义的。
实现-定义的行为
-程序的行为因实现而异,符合的实现必须记录每种行为的效果。例如,std::size_t
或字节中的位数,或std::bad_alloc::what
实现定义行为的子集是特定地区行为
,这取决于提供的实现。现场...
未指定行为
-程序的行为因实现而异,不需要符合的实现来记录每种行为的效果。例如,评价顺序,是否相同字符串文字数组分配开销的大小等等。每个未指定的行为都会产生一组有效的结果之一。
未定义行为
-对该方案的行为没有任何限制。未定义行为
的例子有:数组边界外的内存访问、带符号整数溢出、空指针取消引用、对同一个标量的修改。不止一次在没有序列点的表达式中,通过不同类型的指针访问对象,等等。编译器不需要诊断未定义的行为%28,尽管许多简单的情况被诊断为%29,并且编译的程序不需要做任何有意义的事情。
UB与优化
由于正确的C++程序不存在未定义的行为,因此,当实际具有UB的程序在启用优化的情况下编译时,编译器可能会产生意外的结果:
例如,
符号溢出
二次
int foo(int x) {
return x+1 > x; // either true or UB due to signed overflow
}
二次
可以编译为%28演示29%。
二次
foo(int):
movl $1, %eax
ret
二次
出界
二次
int table[4] = {};
bool exists_in_table(int v)
{
// return true in one of the first 4 iterations or UB due to out-of-bounds access
for (int i = 0; i <= 4; i++) {
if (table[i] == v) return true;
}
return false;
}
二次
可以编译为%28演示29%。
二次
exists_in_table(int):
movl $1, %eax
ret
二次
未初始化标量
二次
std::size_t f(int x)
{
std::size_t a;
if(x) // either x nonzero or UB
a = 42;
return a;
}
二次
可以编译为%28演示29%。
二次
f(int):
mov eax, 42
ret
二次
在早期版本的GCC上观察到了所显示的输出。
二次
bool p; // uninitialized local variable
if(p) // UB access to uninitialized scalar
std::puts("p is true"
if(!p) // UB access to uninitialized scalar
std::puts("p is false"
二次
可能的产出:
二次
p is true
p is false
二次
访问传递给realloc的指针
选择clang来观察所显示的输出。
二次
#include <iostream>
#include <cstdlib>
int main() {
int *p = (int*)std::malloc(sizeof(int)
int *q = (int*)std::realloc(p, sizeof(int)
*p = 1; // UB access to a pointer that was passed to realloc
*q = 2;
if (p == q) // UB access to a pointer that was passed to realloc
std::cout << *p << *q << '\n';
}
二次
可能的产出:
二次
12
二次
无副作用的无限循环
选择clang来观察所显示的输出。
二次
#include <iostream>
int fermat() {
const int MAX = 1000;
int a=1,b=1,c=1;
// Endless loop with no side effects is UB
while (1) {
if (((a*a*a) == ((b*b*b)+(c*c*c)))) return 1;
a++;
if (a>MAX) { a=1; b++; }
if (b>MAX) { b=1; c++; }
if (c>MAX) { c=1;}
}
return 0;
}
int main() {
if (fermat())
std::cout << "Fermat's Last Theorem has been disproved.\n";
else
std::cout << "Fermat's Last Theorem has not been disproved.\n";
}
二次
可能的产出:
二次
Fermat's Last Theorem has been disproved.
二次
外部链接
- 使用空指针的乐趣,第1部分在Linux 2.6.30中由于空指针取消引用%29而引起的%28本地攻击
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。