Выравнивание членов структуры - разный размер с использованием 16-битного и 32-битного компилятора
У меня есть структура, используемая для передачи сообщений на панель управления, которая мне необходима для обеспечения программной совместимости между 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 ответа
Обратите внимание, что структура вашей структуры создает проблему в 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;
Тем не менее, вышеупомянутый подход не является подходом, как правило, для передачи структурных (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;
}
Обратите внимание, что я пропустил обязательные сетевые преобразования порядка байтов, потому что я предполагаю, что вы уже решили проблемы с порядком байтов между двумя доменами процессора. Если вы не рассматривали порядок байтов (с прямым порядком байтов или с прямым порядком байтов), то вам также необходимо решить эту проблему.
Когда вы отправляете сообщение, отправитель делает следующее:
//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 );
Союз не изменит выравнивание членов внутри структуры. Вы заинтересованы в прокладке. Компилятор может вставить любое количество байтов / битов между членами структуры, чтобы удовлетворить требования выравнивания. На 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;
}
За gcc
и другие компиляторы, вы можете использовать __attribute__((packed))
:
Этот атрибут, прикрепленный к определению типа структуры или объединения, указывает, что каждый элемент (кроме битовых полей нулевой ширины) структуры или объединения размещается для минимизации требуемой памяти.