我使用 Visual Studio、Ubuntu 的 GCC 编译器、英特尔编译器和 MinGW 进行了右移测试。所有的移位都在符号位上。我猜想 Xcode 的 GCC 也是这样做的。
我知道这种行为是实现特定的,但是看起来所有主流的桌面/服务器编译器都实现了算术移位。是否有任何广泛使用的编译器不在符号位上进行移位?
谢谢。
我使用 Visual Studio、Ubuntu 的 GCC 编译器、英特尔编译器和 MinGW 进行了右移测试。所有的移位都在符号位上。我猜想 Xcode 的 GCC 也是这样做的。
我知道这种行为是实现特定的,但是看起来所有主流的桌面/服务器编译器都实现了算术移位。是否有任何广泛使用的编译器不在符号位上进行移位?
谢谢。
C可以运行在许多不同的架构上。我是说很多种不同的架构。你可以让C代码运行在嵌入式DSP和Cray超级计算机上。
大多数人认为的C标准中的“实现定义”部分只会在一些冷门的架构上出问题。例如,在某些DSP和Cray超级计算机上,CHAR_BIT
可能非常巨大,如32或64。因此,如果您在x86上尝试您的代码并且可能慷慨地考虑PowerPC、ARM或SPARC,那么您不太可能遇到任何真正奇怪的情况。这很好。现在大部分代码都将在以字节为导向的架构上运行,具有二进制补码整数和算术移位。毫无疑问,在可预见的未来,任何新的CPU架构都将是这样的。
但让我们看看最常见的两种整数表示方法:二进制补码和反码:
switch ((-1) >> 1) {
case 0:
case -0:
puts("Hello, ones' complement world!");
// Possibly sign-magnitude.
break;
case -1:
puts("Hello, two's complement world!");
break;
default:
puts("Hello, computer without arithmetic shift");
break;
}
别担心。当你想除以一个数时,只需使用/
,需要进行位移操作时使用>>
。即使是糟糕的编译器也擅长优化这些操作。(请记住,如果x为负数,则x/2 != x>>1
,除非你在使用补码机器,但几乎肯定不是真的。)
标准确保如果 (int) x
不是负数,则 (int) x >> n == (unsigned) x >> n
,因此编译器没有太多的余地做出完全意外的操作。
shr
而不是sar
。C标准只规定这是“实现定义”,并没有规定二进制补码系统必须使用算术移位。类似于您提到的关于良好CPU的观点,许多C假定实现选择是明智和有益的。仅仅遵循标准是*不足以让C编译器在现实生活中可用的。有时写完全可移植的代码并不值得麻烦或膨胀,因为ISO C在某些方面过于可移植。 - Peter Cordes一般而言,这取决于编译器使用的目标架构。如果架构具有算术(有符号)和逻辑(无符号)移位指令,则该架构的C编译器将使用适当的指令。另一方面,如果只有逻辑移位指令,C编译器将仅使用该指令,即使它对负值不“做正确的事情”,因为C规范允许编译器执行任何操作。
Cray C编译器默认对有符号数进行逻辑右移,但也有选项可选择算术右移。
通常情况下,可以安全地假定有符号右移是算术右移。