无符号按位移位算子[C11]

时间:2014-04-27 12:05:03

标签: c bit-manipulation bitwise-operators c11

  

编辑:如下所述,我错过了ANSI C标准的第一部分:   “如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。”错误(或者错误的错误/差异) )是由于我使用的特定编译器。

我遇到了一些有点奇怪的事情,我希望有人可以在这里阐明我的无知。必要的示例代码如下:

#include <stdio.h>
int main(void)
{
    unsigned a, b;
    int w, x, y;

    a = 0x00000001;
    b = 0x00000020;
    w = 31;
    x = 32;
    y = 33;

    a << w; /*No error*/
    a << x; /*No error*/
    a << y; /*No error*/

    a << 31; /*No error*/
    a << 32; /*Error*/
    a << 33; /*Error*/

    a << 31U; /*No error*/
    a << 32U; /*Error*/
    a << 33U; /*Error*/

    a << w + 1; /*No error*/
    a << b; /*No error*/

    return 0;
}

我的问题是:为什么原始数字会返回错误,但对于任何变量都不会?我认为,他们应该受到同等对待。根据C11标准

  

E1的结果&lt;&lt; E2是E1左移E2位位置;腾出的位充满了   零。如果E1具有无符号类型,则结果的值为E1×2 ^ E2,减少模数   比结果类型中可表示的最大值多一个。如果E1有签名   类型和非负值,E1×2 E2可以在结果类型中表示,然后就是   结果价值;否则,行为未定义。

右侧,因为左边是无符号类型,应该比结果类型中可表示的最大值多2 ^ E2减少模数....这句话对我来说并不完全清楚,但在实践中它似乎它是E1&lt;&lt; (E2%32) - 尽管32不是结果类型中可表示的最大值。无论如何,C11标准尚未定义,但错误

  

左移计数&gt; =类型的宽度[默认启用]

尝试编译时出现

。我无法推断出为什么> 31的某些值有效(例如x = 33; a&lt;

我在64位Fedora上使用GCC编译器。 提前致谢。 - 将会

4 个答案:

答案 0 :(得分:2)

  

我的问题是:为什么原始数字会返回错误,但对于任何变量都不会?

因为没有编译器警告不能保证良好的程序行为。编译器对a << x发出警告是正确的,但它没有。{/ p>

  

我认为他们应该被视为相同的

编译器在为a << 33发出警告时帮助你。当它没有警告a << y时,它对你没有帮助,但是编译器不需要你帮忙。

如果您想确定您的程序不包含未定义的行为,则不能依赖编译器警告的缺失,但您可以使用声音静态分析器。如果未定义行为的声音静态分析器在您的程序中没有检测到任何行为,那么您可以得出结论它不会产生任何(以模块为相关分析器记录的使用条件)。例如:

$ frama-c -val t.c ... t.c:13:[kernel] warning: invalid RHS operand for shift. assert 0 ≤ x < 32;

  

在实践中似乎是E1&lt;&lt; (E2%32)

您看到这个的原因是这是x86_64指令集中的移位指令实现的行为。 但是,移动负数或大于类型宽度的数字是未定义的行为。它在其他体系结构上的工作方式不同,甚至您的体系结构的某些编译器也可以在编译时(作为常量传播阶段的一部分)计算它,其规则与您注意到的规则不同。在E1 << (E2%32) d之后,不要依赖free()结果,而是依赖于仍包含正确结果的记忆。

答案 1 :(得分:1)

  

右侧,因为左边是无符号类型,应该比结果类型中可表示的最大值多2 ^ E2减少模数....这句话对我来说并不完全清楚,但在实践中它似乎它是E1&lt;&lt; (E2%32) - 尽管32不是结果类型中可表示的最大值。

这不是正确的解释。结果是模2 ^ 32,而不是E2。该句子描述了如何丢弃从左侧移位的位。结果,如果允许,任何大于或等于int中的位数的E2将为零。由于大于或等于该位数的位移是未定义的行为,编译器会帮助您在编译时产生错误,而不是将其保留到运行时以便发生奇怪和不正确的事情。

答案 2 :(得分:0)

对于 n 位数据移位仅适用于值 x> 0 x <= n-1 ,其中x为no有点转移。

这里在你的情况下unsigned的内存大小等于32位所以只有可能的移位范围从1到31.你试图将数据移到该变量的存储大小之外,这就是为什么它给你错误的原因

答案 3 :(得分:0)

  

模数超过结果类型中可表示的最大值....

表示E1 * 2^E2的值mod (UINT_MAX+1)减少unsigned int。这与您关于E2的假设没有任何关系。

  

无论如何,C11标准尚未定义,

您忘记在引用之前阅读该段落:

  

如果右操作数的值为负或者是   大于或等于提升的左操作数的宽度,行为是未定义的。

32或更多的所有班次都会导致未定义的行为。编译器不需要发出关于此的警告,但在某些情况下它对你很好。