结构成员对齐 - 使用16位和32位编译器的不同sizeof

Pin*_*GNU 3 c embedded gcc memory-alignment struct-member-alignment

我有一个结构用于构建控制板的消息我需要保持C167 16位Keil编译器和32位Tricore gcc编译器之间的软件兼容性.

typedef struct
{
    unsigned char new_weld_status[2];
    UINT32 new_weld_count;
    UINT16 new_weld_fail_count;
} NEW_PULSE_DATA;
Run Code Online (Sandbox Code Playgroud)

该数组new_weld_status[2]在16位编译器上占用2个字节,在32位编译器上占用4个字节.我正在考虑new_weld_status[2]用gcc编译时替换所有的联合.但是有一个我可以用于gcc的开关,使chars适合/对齐2个字节?

谢谢

Chu*_*ill 6

请注意,您的结构布局会在32位系统上产生问题.许多(大多数)32位CPU架构需要对32位字进行4字节对齐,因此new_weld_count需要"填充"以提供适当的内存对齐.

typedef struct
{
    unsigned char   new_weld_status[2];   //a
    //char padding_1[2]; //hidden padding
    UINT32  new_weld_count;               //a
    UINT16  new_weld_fail_count;          //a
} NEW_PULSE_DATA;
Run Code Online (Sandbox Code Playgroud)

以下对结构的重新定义完全避免了这个问题.

typedef struct
{
    UINT32  new_weld_count;               //a
    UINT16  new_weld_fail_count;          //a
    unsigned char   new_weld_status[2];   //a
} NEW_PULSE_DATA;
NEW_PULSE_DATA ex_PULSE_DATA;
Run Code Online (Sandbox Code Playgroud)

然而,上述方法通常不是通过网络/通过消息传输来传输结构(ured)数据的方法.一种更常见且更好的方法是使用序列化/反序列化层(也称为编组)将结构放置在"线上"格式中.您当前的方法是将内存存储和寻址与通信格式混为一谈.

//you need to decide on the size of wire format data,
//Both ends of the protocol must agree on these sizes,
#define new_weld_count_SZ sizeof(ex_PULSE_DATA.new_weld_count)
#define new_weld_fail_count_SZ sizeof(ex_PULSE_DATA.new_weld_fail_count)
#define new_weld_status_SZ sizeof(ex_PULSE_DATA.new_weld_status)

//Then you define a network/message format
typedef struct
{
    byte new_weld_count[new_weld_count_SZ];
    byte new_weld_fail_count[new_weld_count_SZ];
    byte new_weld_status[new_weld_count_SZ];
} MESSAGE_FORMAT_PULSE_DATA;
Run Code Online (Sandbox Code Playgroud)

然后,您将在传输的两端实现序列化和反序列化功能.下面的例子很简单,但传达了你需要的要点.

byte*
PULSE_DATA_serialize( MESSAGE_FORMAT_PULSE_DATA* msg, NEW_PULSE_DATA* data )
{
    memcpy(&(msg->new_weld_count), data->new_weld_count, new_weld_count_SZ);
    memcpy(&(msg->new_weld_fail_count), data->new_weld_fail_count, new_weld_fail_count_SZ);
    memcpy(&(msg->new_weld_status), data->new_weld_status, new_weld_status_SZ);
    return msg;
}

NEW_PULSE_DATA*
PULSE_DATA_deserialize( NEW_PULSE_DATA* data, MESSAGE_FORMAT_PULSE_DATA* msg )
{
    memcpy(data->new_weld_count, &(msg->new_weld_count), new_weld_count_SZ);
    memcpy(data->new_weld_fail_count, &(msg->new_weld_fail_count), new_weld_fail_count_SZ);
    memcpy(data->new_weld_status, &(msg->new_weld_status), new_weld_status_SZ);
    return msg;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我省略了必须的网络字节顺序转换,因为我假设您已经解决了两个cpu域之间的字节顺序问题.如果你没有考虑字节顺序(big-endian和little-endian),那么你也需要解决这个问题.

发送邮件时,发件人执行以下操作,

//you need this declared & assigned somewhere
NEW_PULSE_DATA data;
//You need space for your message
MESSAGE_FORMAT_PULSE_DATA msg;
result = send(PULSE_DATA_deserialize( &data, &msg ));
Run Code Online (Sandbox Code Playgroud)

收到邮件时,收件人会执行以下操作,

//recipient needs this declared somewhere
NEW_PULSE_DATA data;
//Need buffer to store received data
MESSAGE_FORMAT_PULSE_DATA msg;
result = receive(&msg,sizeof(msg));
//appropriate receipt checking here...
PULSE_DATA_deserialize( &data, &msg );
Run Code Online (Sandbox Code Playgroud)

  • @Acorn没有问题是关于"数组占用4个字节",这首先是不正确的,其次填充无关紧要.所以实际的问题是OP对结构做了一些不好的事情,例如将整个结构作为数据协议发送,注释表明.而这总是非便携式不良做法.正确的解决方案是序列化结构. (3认同)