左移溢出

时间:2015-10-01 22:53:32

标签: c

我有一个简单的程序,我有一些用于位操作的宏。

其中一个宏转换为以下

unsigned long val = 1 << 0x1f;

在这里,我得到输出为

val = 0xffffffff80000000;

我知道它是某种int溢出。我对最终结果的部分感到困惑。为什么结果出来是我得到的?

(我确实理解这里因为某种原因,1被视为int,而当左移使得它为负int。但我对于类型转换感到困惑。)

1 个答案:

答案 0 :(得分:6)

1签名的整数,因此在溢出的情况下,它是未定义的行为。

根据标准(强调我的):

  

E1 << E2的结果是E1左移E2位位置;腾出的位充满了   零。如果E1具有无符号类型,则结果的值为E1 × 2E2,减少模数   比结果类型中可表示的最大值多一个。如果E1已签名   类型和非负值, E1 × 2E2可在结果类型中表示,那就是   结果价值; 否则,行为未定义

相反,请尝试unsigned long1ul

unsigned long val = 1ul << 0x1f
  

(我确实理解这里因为某种原因,1被视为int,当左移使得它成为负数。但是我对于类型转换感到困惑。)

如果符号位设置为1,实际上可能会发生这种情况。(但是,再一次,根据标准, UB 。)

然而,让我们考虑一个法律案例(我使用shortint,因为intlong在我的系统上具有相同的大小):< / p>

                        // on my system
short int s = SHRT_MIN; // 0x8000
unsigned int i = s;     // 0xffff8000

标准的以下部分澄清了它:

  

6.3.1.3有符号和无符号整数

     

当具有整数类型的值转换为除_Bool以外的另一个整数类型时,if   该值可以用新类型表示,不变。

     

否则,如果新类型是无符号的,则通过重复添加或转换该值   减去一个可以在新类型中表示的最大值   直到该值在新类型的范围内

(如果我解释正确),因为我们无法在无符号类型中表示负值,我们将UINT_MAX + 1添加到SHRT_MIN(用纯数学术语,不考虑溢出):

UINT_MAX + 1 + SHRT_MIN // 0xffff8000