当sizeof(int)== 4时,1 << 31在C中定义得很好

根据这个问题的答案:

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

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

但是,如果我使用1 << 31 GCC不会发出警告。 它确实为1 << 32发出一个。 链接

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

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

如果类型unsigned int具有32个值位,则1U << 31为OK并且计算结果为0x80000000

在字节有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的编译器探测器所支持的编译器都没有,但这样做不会破坏整合。

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

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

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

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

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

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

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