C语言 将字节数组转换为uint64时的错误

hsgswve4  于 2023-10-15  发布在  其他
关注(0)|答案(1)|浏览(166)

我试图找出一个错误,当转换一个字节数组到一个uint64_tunsigned 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是什么?**

8yparm6h

8yparm6h1#

在此子表达式中:

byte[4]<<24

你从一个unsigned char值开始。这个值首先被 * 提升 * 为类型intC standard的第6.3.1.1p2节详细介绍了这种行为,其中涉及算术转换:
在表达式中可以使用intunsigned int

  • 具有整数类型(int或unsigned int除外)的对象或表达式,其整数转换秩小于或等于int和unsigned int的秩。
  • 类型为_Boolintsigned intunsigned 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来纠正此错误,以便移位有效。

x=x+((unsigned long long)byte[4]<<24)+(byte[5]<<16)+(byte[6]<<8)+byte[7];

相关问题