C中的移位和undefined behavior

前一阵写一个程序,需要malloc 2G的buffer,于是第一次就这么写了

long size = 1 << 31;

结果malloc返回总是失败,把size打印出来发现是个负数。如果改成以下的写法就没问题了

long size = 1u << 31;

今天正好看到内部邮件列表里有人也在讨论这个问题,贴出了C99对移位操作的规定,也为我解决了这个疑问

ISO C99 6.5.7 Bitwise shift operators, paragraph 4:

 The result of E1 << E2 is E1 left-shifted E2 bit positions; 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. 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; otherwise, the behavior is
 undefined.

也就是说 1 << 31 这种表达式是个未定义行为,因为1是一个signed type,并且 1 x 2^31已经超出了signed int的表达范围,所以gcc中得到负数只是gcc的一个实现。而 1u << 31 能够得到正确的2G数值,是因为1u是一个unsigned type,1u x 2^31 的结果正好能在unsigned int里表示,再赋值给long之后也就没问题了。

3 thoughts on “C中的移位和undefined behavior

  1. XTao

    这个问题嘛,据我的理解是这样的:
    是类型转换问题,在类型转换的时候,默认进行了符号扩展。
    1 << 31 默认type是signed int(按照你的文章,应该是这个类型),size是long,C在赋值的时候,进行类型转换,由于是signed转signed,所以符号位扩展,保留那个负号。
    1u << 31 是unsigned int,unsigned int转singed long,没有符号位扩展,所以结果是正数。

    Reply
  2. Gery Post author

    我之前也是按照符号扩展/无符号扩展这么理解的,就是正好今天看到了C99里的规定,记录一下

    C programming Language需要常翻啊,都忘了……

    Reply

Leave a Reply