简体   繁体   English

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

[英]Is 1 << 31 well defined in C when sizeof(int) == 4

According to the answer to this questions : 根据这个问题的答案:

The result of E1 << E2 is E1 left-shifted E2 bit positions; E1 << E2的结果是E1左移E2位位置; vacated bits are filled with zeros. 腾出的位用零填充。 If E1 has an unsigned type, the value of the result is E1 × 2 E2 , reduced modulo one more than the maximum value representable in the result type. 如果E1具有无符号类型,则结果的值为E1×2 E2 ,比结果类型中可表示的最大值减少一个模数。 If E1 has a signed type and nonnegative value, and E1 × 2 E2 is representable in the result type, then that is the resulting value; 如果E1具有带符号类型和非负值,并且E1×2 E2可在结果类型中表示,那么这就是结果值; otherwise, the behavior is undefined. 否则,行为未定。

Which seems to imply that 1 << 31 is undefined. 这似乎意味着1 << 31未定义。

However GCC doesn't issue a warning if I use 1 << 31 . 但是,如果我使用1 << 31 GCC不会发出警告。 It does issue one for 1 << 32 . 它确实为1 << 32发出一个。 link 链接

So which is it? 那是哪个呢? Am I misunderstanding the standard? 我误解了标准吗? Does GCC have its own interpretation? 海湾合作委员会有自己的解释吗?

No: 1 << 31 has undefined behavior if the type int has only 31 value bits. No: 1 << 31具有未定义的行为,如果int类型只有31个值位。

1U << 31 is OK and evaluates to 0x80000000 if type unsigned int has 32 value bits. 如果类型unsigned int具有32个值位,则1U << 31为OK并且计算结果为0x80000000

On a system where bytes have 8 bits, sizeof(int) == 4 means int has at most 31 value bits, so shifting 1 by 31 places is undefined. 在字节有8位的系统上, sizeof(int) == 4表示int最多有31个值位,因此将1乘以31位是未定义的。 Conversely, on a system where CHAR_BIT > 8 , it may be OK to write 1 << 31 . 相反,在CHAR_BIT > 8的系统上,写1 << 31

gcc might issue a warning if you raise the warning level. 如果提高警告级别, gcc可能会发出警告。 try gcc -Wall -Wextra -W -Werror . 尝试gcc -Wall -Wextra -W -Werror clang does issue a warning with the same options. clang确实发出了相同选项的警告。

To address Michaël Roy's comments, 1 << 31 does not evaluate to INT_MIN reliably. 为了解决MichaëlRoy的评论, 1 << 31 不能可靠地评估INT_MIN It might give this value on your system, but the Standard does not guarantee it, in fact the Standard describes this as undefined behavior, so not only can you not rely on it, you should avoid it to avoid spurious bugs. 它可能会在您的系统上提供此值,但标准并不保证它,实际上标准将此描述为未定义的行为,因此您不仅可以不依赖它,还应避免它以避免虚假错误。 The optimizers routinely take advantage of potential undefined behavior to remove code and break the programmers' assumptions. 优化器通常利用潜在的未定义行为来删除代码并破坏程序员的假设。

For example, the following code might compile to a simple return 1; 例如,以下代码可能编译为简单的return 1; :

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

None of the compilers supported by Godbolt's compiler explorer do, but doing so would not break conformity. Godbolt的编译器资源管理器所支持的编译器都没有,但这样做不会破坏整合。

The reason GCC doesn't warn about this is because 1 << 31 was valid (but implementation-defined) in C90, and is valid (but implementation-defined) even in modern C++. 究其原因GCC不发出警告,这是因为1 << 31 有效的(但定义实现的)在C90,并且有效(但实现定义),即使在现代C ++。 C90 defines << as a bit shift and followed by saying that for unsigned types, its result was that of a multiplication, but did no such thing for signed types, which implicitly made it valid and left it covered by the general wording that bitwise operators have implementation-defined aspects for signed types. C90将<<定义为一个位移,然后对无符号类型进行定义,其结果是乘法的结果,但是对于有符号类型没有这样的东西,它隐含地使它有效并使其被按位运算符的一般措辞所覆盖具有已签名类型的实现定义方面。 C++ nowadays defines << as multiplying to the corresponding unsigned type, with the result converted back to the signed type, which is implementation-defined as well. C ++现在将<<定义为与相应的无符号类型相乘,并将结果转换回有符号类型,该类型也是实现定义的。

C99 and C11 did make this invalid (saying the behaviour is undefined), but compilers are permitted to accept it as an extension. C99和C11确实使其无效(说明行为未定义),但允许编译器接受它作为扩展。 For compatibility with existing code, and to share code between the C and C++ frontends, GCC continues to do so, with one exception: you can use -fsanitize=undefined to get detected undefined behaviour to abort your program at run-time, and this one does handle 1 << 31 , but only when compiling as C99 or C11. 为了与现有代码兼容,并在C和C ++前端之间共享代码,GCC继续这样做,但有一个例外:您可以使用-fsanitize=undefined来获取检测到的未定义行为以在运行时中止您的程序,这样一个处理1 << 31 ,但仅在编译为C99或C11时。

It does invoke undefined behaviour, as explained by the other answers/comments. 它会调用未定义的行为,正如其他答案/评论所解释的那样。 However, as to why GCC doesn't emit a diagnostic. 但是,为什么GCC不发出诊断。

There are actually two things that can lead to undefined behaviour for a left-shift (both from [6.5.7] ): 实际上有两件事可能导致左移的未定义行为(均来自[6.5.7] ):

  1. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined. 如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

  2. If E1 has a signed type and nonnegative value, and E1 × 2 E2 is representable in the result type, then that is the resulting value; 如果E1具有带符号类型和非负值,并且E1×2 E2可在结果类型中表示,那么这就是结果值; otherwise, the behavior is undefined. 否则,行为未定义。

Evidently GCC detects the first one (because it's trivial to do so), but not the latter. 显然GCC检测到第一个(因为这样做很简单),但后者不是。

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

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