将 6 字节数组复制到 long long 整型变量
Copy 6 byte array to long long integer variable
我从内存中读取了一个 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
,因此在末尾有填充零。
有没有比我的换档作业更好的方法?
你可以试试
nBar = 0;
memcpy((unsigned char*)&nBar + 2, aFoo, 6);
数组名前不需要 &
因为它已经是一个地址。
完成所需操作的正确方法是使用 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;
}
优点:代码更清晰,无需在bits、shifts、masks等方面做杂耍
但是,您必须注意,出于速度优化和硬件原因,编译器可能会将填充字节挤入 struct
,导致 aFoo
无法共享 [=] 所需的字节14=]。这个小缺点可以通过告诉计算机在字节边界对齐 union
的成员来解决(而不是默认情况下是在字边界对齐,字是 32 位或 64 位,取决于硬件架构)。
这过去是使用 #pragma
指令实现的,其确切语法取决于您使用的编译器。
自 C11/C++11 起,alignas()
specifier 成为指定 struct/union 成员对齐方式的标准方式(假设您的编译器已经支持它)。
你想要完成的是反序列化或反编组。
对于那么宽的值,使用循环是个好主意,除非你真的需要最大值。速度和你的编译器不向量化循环:
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;
// 左对齐
值 <<= 64 - (sizeof(array) * 8);
注意使用 stdint.h
类型和 sizeof(uint8_t) cannot differ from
1`。只有这些才能保证具有预期的位宽。移动值时也使用无符号整数。右移某些值是实现定义的,而左移调用未定义的行为。
如果f你需要一个带符号的值,就
int64_t final_value = (int64_t)value;
移位后。这仍然是实现定义的,但所有现代实现(可能是旧的)只是复制值而不进行修改。现代编译器可能会对此进行优化,因此不会有任何损失。
当然可以移动声明。为了完整起见,我只是把它们放在它们使用的地方之前。
我从内存中读取了一个 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
,因此在末尾有填充零。
有没有比我的换档作业更好的方法?
你可以试试
nBar = 0;
memcpy((unsigned char*)&nBar + 2, aFoo, 6);
数组名前不需要 &
因为它已经是一个地址。
完成所需操作的正确方法是使用 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;
}
优点:代码更清晰,无需在bits、shifts、masks等方面做杂耍
但是,您必须注意,出于速度优化和硬件原因,编译器可能会将填充字节挤入 struct
,导致 aFoo
无法共享 [=] 所需的字节14=]。这个小缺点可以通过告诉计算机在字节边界对齐 union
的成员来解决(而不是默认情况下是在字边界对齐,字是 32 位或 64 位,取决于硬件架构)。
这过去是使用 #pragma
指令实现的,其确切语法取决于您使用的编译器。
自 C11/C++11 起,alignas()
specifier 成为指定 struct/union 成员对齐方式的标准方式(假设您的编译器已经支持它)。
你想要完成的是反序列化或反编组。
对于那么宽的值,使用循环是个好主意,除非你真的需要最大值。速度和你的编译器不向量化循环:
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;
// 左对齐 值 <<= 64 - (sizeof(array) * 8);
注意使用 stdint.h
类型和 sizeof(uint8_t) cannot differ from
1`。只有这些才能保证具有预期的位宽。移动值时也使用无符号整数。右移某些值是实现定义的,而左移调用未定义的行为。
如果f你需要一个带符号的值,就
int64_t final_value = (int64_t)value;
移位后。这仍然是实现定义的,但所有现代实现(可能是旧的)只是复制值而不进行修改。现代编译器可能会对此进行优化,因此不会有任何损失。
当然可以移动声明。为了完整起见,我只是把它们放在它们使用的地方之前。