繁体   English   中英

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

[英]Unsigned Bitwise Shift 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 << E2的结果是E1左移E2位位置; 空位用零填充。 如果E1具有无符号类型,则结果的值为E1×2 ^ E2,比结果类型中可表示的最大值多模减少1。 如果E1具有带符号的类型和非负值,并且E1×2 E2在结果类型中可表示,则这是结果值; 否则,行为是不确定的。

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

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

尝试编译时显示。 我无法推断出为什么> 31的某些值起作用(例如x = 33; a <

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

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

因为缺少编译器警告并不能保证良好的程序行为。 编译器对a << x发出警告是正确的,但并非必须如此。

我认为应该对他们进行同样的对待

编译器在警告a << 33时对您有所帮助。 当它不为a << y发出警告时,它不会帮您任何忙,但是编译器不必帮您任何忙。

如果要确定程序不包含未定义的行为,则可以不依赖于缺少编译器警告,但是可以使用声音静态分析器。 如果用于不确定行为的声音静态分析仪未在您的程序中检测到任何东西,那么您可以得出结论,它不会产生任何声音(对要使用的分析仪记录使用条件的模数)。 例如:

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

实际上,这似乎是E1 <<(E2%32)

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

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

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

对于n位,仅当值x> 0x <= n-1时才可以进行数据移位,其中x是无位移位。

在这种情况下,无符号的内存大小等于32位,因此仅可能的移位范围是1到31。您正在尝试将数据移位到超出该变量的存储大小的位置,这就是为什么会给您带来错误。

比结果类型中可表示的最大值多一个模。...

表示E1 * 2^E2的值对unsigned int mod (UINT_MAX+1)降为mod (UINT_MAX+1) 这与您关于E2的假设完全无关。

无论如何,对于C11标准,它不是未定义的,

您忘记阅读引用的段落之前的内容:

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

所有32或更多的移位都会导致不确定的行为。 不需要编译器对此发出警告,但是在某些情况下,这对您很高兴。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM