是1<<当sizeof(int)== 4时,在C中定义良好

时间:2017-07-23 18:21:41

标签: c language-lawyer undefined-behavior bit-shift

根据this questions的回答:

  

E1<<< E2 E1 左移 E2 位位置;腾出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1×2 E2 ,减去的模数比可表示的最大值减1。结果类型。如果 E1 具有有符号类型和非负值,并且 E1×2 E2 在结果类型中可表示,那么这就是结果值;否则,行为是未定的。

这似乎意味着1 << 31未定义。

但是,如果我使用1 << 31,GCC不会发出警告。 它会为1 << 32发出一个问题。 link

那是哪个?我误解了标准吗? 海湾合作委员会有自己的解释吗?

3 个答案:

答案 0 :(得分:22)

否:如果类型1 << 31只有31个值位,则int具有未定义的行为。

1U << 31没问题,如果类型0x80000000有32个值位,则评估为unsigned int

在字节有8位的系统上,sizeof(int) == 4表示int最多有31个值位,因此将1乘以31位是未定义的。相反,在CHAR_BIT > 8的系统上,可以编写1 << 31

如果提高警告级别,

gcc可能会发出警告。试试gcc -Wall -Wextra -W -Werrorclang会发出相同选项的警告。

为解决MichaëlRoy的评论,1 << 31 }可靠地评估INT_MIN。它可能会在您的系统上提供此值,但标准并不保证它,实际上标准将此描述为未定义的行为,因此您不仅可以不依赖它,还应避免它以避免虚假错误。优化器通常利用潜在的未定义行为来删除代码并破坏程序员的假设。

例如,以下代码可能编译为简单的return 1;

int check_shift(int i) {
   if ((1 << i) > 0)
       return 1;
   else
       return 0;
}

Godbolt's compiler explorer支持的编译器都没有,但这样做不会破坏符合性。

答案 1 :(得分:17)

GCC没有对此发出警告的原因是因为1 << 31 在C90中有效(但是实现定义),并且 有效(但是实现) -defined)即使在现代C ++中也是如此。 C90将<<定义为一个位移,然后说对于无符号类型,其结果是乘法的结果,但是对于有符号类型没有这样的东西,这隐含地使它有效并使其被一般措辞覆盖按位运算符具有已签名类型的实现定义方面。 C ++现在定义<<乘以相应的无符号类型,结果转换回签名类型,也是实现定义的。

C99和C11确实使此无效(说明行为未定义),但允许编译器接受它作为扩展名。为了与现有代码兼容,并在C和C ++前端之间共享代码,GCC继续这样做,但有一个例外:您可以使用-fsanitize=undefined来获取检测到的未定义行为,以便在运行时中止您的程序,以及这个处理1 << 31,但只有在编译为C99或C11时才会处理。

答案 2 :(得分:9)

它会调用未定义的行为,正如其他答案/评论所解释的那样。但是,为什么GCC不会发出诊断信息。

实际上有两件事可能导致左移的未定义行为(均来自 [6.5.7] ):

  1.   

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

  2.   

    如果E1具有带符号类型和非负值,并且E1×2 E2 在结果类型中可表示,那么这就是结果值;否则,行为未定义。

  3. 显然GCC会检测到第一个(因为这样做很简单),但不是后者。