我的代码中有一个问题,在C语言中按位移位,我将其归结为以下示例:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
int main(int argc, char **argv)
{
char a = 1;
int i=0;
uint64_t b;
for(i=0; i<64; i++)
{
b=0;
printf("i=%d\n", i);
b = (a<< ((uint64_t) i));
printf("%" PRIu64 "\n", b);
}
return 0;
}
由于这个MWE中不明显的原因,我想从中产生2到2^63的能量它失败是因为发生了一些奇怪的事情,因为
a
不是char
明显的解决办法是b = ((uint64_t)a<< ((uint64_t) i))
为了理解到底发生了什么,我在上面写了一个MWE示例,从中我得到了输出(部分显示):
i=30
1073741824
i=31
18446744071562067968
i=32
1
i=33
2
现在我想知道如何解释从
a
跳到uint64_t
的过程怎么会在i=30
时再次成为1呢?如果有兴趣的话,我使用gcc(
i=31
)编译了上面的代码 最佳答案
很多答案已经指出你的代码有未定义的行为因此,对所发生的事情进行推理(因为任何事情都可能发生……)通常是没有意义的。
但是。。。。有时候不管怎样做都是很有趣的-记住这是纯粹的猜测,不能保证是正确的和高度依赖于系统的。。。。
所有的免责声明都准备好了。。。
奇数18446744071562067968从哪里来?
让我们假设32位int
,并注意到由于整数提升,您的char a = 1;
也可以int a = 1;
所以我们可以写:
int a = 1;
int as = a << 31; // Undefined behavior here as 1*2^31 can't be stored in 32 bit int
uint64_t b = as;
printf("%" PRIu64 "\n", b);
输出:
18446744071562067968
六羟甲基三聚氰胺六甲醚。。。。我们有神秘的18446744071562067968但是为什么?
再打印一次:
int a = 1;
int as = a << 31; // Undefined behavior here
uint64_t b = as;
printf("%d\n", as);
printf("%" PRIu64 "\n", b);
输出
-2147483648
18446744071562067968
所以
as
是阴性的所以我们真的做到了:uint64_t b = -2147483648;
因为b是无符号的,所以上面的计算如下:
uint64_t b = UINT64_MAX + 1 - 2147483648; // which is 18446744071562067968
现在我们知道18446744071562067968是从哪里来的毕竟没那么神秘。
但这留下了另一个问题-为什么
as
是阴性的?六羟甲基三聚氰胺六甲醚。。。。再打印一次:
int a = 1;
int as = a << 31; // Undefined behavior here
uint64_t b = as;
printf("%d\n", as);
printf("%x\n", as); // Print as in hex
printf("%" PRIu64 "\n", b);
输出:
-2147483648
80000000
18446744071562067968
所以在hex中
as
是80000000
实际上是a1
左移31次所以处理器只是做了我们要求的,也就是说,1 << 31
它在C标准中没有定义,但是您/我的处理器就是这样实现的-其他机器可能会做其他事情。80000000
作为32位2的补码是-2147483648
,因此我们知道为什么as
是负的。