将字节数组复制到c结构时出现问题

将字节数组复制到c结构时出现问题,c,endianness,C,Endianness,我知道这个问题以前可能已经得到了回答,但我仍然无法找到一个解决方案来解决我认为是一个持久性问题。我在下面构建了一个快速示例,演示了我的测试代码 在这个例子中,我有一个简单的字节数组。实际上,这个字节数组是通过CAN收集的一个较大的数据集,但是为了解决这个问题,我使用了一个较小的硬编码数组 目标 在c语言中,我的目标是将字节数组复制到一个结构中,保持数组的顺序(如果有意义的话)。比如说 数据集包含: {0x12, 0x34, 0x56, 0x78, 0x0A, 0x06, 0x77} 结构定义

我知道这个问题以前可能已经得到了回答,但我仍然无法找到一个解决方案来解决我认为是一个持久性问题。我在下面构建了一个快速示例,演示了我的测试代码

在这个例子中,我有一个简单的字节数组。实际上,这个字节数组是通过CAN收集的一个较大的数据集,但是为了解决这个问题,我使用了一个较小的硬编码数组

目标 在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中。如上所述,我使用了一个小数组进行测试,但实际的数组相当大,因此手动分配结构成员对我来说不是一个选项


我知道这不是问题所在,因为它不关心endianness,而实际的问题是我对数据应该如何对齐的假设。就主机而言,这是在windows系统上运行的,我相信这是little endian。

由于不完全了解您的问题,我删除了我的原始答案。我在阅读了以下关于的文章后现在明白了:

首先是对齐问题:

如500所述-内部服务器错误

您在处理数据时会遇到问题,因为您的结构将包含填充。在您的示例中,将向结构中添加1个字节

下面是一个在32位C实现上从VS获得的内存布局示例

size = 8
Address of test0        = 5504200
Padding added here at address 5504201
Address of test1        = 5504202
Address of test2        = 5504204
要指定编译器应使用的对齐规则,请使用预处理器指令

使用您的示例,结构定义如下所示:

#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
第二个endian问题:

这里的问题是整数如何存储在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;
}
Endianness链接到CPU,而不是操作系统。但由于Windows仅在x86上运行,并且x86是little-endian,所以Windows是little-endian(嗯,他们似乎也有ARM版本,但大多数ARM也是little-endian)

由于您的数据是big-endian,因此必须将其转换为处理器endian。但是big-endian也是标准的网络字节顺序,因此您可以依靠
ntoh*()
函数来完成这项工作。不幸的是,这意味着您将不得不为每个字段手动执行

如果您的CPU是大端的,您可以使用
#pragma pack(1)
memcpy()
对结构进行打包(或投射指针)

如果数组中的字节顺序与处理器匹配,并且结构中有许多变量,则可以使用_packed属性和memcpy()


如果要使用
memcpy
,您会遇到两个问题:endianness是一个问题,因为您怀疑必须在Windows上将字节顺序替换为多字节值。另一个问题是对齐/填充:默认情况下,上面的结构将有填充字节来对齐自然边界上的每个字段(偏移量可被其大小整除)。翻转结构成员的顺序,不要让编译器填充,然后将字节从最后复制到开始。这是一种选择。由于前面提到的问题,您最好诚实地编写代码来反序列化字节数组数据,并逐个分配给结构字段,同时根据需要考虑endianness。最好使用
#pragma pack(push,1)
/
#pragma(pop)
(如果您的编译器上提供的话)保留以前可能设置的包值。感谢Matthieu的编辑和其他包信息。我已经在答案中包含了您的评论。另外,请注意,如果需要,您有函数
ntoh*()
,可以为您执行字节重新排序。谢谢你们两位。这个解释非常有用!
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
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];
typedef __packed struct {
    uint8_t  test0;
    uint16_t test1;
    uint32_t test2;
} Foo_t;

Foo_t fs;
memcpy(&fs, data, sizeof(fs));