具有未定义结果的C代码,编译器生成无效代码(使用-O3)

fwe*_*nom 7 c gcc undefined-behavior

我知道当你在C程序中做某些事情时,结果是不确定的.但是,编译器不应该生成无效(机器)代码,对吧?如果代码做错了,或者代码生成了段错误或其他什么,那将是合理的......

这应该是根据编译器规范发生的,还是编译器中的错误?

这是我正在使用的(简单)程序:

int main() {
    char *ptr = 0;
    *(ptr) = 0;
}
Run Code Online (Sandbox Code Playgroud)

我正在编译-O3.那不应该生成无效的硬件指令,对吧?有了-O0,我在运行代码时遇到了段错误.这看起来更加明智.

编辑:它正在生成一条ud2指令......

Sha*_*our 13

ud2的指令是"有效指令",它代表了未定义指令,并生成一个无效的操作码异常,显然GCC当程序调用未定义行为能够产生这种代码.

clang上面的链接可以解释如下:

存储为null并通过空指针调用将转换为__builtin_trap()调用(在x86上变为陷阱指令,如"ud2").这些在优化代码中一直发生(作为其他转换的结果,如内联和常量传播),我们过去只是删除包含它们的块,因为它们"显然无法访问".

虽然(从迂腐的语言律师角度来看)这是完全正确的,但我们很快就知道人们偶尔会取消引用空指针,并且让代码执行只是落入下一个函数的顶部,这使得理解问题变得非常困难.从性能角度来看,暴露这些的最重要方面是压缩下游代码.因此,clang将这些转换为运行时陷阱:如果其中一个实际上是动态到达的,程序会立即停止并可以调试.这样做的缺点是我们通过执行这些操作并具有控制其谓词的条件来略微膨胀代码.

在一天结束时,一旦调用未定义的行为,程序的行为就无法预测.这里的理念是,最好是崩溃并让开发人员指出某些事情是严重错误的,并允许他们调试正确的点,而不是生成一个似乎有效但实际上已经破解的程序.

正如Ruslan所指出的那样,它在某种意义上是"有效的",它保证引发无效的操作码异常,而不是将来可能有效的其他未使用的序列.

  • 嗯,`ud2` 和 `dw 0xffff` 一样有效。唯一使它“有效”的是它保证总是无效的,而其他无效的字节序列可以被认为是保留的,并且可能在未来的 CPU 实现中变得有效。 (3认同)