Выравнивание членов структуры - разный размер с использованием 16-битного и 32-битного компилятора - PullRequest
0 голосов
/ 17 мая 2018

У меня есть структура, используемая для создания сообщений на плате управления, необходимая мне для поддержания программной совместимости между 16-битным компилятором Keil C167 и 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] занимает 2 байта в 16-битном компиляторе, но 4 байта в 32-битном компиляторе. Я думал о замене всех new_weld_status[2] на объединение при компиляции с gcc. Но есть ли переключатель, который я могу использовать для gcc, который позволяет использовать / выравнивать символы в 2 байта?

Спасибо

Ответы [ 3 ]

0 голосов
/ 17 мая 2018

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_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;
}
0 голосов
/ 17 мая 2018

Обратите внимание, что структура вашей структуры создает проблему в 32-битной системе. Многие (большинство) 32-битных архитектур ЦП требуют 4-байтового выравнивания для 32-битных слов, поэтому new_weld_count требует 'padding' для обеспечения правильного выравнивания памяти.

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

Обратите внимание, что я пропустил обязательные сетевые преобразования порядка байтов, потому что я предполагаю, что вы уже решили проблемы с порядком байтов между двумя доменами процессора. Если вы не рассматривали порядок байтов (от байтов до байтов), то вам также необходимо решить эту проблему.

При отправке сообщения отправитель выполняет следующие действия:

//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 );
0 голосов
/ 17 мая 2018

Для gcc и других компиляторов вы можете использовать __attribute__((packed)):

Этот атрибут, связанный с определением типа структуры или объединения, указывает, что каждый член (другойчем битовые поля нулевой ширины) структуры или объединения, чтобы минимизировать требуемую память.

...