将字节数组复制到c结构时出现问题
Problem when copying byte array into c structure
我知道以前可能已经回答过这个问题,但我仍然无法解决我认为是字节序问题的解决方案。我在下面构建了一个快速示例来演示我的测试代码。
https://onlinegdb.com/SJtEatMvS
在这个例子中,我有一个简单的字节数组。实际上,这个字节数组是通过 CAN 收集的一个更大的数据集,但为了这个问题,我使用了一个更小的硬编码数组。
Objective
我的目标是在 c 语言中将字节数组复制到一个结构中,同时保留数组的顺序(如果这有意义的话)。例如
数据集包含:
{0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77}
结构定义为
typedef struct {
uint8_t test0;
uint16_t test1;
uint32_t test2;
} Foo_t;
我想将 0x12 复制到 test0,将 {0x3456} 复制到 test1,并将 {0x780A0677} 复制到 test2。如上所述,我使用了一个小数组进行测试,但实际数组非常大,因此手动分配结构成员不是我的选择。
我知道 memcpy 不是问题,因为它不关心字节顺序,实际问题是我对数据应该如何对齐的假设。就主机而言,这是 windows 系统上的 运行,我认为这是小端。
由于不完全理解你的问题,我删除了我原来的答案。在阅读以下文章后,我现在明白了:Writing endian-independent code in C
首先是对齐问题:
如 500 所述 - 内部服务器错误
您在处理数据时会遇到问题,因为您的结构将包含填充。在您的示例中,将向结构添加 1 个字节。
这是从 VS 获得的 32 位 C 实现的内存布局示例。
size = 8
Address of test0 = 5504200
Padding added here at address 5504201
Address of test1 = 5504202
Address of test2 = 5504204
要指定编译器应使用的对齐规则,请使用预处理器指令 pack。
// Aligns on byte boundaries, then restore alignment value to system defaults
#pragma pack ( 1 )
#pragma pack ()
// Aligns on byte boundaries, restores previously assigned alignment value.
#pragma pack ( push, 1 )
#pragma pack (pop)
使用您的示例,结构定义将如下所示:
#pragma pack ( 1 )
typedef struct {
unsigned char test0;
unsigned short test1;
unsigned int test2;
} Foo_t;
#pragma pack ()
Foo_t s2;
printf("\nsize = %d\n", sizeof(Foo_t));
printf(" Address of test0 = %u\n", &s2.test0);
printf(" Address of test1 = %u\n", &s2.test1);
printf(" Address of test2 = %u\n", &s2.test2);
结果:
size = 7
Address of test0 = 10287904
Address of test1 = 10287905
Address of test2 = 10287907
二字节序问题:
这里的问题是在 32 位 x86 机器上如何将整数存储在内存中。在 x86 机器上,它们以小端顺序存储。
例如,将包含字节 x34 和 x56 的 2 字节数组复制到一个短整数中,将存储为 x56(低位字节)x34(下一个字节)。这不是你想要的。
要解决此问题,您需要按照其他建议切换字节。我对此的看法是创建一个可以就地进行字节交换的函数。
示例:
int main()
{
#pragma pack ( 1 )
typedef struct {
unsigned char test0;
unsigned short test1;
unsigned int test2;
} Foo_t;
#pragma pack ()
unsigned char tempBuf[7] = { 0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77 };
Foo_t foo;
memcpy(&foo, &tempBuf[0], 7);
//foo.test0 = netToHost (&foo,0,1); // not needed
foo.test1 = reverseByteOrder(&foo, 1, 2);
foo.test2 = reverseByteOrder(&foo, 3, 4);
printf("\n After memcpy We have %02X %04X %08X\n", foo.test0, foo.test1, foo.test2);
}
int reverseByteOrder(char array[], int startIndex, int size)
{
int intNumber =0;
for (int i = 0; i < size; i++)
intNumber = (intNumber << 8) | array[startIndex + i];
return intNumber;
}
输出为:
After memcpy We have 12 3456 780A0677
字节序链接到 CPU,而不是 OS。但是由于 Windows 仅在 x86 上是 运行 并且 x86 是小端,Windows 是小端(好吧,他们似乎也有 ARM 版本,但大多数 ARM 是小端以及)。
由于您的数据是大端数据,因此您必须将其转换为您的处理器字节序。但是 big-endian 也是标准的网络字节顺序,因此您可以依靠 ntoh*()
函数来为您做到这一点。不幸的是,这意味着您必须为每个字段手动执行此操作...
如果你的 CPU 是大字节序的,你可以用 #pragma pack(1)
和 memcpy()
打包你的结构(或转换指针)。
uint8_t data[] = {0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77};
typedef struct {
uint8_t test0;
uint16_t test1;
uint32_t test2;
} Foo_t;
Foo_t fs;
fs.test0 = data[0];
fs.test1 = data[1]<<8 + data[2];
fs.test2 = data[3]<<24 + data[4]<<16 + data[5]<<8 + data[6];
如果你的处理器是大端,你可以稍微作弊..
fs.test0 = data[0];
fs.test1 = *(uint16_t*)&data[1];
fs.text2 = *(uint32_t*)&data[3];
如果您的数组中的字节顺序与您的处理器匹配,并且您的结构中有许多变量,您可以使用 __packed 属性和 memcpy()。
typedef __packed struct {
uint8_t test0;
uint16_t test1;
uint32_t test2;
} Foo_t;
Foo_t fs;
memcpy(&fs, data, sizeof(fs));
我知道以前可能已经回答过这个问题,但我仍然无法解决我认为是字节序问题的解决方案。我在下面构建了一个快速示例来演示我的测试代码。
https://onlinegdb.com/SJtEatMvS
在这个例子中,我有一个简单的字节数组。实际上,这个字节数组是通过 CAN 收集的一个更大的数据集,但为了这个问题,我使用了一个更小的硬编码数组。
Objective
我的目标是在 c 语言中将字节数组复制到一个结构中,同时保留数组的顺序(如果这有意义的话)。例如
数据集包含:
{0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77}
结构定义为
typedef struct {
uint8_t test0;
uint16_t test1;
uint32_t test2;
} Foo_t;
我想将 0x12 复制到 test0,将 {0x3456} 复制到 test1,并将 {0x780A0677} 复制到 test2。如上所述,我使用了一个小数组进行测试,但实际数组非常大,因此手动分配结构成员不是我的选择。
我知道 memcpy 不是问题,因为它不关心字节顺序,实际问题是我对数据应该如何对齐的假设。就主机而言,这是 windows 系统上的 运行,我认为这是小端。
由于不完全理解你的问题,我删除了我原来的答案。在阅读以下文章后,我现在明白了:Writing endian-independent code in C
首先是对齐问题:
如 500 所述 - 内部服务器错误
您在处理数据时会遇到问题,因为您的结构将包含填充。在您的示例中,将向结构添加 1 个字节。
这是从 VS 获得的 32 位 C 实现的内存布局示例。
size = 8
Address of test0 = 5504200
Padding added here at address 5504201
Address of test1 = 5504202
Address of test2 = 5504204
要指定编译器应使用的对齐规则,请使用预处理器指令 pack。
// Aligns on byte boundaries, then restore alignment value to system defaults
#pragma pack ( 1 )
#pragma pack ()
// Aligns on byte boundaries, restores previously assigned alignment value.
#pragma pack ( push, 1 )
#pragma pack (pop)
使用您的示例,结构定义将如下所示:
#pragma pack ( 1 )
typedef struct {
unsigned char test0;
unsigned short test1;
unsigned int test2;
} Foo_t;
#pragma pack ()
Foo_t s2;
printf("\nsize = %d\n", sizeof(Foo_t));
printf(" Address of test0 = %u\n", &s2.test0);
printf(" Address of test1 = %u\n", &s2.test1);
printf(" Address of test2 = %u\n", &s2.test2);
结果:
size = 7
Address of test0 = 10287904
Address of test1 = 10287905
Address of test2 = 10287907
二字节序问题:
这里的问题是在 32 位 x86 机器上如何将整数存储在内存中。在 x86 机器上,它们以小端顺序存储。
例如,将包含字节 x34 和 x56 的 2 字节数组复制到一个短整数中,将存储为 x56(低位字节)x34(下一个字节)。这不是你想要的。
要解决此问题,您需要按照其他建议切换字节。我对此的看法是创建一个可以就地进行字节交换的函数。
示例:
int main()
{
#pragma pack ( 1 )
typedef struct {
unsigned char test0;
unsigned short test1;
unsigned int test2;
} Foo_t;
#pragma pack ()
unsigned char tempBuf[7] = { 0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77 };
Foo_t foo;
memcpy(&foo, &tempBuf[0], 7);
//foo.test0 = netToHost (&foo,0,1); // not needed
foo.test1 = reverseByteOrder(&foo, 1, 2);
foo.test2 = reverseByteOrder(&foo, 3, 4);
printf("\n After memcpy We have %02X %04X %08X\n", foo.test0, foo.test1, foo.test2);
}
int reverseByteOrder(char array[], int startIndex, int size)
{
int intNumber =0;
for (int i = 0; i < size; i++)
intNumber = (intNumber << 8) | array[startIndex + i];
return intNumber;
}
输出为:
After memcpy We have 12 3456 780A0677
字节序链接到 CPU,而不是 OS。但是由于 Windows 仅在 x86 上是 运行 并且 x86 是小端,Windows 是小端(好吧,他们似乎也有 ARM 版本,但大多数 ARM 是小端以及)。
由于您的数据是大端数据,因此您必须将其转换为您的处理器字节序。但是 big-endian 也是标准的网络字节顺序,因此您可以依靠 ntoh*()
函数来为您做到这一点。不幸的是,这意味着您必须为每个字段手动执行此操作...
如果你的 CPU 是大字节序的,你可以用 #pragma pack(1)
和 memcpy()
打包你的结构(或转换指针)。
uint8_t data[] = {0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77};
typedef struct {
uint8_t test0;
uint16_t test1;
uint32_t test2;
} Foo_t;
Foo_t fs;
fs.test0 = data[0];
fs.test1 = data[1]<<8 + data[2];
fs.test2 = data[3]<<24 + data[4]<<16 + data[5]<<8 + data[6];
如果你的处理器是大端,你可以稍微作弊..
fs.test0 = data[0];
fs.test1 = *(uint16_t*)&data[1];
fs.text2 = *(uint32_t*)&data[3];
如果您的数组中的字节顺序与您的处理器匹配,并且您的结构中有许多变量,您可以使用 __packed 属性和 memcpy()。
typedef __packed struct {
uint8_t test0;
uint16_t test1;
uint32_t test2;
} Foo_t;
Foo_t fs;
memcpy(&fs, data, sizeof(fs));