我试图找出一个错误,当转换一个字节数组到一个uint64_t
(unsigned long long
)。
我知道你可以轮班,但这不是我的问题。我想用乘法来做这个。我想知道为什么在某些情况下乘法不按计划进行。
这段代码的结果是正确的:
#include <stdio.h>
int main()
{
unsigned char byte[8];
unsigned long long x;
byte[0] = 0x15;
byte[1] = 0x15;
byte[2] = 0x17;
byte[3] = 0x18;
byte[4] = 0x19;
byte[5] = 0x20;
byte[6] = 0x21;
byte[7] = 0x12;
x = (byte[0]*0x100000000000000) +
(byte[1]*0x1000000000000) +
(byte[2]*0x10000000000) +
(byte[3]*0x100000000);
x = x +
(byte[4]<<24) +
(byte[5]<<16) +
(byte[6]<<8) +
byte[7];
printf("%llx\n", x);
}
结果是1515171819202112
,这是正确的。
但在这种情况下,我使用这些字节:
byte[0] = 0x15;
byte[1] = 0x92; // changed
byte[2] = 0x53; // changed
byte[3] = 0x22; // changed
byte[4] = 0xec; // changed
byte[5] = 0x33; // changed
byte[6] = 0x99; // changed
byte[7] = 0x12;
结果是错误的。
我得到:
15925321ec339912.
但是,它应该是:
15925322ec339912.
为什么?bug是什么?**
1条答案
按热度按时间8yparm6h1#
在此子表达式中:
你从一个
unsigned char
值开始。这个值首先被 * 提升 * 为类型int
。C standard的第6.3.1.1p2节详细介绍了这种行为,其中涉及算术转换:在表达式中可以使用
int
或unsigned int
:_Bool
、int
、signed int
或unsigned int
的位字段。如果
int
可以表示原始类型的所有值(对于位字段,受宽度限制),则该值将转换为int
;否则将转换为unsigned int
。这些被称为 integer promotions。所有其他类型都不受整数提升的影响。因此
byte[4]
的提升值具有类型int
,它是有符号的,并且(很可能)是32位的。然后将这个int
值左移24。假设原始值是0xec
,这会导致值1被移位到结果int
值的符号位。将1移位到符号位得到undefined behavior。这在C standard的第6.5.7p4节中关于按位移位运算符进行了详细说明:
E1 << E2
的结果是E1左移的E2比特位置;空出来的比特用零填充。如果E1是无符号类型,则结果的值是E1 × 2 E2,比结果类型中可表示的最大值大一模。如果E1有符号类型和非负值,并且E1 × 2 E2在结果类型中是可表示的,那么这就是结果值;否则,行为是未定义的。您可以通过首先将值强制转换为
unsigned long long
来纠正此错误,以便移位有效。