委婉地说,未定义的行为是语言设计中的一个模棱两可的决定。然而,数以百万计的程序员不得不忍受这种邪恶。在标准中,出现未定义行为的情况是用非常繁重的技术语言描述的。毫不奇怪,大量的程序员,以及他们中的大部分人,根本没有阅读标准,如果他们阅读了,他们一半看不懂,而忘记了另一半。
最糟糕的不是标准通常允许未定义的行为,或者它相当普遍。事实上,在生活中,普通程序员根本无法理解。更令人失望的是编译器开发人员不寻求帮助他们的客户,一个普通的程序员,尽管他们可以,一个教科书的例子:
int i = 0;
i = ++i + i++;
在这里,我的 MSVC 默默地吃这段代码。在很多情况下编译器会警告程序员。
他们为什么不报告?因为他们不必。但是,某些编译器可能会报告某些可能导致未定义行为的情况。因此,如果您使用 GCC 或 clang 从问题中构建代码,您将收到警告。编译器还涵盖了其他情况,但不是全部。
对于其他情况,还有各种静态分析器,例如 PVS Studio、clang-tidy(内置于 CLion & Resharper++)等。
我建议从一个稍微不同的角度来看待未定义的行为:在大多数情况下,未定义的行为直接发生在语言结构中,程序员违反了标准中固定的一些要求,如果没有这些要求,这些结构可能在当前版本的语言中根本不存在。换句话说,编译器被明确允许期望程序员不要做某些事情,因为没有这样的让步是不可能编写编译器的。
简单的例子:
在这里,这件作品
x += 1;
暗示了一大堆潜在的未定义行为:x
可能是对无效对象的引用x
可以是对另一种类型的对象的引用(又名严格别名违规)x
可以是对有效对象的引用,但是具有 const 限定符的对象的一部分x
将从其他线程访问编译器别无选择,只能假设这里不会发生任何坏事。
为什么语言会出现这种情况是另一个问题。必须假设大多数此类问题来自 S。
它可以是为特定编译器(包括自动生成的编译器)量身定制的遗留代码和/或代码。毕竟,行为不仅仅根据标准来定义,编译器/平台对任何问题都有一个特定的答案。