首先,来自 C++03 标准的一些定义:
1.3.5 实现定义的行为
行为,对于格式良好的程序构造和正确的数据,取决于实现并且每个实现都应记录
1.3.12 未定义的行为
行为,例如在使用错误程序结构或错误数据时可能出现的行为,本国际标准对此没有任何要求。当本国际标准省略任何明确定义或行为的描述时,也可能出现未定义行为。
1.3.13 未指明的行为
行为,对于格式良好的程序构造和正确的数据,取决于实现。实现不需要记录发生了哪些行为。
尽管可以将未指定的行为称为 UB,但我从未见过,而且 UB 始终意味着未定义的行为。 贯穿整个标准的语句类似于“执行 X 是未定义的行为”,但有时您会遇到根本未涵盖的情况。
换一种说法,如果你在任何地方有任何未定义的行为,那么所有的赌注都是 off。就标准而言,您的程序可以做任何事情,从邀请您的岳母参加 SuperBowl 周末到运行 nethack。由于 UB 的本质,您无法对其进行测试,并且您不能期望编译器提供任何帮助。(尽管对于一些微不足道的常见错误,编译器通常会产生诊断。)
通常某些东西被定义为 UB,因为它在逻辑上没有意义(例如访问越界数组),但也经常因为它需要实现做太多工作来防止 - 通常在运行时。请记住,C++ 源自 C,能够生成高度优化的程序是这两种语言的主要目标。为此,语言遵从程序员的要求,以确保代码在这些情况下是正确的,这与“你不为你不使用的东西付费”的原则有关。
所以,最后,UB 很糟糕,非常糟糕;不惜一切代价避免它。但是,UB 的难点在于不知道它是什么或在什么情况下发生。当您调用 UB 时,困难的部分是识别。例如:
std::string s = "abc";
char& c = s[0];
cout.write(s.data(), s.length());
c = '-';
看起来完全合理,对吧?不,这是 UB,但它会在所有流行的实现中按您期望的那样工作。