位移运算右操作数类型

8

我想知道C/C++位移运算符的正确右操作数是什么。

截至撰写本文时,内置算术类型都少于256位,因此单个字节足够。此外,x86移位指令使用imm8。这表明右操作数应该是一个unsigned char,在此处使用不同类型将需要类型转换。

这里是否有“最正确”的类型可用?我知道标准对于位移的其他方面非常宽松,所以也许这是另一种情况?


2
就所有目的而言,类型为 int - Steve Summit
C 11 的表达式语法可以在这里找到:https://port70.net/~nsz/c/c11/n1570.html#A.2.1。根据表达式和计数的方式,移位表达式距顶级 primary-expression 大约有五到八个级别的差距。是的,有很多路径会导致 shift-expression,所以我认为“宽容”是语法的准确描述。 - Andrew Henle
1
关于“此外,x86移位指令使用imm8”:机器指令可能会影响C语言的实现(例如实现对整数类型使用的宽度的选择),但并不是强制性的。使用编译器的主要目的之一是让它生成指令,并且它将生成必要的指令来实现C语义,即使这意味着使用多个指令来完成任务。 - Eric Postpischil
2个回答

9

任何整数类型都可以用作按位移位运算符的右操作数,只要其值至少为0且小于左操作数的比特长度。

这在C标准关于按位移位运算符的第6.5.7节第2和第3段中有详细规定:

2 每个操作数都必须具有整数类型

3 对每个操作数执行整数提升。结果的类型是提升后的左操作数的类型。如果右操作数的值为负数或大于等于提升后的左操作数的宽度,则行为未定义。

因此,虽然一个unsigned char的范围足以容纳任何有效值,但右操作数将被提升为int。


2

C语言倾向于将所有东西都作为至少int类型处理,因此如果左移运算符<<和右移运算符>>的右侧指定为unsigned short或unsigned char,则会非常令人惊讶。

很难想象为什么程序员会在那里使用long(或更可怕的是long long),但我刚试了这段代码:

int main()
{
    int x = 16;
    long int y = 2;
    int z1 = x << y;
    int z2 = x >> y;
    printf("%d %d\n", z1, z2);

    long long int y2 = 2;
    z1 = x << y2;
    z2 = x >> y2;
    printf("%d %d\n", z1, z2);
}

我在两个编译器下编译了它,都没有警告,并且两个程序都打印出了64 4。(虽然不是决定性的测试,但具有启示意义。)


为什么要凭直觉和不确定的测试来回答问题,当我们已经有了引用权威来源——C标准的答案呢? - Eric Postpischil
为什么它要给你任何警告?只有当编译器能够预测右操作数的值,并且这个值为负数或大于左操作数的宽度时,才会发出警告。 - 0___________
@EricPostpischil 因为有时候,根据提问者的需求,对一个答案有多个角度的解释是有用的,我认为这可能是其中之一。(此外,虽然经验测试可能存在风险,但我想给提问者一个温和的提示,让他们尝试一下。) - Steve Summit
如果在那里使用 longlong long 有任何问题,如果有任何理由要打击它,那么一个高品质的编译器(我尝试了gcc和clang)可能会发出错误。既然两者都没有,那就表明,在这些运算符中使用比 int 更宽的右操作数是没有问题的。 - Steve Summit
“很难想象为什么程序员会使用long”:因为他们已经解码了存储在long中的各种信息,例如(x >> 23 & 0xFF),以获取他们想要用作移位量的一些位,之后e >> (x >> 23 & 0xFF)具有一个long作为右操作数,只是因为这是在对x进行操作后得到的结果。 - Eric Postpischil

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