Отключить структурную прокладку в C без использования Pragma

StackOverflow https://stackoverflow.com/questions/4301759

  •  29-09-2019
  •  | 
  •  

Вопрос

Как я могу отключить структурную прокладку в C без использования Pragma?

Это было полезно?

Решение

Там нет стандартного способа сделать это. Стандарт гласит, что прокладка может быть выполнена по усмотрению реализации. Из C99 6.7.2.1 Structure and union specifiers, пункт 12:

Каждый небитный член структуры или объекта объединения выровнен в определяемом внедрении, подходящим для его типа.

Сказав это, есть несколько вещей, которые вы можете попробовать.


Первое, что вы уже сбрасывали, используя #pragma Чтобы попытаться убедить компилятора не упаковать. В любом случае, это не портативно. Также не являются никаких других специфических для реализации способов, но вы должны проверить их, поскольку это может быть необходимо сделать, если вам действительно нужна эта возможность.


Второе - заказать ваши поля в наибольшем до наименьшего порядка, например, все long long Типы, за которыми следуют long одни, тогда все int, short и наконец char типы Обычно это работает, поскольку чаще всего это более крупные типы, которые имеют более строгие требования к выравниванию. Опять не портативный.


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

Этот последний несет в себе дополнительное объяснение. Скажем, у вас есть структура с полями в следующем порядке:

char C; // one byte
int  I; // two bytes
long L; // four bytes

С заполнением вы можете получить следующие байты:

CxxxIIxxLLLL

куда x это наполнение.

Однако, если вы определяете свою структуру как:

typedef struct { char c[7]; } myType;
myType n;

Вы получаете:

CCCCCCC

Тогда вы можете сделать что -то вроде:

int *pInt = &(n.c[1]);
int *pLng = &(n.c[3]);
int myInt = *pInt;
int myLong = *pLng;

чтобы дать вам:

CIILLLL

Опять же, к сожалению, не портативно.


Все эти «решения» полагаются на то, что вы имеете интимные знания вашего компилятора и основных типов данных.

Другие советы

Помимо вариантов компилятора, таких как Pragma Pack, вы не можете, заполнение находится в стандарте C.

Вы всегда можете попытаться сократить прокладку, объявив самые маленькие типы, которые будут длиться в структуре, как в:

struct _foo {
     int a;  /* No padding between a & b */
     short b;
} foo;

struct _bar {
     short b; /* 2 bytes of padding between a & b */
     int a;
} bar;

Примечание Для реализаций, которые имеют 4 байта

На некоторых архитектурах сам процессор будет возражать, если их попросят работать над смещенными данными. Чтобы обойти это, компилятор может генерировать несколько выровненных инструкций чтения или записи, сдвинуть и разделить или объединять различные биты. Вы можете разумно ожидать, что он будет в 5 или 10 раз медленнее, чем выровненная обработка данных. Но стандарт не требует, чтобы компиляторы были готовы сделать это ... учитывая стоимость производительности, это просто недостаточно. Компиляторы, которые поддерживают явный контроль над прокладкой, обеспечивают свои собственные прагмы именно потому, что прагмы зарезервированы для нестандартных функциональности.

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

Либо вы позволяете компилятору делать заполнение, либо не говорите ему не делать с помощью #Pragma, либо вы просто используете кучу байтов, таких как массив Char, и вы создаете все свои данные самостоятельно (смещение и добавление байтов). Это действительно неэффективно, но вы точно контролируете макет байтов. Я делал это иногда подготовка сетевых пакетов вручную, но в большинстве случаев это плохая идея, даже если это стандартная.

Если вам действительно нужны структуры без прокладки: определите замену дата данных для коротких, Int, Long и т. Д., Используя структуры или классы, которые составлены только из 8 -битных байтов. Затем составьте свои строки более высокого уровня, используя запасные данные дата.

Перегрузка оператора C ++ очень удобна, но вы можете достичь того же эффекта в C, используя структуры вместо классов. Приведенные ниже реализации актеров и назначения предполагают, что ЦП может обрабатывать смещенные 32 -битные целые числа, но другие реализации могут приспособиться к более строгим процессорам.

Вот пример кода:

#include <stdint.h>
#include <stdio.h>

class packable_int { public:

  int8_t b[4];

  operator int32_t () const       { return *(int32_t*) b; }
  void operator =  ( int32_t n )  { *(int32_t*) b = n; }

};

struct SA {
  int8_t   c;
  int32_t  n;
} sa;

struct SB {
  int8_t        c;
  packable_int  n;
} sb;

int main () {
  printf ( "sizeof sa  %d\n", sizeof sa );    // sizeof sa  8               
  printf ( "sizeof sb  %d\n", sizeof sb );    // sizeof sb  5               
  return 0;
}

Мы можем отключить структурную прокладку в программе C, используя любой из следующих методов.

-> Использовать __attribute__((packed)) За определением структуры. Например.

struct node {
    char x;
    short y;
    int z;
} __attribute__((packed));

-> Использовать -fpack-struct Флаг во время компиляции C -кода. Например.

$ gcc -fpack-struct -o tmp tmp.c

Надеюсь это поможет. Спасибо.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top