При манипуляции с данными в C/C++ довольно часто пользуются наложением структуры на массив / приведением типа, если не рассуждать о безопасности этого метода, можно сказать, что довольно часто у начинающих бывает одна маленькая и забавная ошибка, они забывают про выравнивание полей структуры в памяти. Для чего это все делают забавные компиляторы? Очевидно, что для оптимизации работы с памятью, учитывая особенности реализации конкретной аппаратной платформы.
Данную ситуацию можно рассмотреть на пример некоторой реализации протокола обмена данными или же на примере работы с файлами двоичной структуры. Допустим, что у нас есть некоторый протокол, в котором каждое сообщение (пакет) имеет заголовок с полями приведенными ниже:
Данную ситуацию можно рассмотреть на пример некоторой реализации протокола обмена данными или же на примере работы с файлами двоичной структуры. Допустим, что у нас есть некоторый протокол, в котором каждое сообщение (пакет) имеет заголовок с полями приведенными ниже:
На С/С++ заголовок сообщения вполне можем представить в виде структуры:
typedef struct {
unsigned char sg;
unsigned char type;
unsigned short len;
time_t time;
unsigned char crc;
} msg_header_t;
Не будем рассматривать сообщения имеющие блину тела отличную от нуля, пусть это будет короткое сообщение, состоящее только из заголовка. Мы его формируем, передам в кольцевой буфер и так далее...
msg_header_t *msg;/* ... */
RingBuffer->Write((unsigned char*)msg, sizeof(msg_header_t));
Все замечательно и мы рассчитываем передать в кольцевой буфер блок длиной в 9 байт (при условии, что time_t у нас 4 байта), однако компиляторы хлебом не корми, дай что-нибудь соптимизировать и в итоге мы получаем 12 байт, как раз из за выравнивания.
В принципе, если обмен данными происходит между двумя экземплярами одной и той же программы, то все даже будет работать корректно, однако это очень частный случай и как правило такая ситуация вызовет некорректную обработку сообщения (пакета).
Решение данной ситуации, это явное указание компилятору, как нужно разместить поля структуры в памяти. Компиляторы С/С++ поддерживаются специальную директиву препроцессора #pragma pack. Применяется это так:
#pragma pack(1)
/* Описываем нашу структуру данных с побайтной упаковкой */
#pragma pack()
Посмотреть как это работает можно на простом примере:
/* msg_header_t1 */В принципе, если обмен данными происходит между двумя экземплярами одной и той же программы, то все даже будет работать корректно, однако это очень частный случай и как правило такая ситуация вызовет некорректную обработку сообщения (пакета).
Решение данной ситуации, это явное указание компилятору, как нужно разместить поля структуры в памяти. Компиляторы С/С++ поддерживаются специальную директиву препроцессора #pragma pack. Применяется это так:
#pragma pack(1)
/* Описываем нашу структуру данных с побайтной упаковкой */
#pragma pack()
Посмотреть как это работает можно на простом примере:
typedef struct {
unsigned char sg;
unsigned char type;
unsigned short len;
time_t time;
unsigned char crc;
} msg_header_t1;
/* msg_header_t2 */
#pragma pack(1)
typedef struct {
unsigned char sg;
unsigned char type;
unsigned short len;
time_t time;
unsigned char crc;
} msg_header_t2;
#pragma pack()
/* ... */
int main(void) {
printf("msg_header_t1: %d, msg_header_t2: %d\n",
sizeof(msg_header_t1),
sizeof(msg_header_t2));
return 0;
}
Результат должен выглядеть примерно так:
msg_header_t1: 12, msg_header_t2: 9
0 комментария(ев):
Post a Comment