簡體   English   中英

結構成員對齊 - 使用16位和32位編譯器的不同sizeof

[英]Struct member alignment — different sizeof using 16-bit and 32-bit compiler

我有一個結構用於構建控制板的消息我需要保持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個字節。 我正在考慮用gcc編譯時用union替換所有new_weld_status[2] 但是有一個我可以用於gcc的開關,使chars適合/對齊2個字節?

謝謝

請注意,您的結構布局會在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;

然而,上述方法通常不是通過網絡/通過消息傳輸來傳輸結構(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;

然后,您將在傳輸的兩端實現序列化和反序列化功能。 下面的例子很簡單,但傳達了你需要的要點。

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和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 );

Union不會更改結構內的成員對齊方式。 您對填充感興趣。 編譯器可以在struct成員之間插入任意數量的字節/位以滿足對齊requiremens。 在gcc兼容的編譯器上,你可以使用__attribute__((__packed__))作為Acorn已經指出的,但是這並沒有考慮到endianess。 平台之間最兼容的版本(包括具有不同對齊和不同endianess的平台)將使用(遺憾地!)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_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;
}

對於gcc和其他編譯器,您可以使用__attribute__((packed))

此屬性附加到struct或union類型定義,指定放置結構或聯合的每個成員(除了零寬度位字段)以最小化所需的內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM