我们都听过这样的警告:如果您在 C 或 C++ 中调用未定义的行为,任何事情都可能发生。
这是否仅限于任何运行时行为,或者这是否还包括任何编译时行为?特别是,在遇到调用未定义行为的构造时,编译器是否允许拒绝代码(在标准中没有其他要求的情况下),甚至崩溃?
我们都听过这样的警告:如果您在 C 或 C++ 中调用未定义的行为,任何事情都可能发生。
这是否仅限于任何运行时行为,或者这是否还包括任何编译时行为?特别是,在遇到调用未定义行为的构造时,编译器是否允许拒绝代码(在标准中没有其他要求的情况下),甚至崩溃?
“你们都忽略了实际的定义,只关注注释,标准没有要求。 ” - @ R.MartinhoFernandes
上面的消息是由Lounge<C++>中的给定用户编写的,并且是一个非常有效的论点;当涉及到调用未定义行为的代码时,该标准没有强加任何要求。
undefined-behavior甚至延伸到编译器解析输入数据(即代码)的最远角落,正如以下来自C++11和C99标准的引用所验证的那样。
一句话回答你的问题;
“以环境的记录方式特征”是一种奇怪的陈述,您几乎可以编写一个编译器来记录它可能会在任何给定的代码(这是无效的)上崩溃,以赋予它随时崩溃的可能性。
1.引用C++11/C99标准
### c++11
###1.3.24 [defns.undefined]
未定义的行为;本国际标准没有要求的行为
[注:
当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。
允许的未定义行为的范围从完全忽略具有不可预测结果的情况,到在翻译 或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到 终止翻译或执行(有发出的诊断消息)。
许多错误的程序结构不会产生未定义的行为;他们需要被诊断出来。
——尾注]
### c99
3.4.3 -未定义的行为
行为,在使用不可移植或错误程序构造或错误数据时,本>国际标准对此没有要求
注意 可能的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到终止翻译或执行(使用发出诊断消息)。
如果行为未定义,编译器可以接受、拒绝、发出警告,并根据标准,甚至在您的计算机上崩溃、挂起或安装病毒。
实际上,这并不意味着如果您正在编写编译器,您应该故意做这些事情,但是您可以使用一种算法,例如,如果性能优势证明它是合理的,那么您可以使用一种适用于已定义案例并崩溃或挂起未定义案例的算法.
尽管如此,一个有信誉的编译器会避免这种情况,或者至少有很好的文档记录。
它不限于运行时行为。根据 ISO/IEC 14882,第一版,1998-09-01, 1.3.12,在注释中(因此非规范性):“允许的未定义行为范围从……到在翻译或编程执行期间以记录方式执行的行为特征环境”。换句话说,该标准说,只要有文档,实现可以做操作系统(或其他环境)允许的任何事情。
我一直喜欢标准引号,所以如果这是您要寻找的,标准将未定义的行为定义为
本国际标准没有要求的行为
[注意:当本国际标准省略了程序使用错误构造或错误数据时的行为的任何明确定义时,可能会出现未定义的行为。允许的未定义行为的范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到终止翻译或执行(发出的诊断消息)。许多错误的程序结构不会产生未定义的行为;他们需要被诊断出来。——尾注]
而“翻译”基本上是从源代码到最终产品(组装或其他)。所以我们可以混合这两种可能性并得到
在翻译过程中完全忽略了情况,结果无法预测
所以是的,编译器可以在编译期间和运行时自由地表现出未定义的行为。
至少某些形式的未定义行为可能会导致编译行为本身的行为超出标准的管辖范围。例如,标准的作者不想排除使用类似
#include `someProg arg`
或者
#pragma exec-include "someProg arg"
作为someProg
使用给定参数运行的一种方式,并将此类程序的输出视为源文本的一部分。由于此类语法调用的程序的行为将超出标准的管辖范围,因此从标准的角度来看,此类构造仅被视为在编译时调用 UB。