包含未定义行为的源代码使编译器崩溃合法吗? 我想编译一些写得很差的C++源代码,调用未定义的行为,因此(如他们所说)“任何事情都可能发生”。 从C++语言规范中的“一致性”编译器所接受的角度来看,在这种情况下,“任何”都包括编译器崩溃(或窃取密码,或在编译时出错或出错),或者未定义行为的范围是否仅限于结果可执行文件运行时可能发生的情况?

包含未定义行为的源代码使编译器崩溃合法吗? 我想编译一些写得很差的C++源代码,调用未定义的行为,因此(如他们所说)“任何事情都可能发生”。 从C++语言规范中的“一致性”编译器所接受的角度来看,在这种情况下,“任何”都包括编译器崩溃(或窃取密码,或在编译时出错或出错),或者未定义行为的范围是否仅限于结果可执行文件运行时可能发生的情况?,c++,language-lawyer,undefined-behavior,C++,Language Lawyer,Undefined Behavior,未定义行为的标准定义如下: 本国际标准不要求的行为 [ 注:当此国际标准发生变化时,可能会出现未定义的行为 标准省略了行为的任何明确定义,或者当程序 使用错误的构造或错误的数据。允许的未定义 行为范围从完全忽略情况到 不可预测的结果,翻译或编程过程中的行为 以环境特有的文件化方式执行 (无论是否发出诊断信息),终止 翻译或执行(通过发布诊断文件) 许多错误的程序构造不会产生未定义的 行为;需要诊断。常数的评估 表达式从不显示显式指定为未定义的行为。  — 尾注 ] 虽然注释本身并不规范,但它确

未定义行为的标准定义如下:

本国际标准不要求的行为

[ 注:当此国际标准发生变化时,可能会出现未定义的行为 标准省略了行为的任何明确定义,或者当程序 使用错误的构造或错误的数据。允许的未定义 行为范围从完全忽略情况到 不可预测的结果,翻译或编程过程中的行为 以环境特有的文件化方式执行 (无论是否发出诊断信息),终止 翻译或执行(通过发布诊断文件) 许多错误的程序构造不会产生未定义的 行为;需要诊断。常数的评估 表达式从不显示显式指定为未定义的行为。  — 尾注 ]


虽然注释本身并不规范,但它确实描述了一系列已知的实现所表现出的行为,根据该注释是合法的。但实际上,正如规范性文本所说,该标准对执行或翻译没有任何限制。如果某个实现窃取了您的密码,则不违反该标准中规定的任何合同。

什么是“合法的”这里的意思是:任何与C标准或C++标准不矛盾的东西都是合法的,根据这些标准,如果你执行一个语句“代码> i= i++;< /COD>”,结果恐龙就接管了世界,这与标准不矛盾,它确实违背了物理定律,所以不会发生:-)

如果未定义的行为会破坏编译器,这并不违反C或C++标准。但是,这意味着编译器的质量可以(也应该)得到改善。 在以前版本的C标准中,存在错误或不依赖于未定义行为的声明:

char* p = 1 / 0;

允许将常量0赋给char*。不允许使用非零常量。由于1/0的值是未定义的行为,因此编译器是否应该接受此语句是未定义的行为。(现在,1/0不再满足“整型常量表达式”的定义).

我们通常担心的大多数类型的UB,如NULL deref或被零除,都是运行时UB。编译一个如果执行会导致运行时UB的函数一定不会导致编译器崩溃。除非它能证明该函数(以及通过该函数的路径)肯定会被程序执行

(第二个想法:可能我没有考虑编译时需要对模板/constexpr进行评估。在编译期间,可能允许UB在翻译过程中造成任意的奇怪,即使结果函数从未调用过。)

在ISO C++引用中,翻译部分的行为类似于ISO标准中使用的语言。C在编译时不包括模板或<代码> CONSTEPRP< /代码>强制性EVA.< 但是有趣的事实:ISO C在一份说明中说,如果翻译被终止,它必须带有诊断信息。或者“翻译过程中的行为……以记录的方式”。我不认为“完全忽略情况”可以理解为包括停止翻译


旧答案,在我了解翻译时间UB之前就写好了。但对于运行时UB来说确实如此,因此可能仍然有用


在编译时没有UB这样的事情。它可以在一定的执行路径上对编译器可见,但是在C++中,直到执行通过函数执行该路径时才发生。

程序中的缺陷,使得编译不可能不是乌布,它们是语法错误。这样的程序在C++术语中是“不好”的(如果我的标准是正确的)。程序可以很好地形成,但包含ub。< /p>

除非我误解了一些东西,否则ISO C++需要这个程序正确编译和执行,因为执行永远不会达到零。好的编译器只会生成工作可执行文件。GCC/CLAN警告<代码> X/0 但不是这样,即使在优化时。但是,我们试图告诉大家ISO C++的实现质量是如何低的。因此,检查Gcc/CLAN除了确认我正确编写程序之外,几乎不是一个有用的测试。 这方面的一个用例可能涉及C预处理器,或者

constexpr
变量和这些变量上的分支,这会导致在一些路径中出现无意义的情况,而这些路径对于常数的选择是永远无法到达的

导致编译时可见UB的执行路径可以假定为从不使用,例如,x86编译器可以发出
ud2
(导致非法指令异常)作为
cause\u UB()
。或者在函数中,如果
if()
的一侧导致可证明UB,则可以删除分支

但是编译器仍然必须以一种理智和正确的方式编译其他所有内容。所有没有遇到(或无法证明遇到)UB的路径都必须编译为asm,即
int cause_UB() {
    int x=0;
    return 1 / x;      // UB if ever reached.
 // Note I'm avoiding  x/0  in case that counts as translation time UB.
 // UB still obvious when optimizing across statements, though.
}

int main(){
    if (0)
        cause_UB();
}
int minefield(int x) {
    if (x == 3) {
        *(char*)nullptr = x/0;
    }

    return x * 5;
}