将6字节数组复制到长整型变量

时间:2016-02-24 08:52:53

标签: c long-integer memcpy

我从内存中读取了一个6字节unsigned char数组。 这里的结尾是Big Endian。 现在我想将存储在数组中的值赋给整数变量。我假设这必须是long long,因为它必须包含最多6个字节。

目前我正以这种方式分配:

unsigned char aFoo[6];
long long nBar;
// read values to aFoo[]...
// aFoo[0]: 0x00
// aFoo[1]: 0x00
// aFoo[2]: 0x00
// aFoo[3]: 0x00
// aFoo[4]: 0x26
// aFoo[5]: 0x8e
nBar = (aFoo[0] << 64) + (aFoo[1] << 32) +(aFoo[2] << 24) + (aFoo[3] << 16) + (aFoo[4] << 8) + (aFoo[5]);

memcpy方法很简洁,但是当我这样做时

memcpy(&nBar, &aFoo, 6);

6个字节从一开始就被复制到long long,因此在结尾处有填充零。 有没有比我的转移更好的方法?

3 个答案:

答案 0 :(得分:2)

您想要实现的目标称为反序列化或解组编码。

对于宽的值,使用循环是一个好主意,除非你真的需要最大值。速度和你的编译器没有矢量化循环:

uint8_t array[6];
...
uint64_t value = 0;

uint8_t *p = array;
for ( int i = (sizeof(array) - 1) * 8 ; i >= 0 ; i -= 8 )
    value |= (uint64_t)*p++ << i;

// left-align    值&lt;&lt; =&lt; = 64 - (sizeof(array)* 8);

请注意使用stdint.h类型和sizeof(uint8_t) cannot differ from 1`。只有这些保证具有预期的位宽。在移动值时也使用无符号整数。右移某些值是实现定义的,而左移调用未定义的行为。

如果 f 您需要签名值,只需

int64_t final_value = (int64_t)value;
转移后

。这仍然是实现定义,但所有现代实现(可能是较旧的实现)只是复制值而不进行修改。现代编译器可能会对此进行优化,因此不会受到惩罚。

当然,可以移动声明。我只是把它们放在用于完整性的地方之前。

答案 1 :(得分:-2)

您可以尝试

nBar = 0;
memcpy((unsigned char*)&nBar + 2, aFoo, 6);

在数组名称之前不需要&,因为它已经是地址。

答案 2 :(得分:-4)

执行所需操作的正确方法是使用union

#include <stdio.h>

typedef union {
    struct {
      char padding[2];
      char aFoo[6];
    } chars;
    long long nBar;
} Combined;

int main ()
{
  Combined x;

  // reset the content of "x"
  x.nBar = 0;           // or memset(&x, 0, sizeof(x));

  // put values directly in x.chars.aFoo[]...
  x.chars.aFoo[0] = 0x00;
  x.chars.aFoo[1] = 0x00;
  x.chars.aFoo[2] = 0x00;
  x.chars.aFoo[3] = 0x00;
  x.chars.aFoo[4] = 0x26;
  x.chars.aFoo[5] = 0x8e;

  printf("nBar: %llx\n", x.nBar);

  return 0;
}

优势:代码更清晰,无需与位,移位,掩码等进行操作。

但是,您必须注意,出于速度优化和硬件原因,编译器可能会将填充字节压缩到struct,从而导致aFoo不共享{{1}的所需字节}。这个小缺点可以通过告诉计算机将nBar的成员对齐到字节边界来解决(与默认情况相反,这是字边界处的对齐,字是32位或64位,取决于硬件架构。)

以前这是使用union指令实现的,其确切的语法取决于您使用的编译器。

从C11 / C ++ 11开始,alignas() specifier成为指定struct / union成员对齐的标准方法(假设编译器已经支持它)。