我有一个简单的程序,我有一些用于位操作的宏。
其中一个宏转换为以下
unsigned long val = 1 << 0x1f;
在这里,我得到输出为
val = 0xffffffff80000000;
我知道它是某种int溢出。我对最终结果的部分感到困惑。为什么结果出来是我得到的?
(我确实理解这里因为某种原因,1被视为int,而当左移使得它为负int。但我对于类型转换感到困惑。)
答案 0 :(得分:6)
1
是签名的整数,因此在溢出的情况下,它是未定义的行为。
根据标准(强调我的):
E1 << E2
的结果是E1
左移E2
位位置;腾出的位充满了 零。如果E1
具有无符号类型,则结果的值为E1 × 2E2
,减少模数 比结果类型中可表示的最大值多一个。如果E1
已签名 类型和非负值,E1 × 2E2
可在结果类型中表示,那就是 结果价值; 否则,行为未定义。
相反,请尝试unsigned long
值1ul
:
unsigned long val = 1ul << 0x1f
(我确实理解这里因为某种原因,1被视为
int
,当左移使得它成为负数。但是我对于类型转换感到困惑。)
如果符号位设置为1,实际上可能会发生这种情况。(但是,再一次,根据标准, UB 。)
然而,让我们考虑一个法律案例(我使用short
和int
,因为int
和long
在我的系统上具有相同的大小):< / 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