为什么C语言没有左/右旋转运算符?

15

我想这是一个有哲学意味的问题。

C语言有标准的位运算,包括异或左/右位移等。为什么在语言中没有包含旋转左/右操作符或函数呢?

这些运算符的复杂度与其他位运算符相同,并且通常只需要一个类似于其他位运算符的单一汇编指令即可实现。此外,我可以想到许多用途,可能不比xor运算符少 - 因此,对我来说,它们没有与其他运算符一起包含在C语言中听起来有点奇怪。

如果您需要在C或C++中进行旋转操作, 这里有一个关于最佳实践的专门问答。但讨论这个问题已经超出了本问题的范围。


1
可以轻松地使用汇编完成。https://dev59.com/J3RA5IYBdhLWcg3w8SiJ - Anirudh Ramanathan
3
@Cthulhu,我知道这一点,甚至在问题描述中提到了。问题是关于 C 的。 - SomeWittyUsername
1
因为它被遗忘了?;-) 我喜欢这些:http://stackoverflow.com/a/12733007/694576 - alk
@Will 就编译器而言,操作数的大小是明确定义的。如果结果与预期不符,则可能是程序中的错误。 - SomeWittyUsername
我怀疑原因可能是政治上的。在unsigned int长度为2的幂且不包含填充位的平台上,左旋转/右旋转是有意义的。在其他平台上则不太合理。标准委员会定义一个只在99%的C平台上有意义的操作,实际上等同于将剩余的1%视为另类。从技术角度来看,对于大多数编译器来说,包含左/右旋转内部函数比识别数百(或数千)种不同的最小复杂度方式的Peephole优化器更容易... - supercat
显示剩余6条评论
2个回答

6
我认为这是因为有两种旋转方式:带进位和不带进位,这使得旋转的实现方式根据机器的进位标志(1或0)而不同。这意味着需要实现4个操作符,从而使语言变得不必要地复杂,而旋转可以像@Aniket所展示的那样简单地实现。

编辑:

然而,移位也可以进行有符号和无符号的操作。实际上,Javascript都支持这两种运算符。但是,由于C支持有符号和无符号变量,所以执行有符号移位没有意义,因为编译器应该知道我们是否移动了有符号或无符号变量。 有符号/无符号移位对于算术计算很有用,C编译器可以使用它们生成汇编代码。例如,许多算术运算,如乘以或除以2的幂,都被编译器翻译成移位操作。C语言中有移位运算符的唯一原因是为了处理位掩码。

如果有@icepack,那么其他运算符也不再需要了。我们只需要一个运算符(NAND),就可以实现所有的布尔逻辑。 - Aniket Inge
“简单”?!……这是一个词……但实际上它并不那么简单。 - Memos Electron
@Aniket 不确定是否足够进行移位和旋转,但基本上是可以的。 - SomeWittyUsername
是的,我知道。移位可以进行有符号和无符号的操作。实际上,据我所知,Javascript都有这两个运算符。然而,由于C支持有符号和无符号变量,因此我认为执行有符号移位没有任何意义,因为编译器应该检测到我们是否在移动有符号或无符号变量。有符号/无符号移位用于算术计算,但在C中,你常常使用移位进行位掩码(即无符号移位)。 - Claudi
@icepack:大部分(全部?)的CPU都支持逻辑移位和算术移位,并且只要移位量小于字长,它们的行为就是相同的。另一方面,有些CPU仅支持通过进位旋转,而其他一些则仅支持无进位旋转。 - ninjalj
显示剩余5条评论

1

在 C 语言中没有提供二进制的左旋和右旋操作。你可以自己编写左旋和右旋函数来实现这些功能。但根据标准规定,它们并不存在。

简单的左旋示例:

int rotate_left(int num, int bits)
{
  return ((num << bits) | (num >> (32 -bits)));
} 

int rotate_right(int num, int bits)
{
  return ((num >> bits) | (num << (32 -bits)));
}

上述函数仅适用于32位整数 :)
现在说一下哲学:C语言旨在尽可能地实现可移植性。这就是标准团队希望它成为“可移植汇编程序”的原因。未来的架构中可能没有保证存在rol和ror,或者可能行为不同。因此,它被远离了标准。

3
好的,实际问题是“有人知道为什么左旋/右旋没有包含在语言中吗?” - Memos Electron
4
抱歉,我不能担任翻译工作,因为我只能用英文回答问题。 - Mat
@Mat: 不要啊!真的!请告诉我原因。 - Grijesh Chauhan
6
这段代码中存在许多未定义行为,对负整数进行左移是未定义行为,对非负值带符号整数进行左移如果 value * 2^shift 不能用该类型表示也是未定义行为。对负整数进行右移是实现定义的,通常会进行符号扩展,因此这些操作实际上不会旋转。将它们操作的对象改成无符号整数,你只需要满足移动距离是非负且小于该类型的位宽度(bits &= 31),如果 bits == 0,则返回 num。 - Daniel Fischer
在x86上使用gcc 4.9.2时,检查bits &= 31实际上会导致额外的指令:请参见我在https://dev59.com/1XE85IYBdhLWcg3wUByz上的评论。当许多行为未定义时,这真的很烦人,主要是因为标准不想假设二进制补码。 - Peter Cordes
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接