C 中的操作重排序和符号性

mar*_*nev 2 c signed c99 compiler-optimization

我发现了几个讨论有符号与无符号编译器优化的问题。一般结论是,由于溢出的未定义行为,有符号允许更多优化。但是,我没有找到任何关于签名如何与操作重新排序一起发挥作用的讨论。

考虑表达式

a - b + c
Run Code Online (Sandbox Code Playgroud)

如果所有值都是无符号的,编译器总是可以重新排序操作(添加ac首先可以提高性能)。

如果所有值都有符号,编译器必须证明重新排序不会导致发生溢出。一般来说a + c,编译器可能会溢出,因此它可以重新排序的内容受到限制。

我是否正确地认为编译器可以更自由地对无符号值的操作进行重新排序?


编辑

我本来想问有关*and等其他操作,但我想这个问题只对and/有意义。一般来说,无论整数是有符号还是无符号,乘法和除法都不能重新排序。+-

Eri*_*hil 7

\n

如果所有值都有符号,编译器必须证明重新排序不会导致发生溢出。

\n
\n

这个前提是错误的。如果 C 程序中没有未定义的行为,则编译器\xe2\x80\x99 的负担就是产生该行为。C 标准不限制它的实现方式。如果它重新排序a - b + ca + c - b,但它使用行为适当的指令来执行此操作,则将a + c - b计算与 相同的结果a - b + c

\n

考虑使用 32 位对象的示例intais 7FFFFFF0 16bis 100 16cis 20 16。那么a - b将是 7FFFFEF0 16,并且a - b + c将是 7FFFFF10 16。如果编译器使用产生 80000010 16 的a + c加法指令进行计算,然后使用产生 7FFFFF10 16的减法指令进行计算a + c - b进行计算,则结果是正确的。

\n

这是有效的,因为加法和减法指令不会溢出,并且即使超出范围也会产生所需的结果int。此类指令不仅在当今的\xe2\x80\x99s 处理器上很常见,而且相同的加法和减法指令也用于有符号和无符号算术,因为加法和减法的位模式对于无符号和两个\xe2\x80 是相同的\x99s 补码表示。(比较指令有所不同,因为这些位在解释为无符号或两个\xe2\x80\x99s补码时具有不同的含义。)

\n

如果源代码实际上被更改为a + c - b,则程序将具有未定义的行为,这一事实是无关紧要的。编译器通常不会通过名义上将 C 源代码重新排列为随后编译的其他 C 源代码来进行操作。编译器通常通过将 C 语义转换为某种内部表示,然后生成汇编代码来实现这些语义来进行操作。

\n

  • [编译器优化可能会导致整数溢出。可以吗?](/sf/ask/5187185811/) 有证据表明编译器进行了这种优化。(至少 clang 是这样的;GCC 实际上*确实*因为不想在 asm 中引入有符号溢出而烦恼,因为它在源代码中未定义。与使用相同的 bool 标志来表示多个事物有关GCC 内部 IIRC。)基本上是重复的,但来自另一个方向的同一问题。 (2认同)