无符号长整型和位移操作

8

我在位移和无符号长整型上遇到了问题。这是我的测试代码:

char header[4];
header[0] = 0x80;
header[1] = 0x00;
header[2] = 0x00;
header[3] = 0x00;

unsigned long l1 = 0x80000000UL;
unsigned long l2 = ((unsigned long) header[0] << 24) + ((unsigned long) header[1] << 16) + ((unsigned long) header[2] << 8) + (unsigned long) header[3];

cout << l1 << endl;
cout << l2 << endl;

我原本期望l2的值也是2147483648,但实际上它输出的是18446744071562067968。我猜测第一个字节的位移导致了问题?

希望有人能解释为什么出现这个错误,以及如何修改l2的计算方式以返回正确的值。

提前感谢。


无法复现。操作系统、CPU、编译器?顺便说一句,您不必将 header[x] 强制转换为 unsigned long。 - Cat Plus Plus
1
如果你需要这样做,你应该使用C++转换。 - Lightness Races in Orbit
@Marcello 请尝试打印 sizeof(unsigned long) 的结果。 - Etienne de Martel
@Cat Plus Plus:为什么不需要强制转换?我以为移位操作数的结果类型是左操作数的类型。如果您不进行强制转换,那么它将是char类型(当然最终结果将是unsigned long)。或者我错过了什么? - flolo
@Etienne sizeof(unsigned long) == 8,所以像您下面提到的那样是64位。 - Marcello
显示剩余3条评论
2个回答

5

当你在char中存储0x80时,它是带符号的。当将其转换为更宽的类型时,该值被符号扩展以保持与较大类型相同的值。

将第一行中的char类型改为unsigned char,就不会发生符号扩展。

为了简化你的情况,请运行以下内容:

char c = 0x80
unsigned long l = c
cout << l << endl;

您会得到这样的输出:
18446744073709551488

这是一个关于IT技术的翻译内容:在64位整数中,-128用0x80表示,而在8位整数中,0x80表示-128。


进行左移时不会发生符号扩展 - 只有进行右移时才会发生。当将char类型转换为unsigned long类型时发生。 - flolo

2

在这里得到了同样的结果(Linux/x86-64,GCC 4.4.5)。行为取决于unsigned long的大小,它至少为32位,但可能更大。

如果你想要精确的32位,请使用uint32_t代替(来自头文件<stdint.h>;在C++03中不支持,在即将发布的标准中广泛支持)。


是的,看起来他的实现使用了64位的“unsigned long”。 - Etienne de Martel
确实 uint32_t 可以工作。但是为什么 unsigned long 失败了呢?我没有超过 32 位,对吧? - Marcello
@Marcello - 请尝试使用 std::cout << sizeof(unsigned long) << std::endl; - Steve Townsend
1
我认为这总结了问题。成为专业程序员的第一步是放弃“业余爱好者类型”:char,int,unsigned long等,并使用uintxx_t。如果因为使用C90或C ++而没有可用的stdint.h,则需自己创建带有typedefs的头文件。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接