3

我有一个用于向控制板构造消息的结构,我需要保持 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;

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

谢谢

4

3 回答 3

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;

以下对结构的重新定义完全避免了该问题。

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;

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

//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;

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

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;
}

请注意,我省略了强制性的网络字节顺序转换,因为我假设您已经解决了两个 cpu 域之间的字节顺序问题。如果您没有考虑字节顺序(big-endian vs. 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 ));

当您收到消息时,收件人会执行以下操作,

//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 );
于 2018-05-16T22:57:49.160 回答
4

Union 不会更改结构内的成员对齐方式。您对填充感兴趣。编译器可以在结构成员之间插入任意数量的字节/位以满足对齐要求。在与 gcc 兼容的编译器上,您可以使用__attribute__((__packed__))Acorn 已经指出的,但这不考虑字节序。平台之间最兼容的版本(包括具有不同对齐方式和不同字节序的平台)将使用(可悲!)get/set 函数,如下所示:

typedef struct {
    unsigned char data[2+4+2];
} NEW_PULSE_DATA;

unsigned char NEW_PULSE_DATA_get_new_weld_status(NEW_PULSE_DATA *t, size_t idx) {
    return t->data[idx];
}

void NEW_PULSE_DATA_set_new_weld_status(NEW_PULSE_DATA *t, size_t idx, unsigned char value) {
    t[idx] = value;
}

UINT32 NEW_PULSE_DATA_get_new_weld_count(NEW_PULSE_DATA *t) {
    return (UINT32)t->data[2]<<24
        | (UINT32)t->data[3]<<16 
        | (UINT32)t->data[4]<<8 
        | (UINT32)t->data[5];
}

void NEW_PULSE_DATA_set_new_weld_count(NEW_PULSE_DATA *t, UINT32 val) {
    t->data[2] = val>>24;
    t->data[3] = val>>16;
    t->data[4] = val>>8;
    t->data[5] = val;
}

UINT16 NEW_PULSE_DATA_get_new_weld_fail_count(NEW_PULSE_DATA *t) {
    return (UINT16)t->data[6]<<8
        | (UINT16)t->data[7];
}

void NEW_PULSE_DATA_set_new_weld_fail_count(NEW_PULSE_DATA *t, UINT16 val) {
    t->data[6] = val>>8;
    t->data[7] = val;
}

这是 100% 确定的唯一“好”方式,NEW_PULSE_DATA 在不同平台上看起来完全相同(至少在每个 char/ CHAR_BIT值具有相同位数的平台上)。然而sizeof(NEW_PULSE_DATA),平台之间可能仍然不同,因为编译器可能会在结构的末尾(在结构的最后一个成员之后)插入填充。因此,您可能希望将NEW_PULSE_DATA类型更改为只是一个字节数组:

typedef unsigned char NEW_PULSE_DATA[2+4+2];

unsigned char NEW_PULSE_DATA_get_new_weld_status(NEW_PULSE_DATA t, size_t idx) {
    return t[idx];
}

unsigned char NEW_PULSE_DATA_set_new_weld_status(NEW_PULSE_DATA t, size_t idx, unsigned char value) {
    t[idx] = value;
}

UINT32 NEW_PULSE_DATA_get_new_weld_count(NEW_PULSE_DATA t) {
    return (UINT32)t[2]<<24
        | (UINT32)t[3]<<16 
        | (UINT32)t[4]<<8 
        | (UINT32)t[5];
}

void NEW_PULSE_DATA_set_new_weld_count(NEW_PULSE_DATA t, UINT32 val) {
    t[2] = val>>24;
    t[3] = val>>16;
    t[4] = val>>8;
    t[5] = val;
}

UINT16 NEW_PULSE_DATA_get_new_weld_fail_count(NEW_PULSE_DATA t) {
    return (UINT16)t[6]<<8
        | (UINT16)t[7];
}

void NEW_PULSE_DATA_set_new_weld_fail_count(NEW_PULSE_DATA t, UINT16 val) 
{
    t[6] = val>>8;
    t[7] = val;
}
于 2018-05-16T22:32:30.623 回答
1

对于gcc和其他编译器,您可以使用__attribute__((packed))

此属性附加到结构或联合类型定义,指定结构或联合的每个成员(零宽度位字段除外)的放置以最小化所需的内存。

于 2018-05-16T22:13:44.160 回答