将字节数组转换为 uint64 时出现错误

enr*_*856 2 c serialization deserialization

我试图找出将字节数组转换为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);
}
Run Code Online (Sandbox Code Playgroud)

结果是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;
Run Code Online (Sandbox Code Playgroud)

……结果是错误的。

我得到:
159253 21 ec339912。

但是,它应该是:
159253 22 ec339912。

为什么?有什么错误?

dbu*_*ush 7

在这个子表达式中:

\n
byte[4]<<24\n
Run Code Online (Sandbox Code Playgroud)\n

你从一个值开始unsigned char。该值首先被提升为type int有关算术转换的C 标准第 6.3.1.1p2 节详细介绍了此行为:

\n
\n

int只要可以使用or的地方,就可以在表达式中使用以下内容unsigned int

\n
    \n
  • 具有整数类型(除 int 或 unsigned int 之外)的对象或表达式,其整数转换等级小于或等于\nint 和 unsigned int 的等级。
  • \n
  • _Bool类型为、intsigned int或 的位字段unsigned int
  • \n
\n

如果 anint可以表示原始类型的所有值(对于位字段而言,\n受宽度限制),则该值将转换为\nan int;否则,它会被转换为unsigned int. 这些称为整数提升。所有其他类型都不会因整数提升而改变。

\n
\n

因此,提升后的值byte[4]具有int带符号类型且(很可能)32 位。然后将此int值左移 24。假设原始值为0xec,这会导致值 1 被移入结果的符号位int

\n

将 1 移入符号位会导致未定义的行为C 标准第 6.5.7p4 节中有关按位移位运算符的说明对此进行了详细说明:

\n
\n

结果E1 << E2是E1左移E2位位置;空出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1 \xc3\x97 2 E2,比结果类型中可表示的最大值多减模 1。如果 E1 具有带符号类型和非负值,并且 E1 \xc3\x97 2 E2可在结果类型中表示,则这就是结果值;否则,行为是未定义的。

\n
\n

您可以通过首先将值转换为unsigned long long以使移位有效来纠正此问题。

\n
x=x+((unsigned long long)byte[4]<<24)+(byte[5]<<16)+(byte[6]<<8)+byte[7];\n
Run Code Online (Sandbox Code Playgroud)\n