c从缓冲区读取4字节小端数的代码

时间:2015-05-22 16:13:40

标签: c bitwise-operators endianness

我遇到了这段存在的C代码。我很难理解它。

我非常希望将缓冲区中传递的4字节无符号值(小端格式)读入类型为" long"的变量。

此代码在64位字大小的小端x86机器上运行 - 其中sizeof(long)为8个字节。 我的猜测是这个代码也打算在32位x86机器上运行 - 因此为了存储来自四字节输入数据的值,使用long类型的变量而不是int。

我有一些疑问,并在代码中添加了评论以表达我理解的内容,或者我不知道的内容: - )

请在该背景下回答以下问题

void read_Value_From_Four_Byte_Buff( char*input)
{
    /* use long so on 32 bit machine, can still accommodate 4 bytes */ 
    long intValueOfInput;  

    /* Bitwise and of input buffer's byte 0 with 0xFF gives MSB or LSB ?*/
    /* This code seems to assume that assignment will store in rightmost byte - is that true on a x86 machine ?*/
    intValueOfInput =  0xFF & input[0];

    /*left shift byte-1 eight times, bitwise "or" places in 2nd byte frm right*/
    intValueOfInput |= ((0xFF & input[1]) << 8);

    /* similar left shift in mult. of 8 and bitwise "or" for next two bytes */
    intValueOfInput |= ((0xFF & input[2]) << 16);
    intValueOfInput |= ((0xFF & input[3]) << 24);

}

我的问题

1)输入缓冲区应该在&#34; Little endian&#34;中。但是从代码看起来这里的假设是它读入字节0 = MSB,字节1,字节2,字节3 = LSB。我之所以这么认为是因为代码从字节0开始读取字节,后续字节(1以后)在左移后放置在目标变量中。这是怎么回事,或者我弄错了?

2)我觉得这是一种令人费解的做事方式 - 是否有更简单的替代方法将值从4字节缓冲区复制到长变量中?

3)假设&#34;此代码将在64位机器上运行&#34;会对我能够轻易做到这一点有什么影响吗?我的意思是让所有这些麻烦都与字大小无关(我认为它现在与字大小无关 - 虽然不确定)?

感谢你的启示:-)

3 个答案:

答案 0 :(得分:3)

  1. 你倒退了。当你离开时,你会进入更重要的位。所以(0xFF & input[3]) << 24)将字节3放入MSB。

  2. 这是在标准C中执行此操作的方法.POSIX具有从网络字节顺序转换为本机32位整数的函数ntohl(),因此这通常用于Unix / Linux应用程序

  3. 除非您使用unsigned long而不是long,否则在64位计算机上这不会完全相同。按照目前的写法,input[3]的最高位将被放入结果的符号位(假设是二进制补码机器),因此您可以获得负面结果。如果long是64位,则所有结果都是正数。

答案 1 :(得分:2)

从代码中,字节0是LSB,字节3是MSB。但是有一些错别字。这些行应该是

intValueOfInput |= ((0xFF & input[2]) << 16);
intValueOfInput |= ((0xFF & input[3]) << 24);

您可以通过删除0xFF但在参数类型中使用“unsigned char”类型来缩短代码。

要缩短代码,您可以执行以下操作:

long intValueOfInput = 0;
for (int i = 0, shift = 0; i < 4; i++, shift += 8)
    intValueOfInput |= ((unsigned char)input[i]) << shift;

答案 2 :(得分:2)

  1. 您正在使用的代码确实将输入缓冲区视为小端。看看它是如何占用缓冲区的第一个字节,只是将它分配给变量而不进行任何移位。如果第一个字节增加1,则结果的值增加1,因此它是最不重要的字节(LSB)。左移使一个字节更重要,而不是更少。左移8通常与乘以256相同。
  2. 除非您使用外部函数,或者对运行此代码的机器做出假设,或者调用未定义的行为,否则我认为您不会比这更简单。在大多数情况下,只需编写uint32_t x = *(uint32_t *)input;即可,但这假设您的机器是小端,我认为根据C标准,它可能是未定义的行为。
  3. 不,在64位计算机上运行不是问题。我建议使用uint32_tint32_t等类型,以便更轻松地推断您的代码是否适用于不同的体系结构。您只需要包含C99的stdint.h标头即可使用这些类型。
  4. 此函数最后一行的右侧可能会显示undefined behavior,具体取决于输入中的数据:

    ((0xFF & input[3]) << 24)
    

    问题是(0xFF & input[3])将是签名的int(因为整数提升)。 int可能是32位,并且您将其向左移动到左侧,结果值可能无法在int中表示。 C standard表示这是未定义的行为,您应该尽量避免这种行为,因为它为编译器提供了执行任何操作的许可,您将无法预测结果。

    解决方案是在使用强制转换之前将其从int转换为uint32_t

    最后,变量intValueOfInput被写入但从未使用过。你不应该把它归还或存放在某个地方吗?

    考虑到所有这些,我会改写这样的函数:

    uint32_t read_value_from_four_byte_buff(char * input)
    {
        uint32_t x;
        x = 0xFF & input[0];
        x |= (0xFF & input[1]) << 8;
        x |= (0xFF & input[2]) << 16;
        x |= (uint32_t)(0xFF & input[3]) << 24;
        return x;
    }