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

Как отключить заполнение структуры в C без использования pragma?

6 ответов


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

каждый элемент структуры или объекта объединения, не являющийся битовым полем, выравнивается определенным способом реализации, соответствующим его типу.

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


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


во-вторых, чтобы заказать Ваши поля в наибольшем и наименьшем порядке, таких как все типа по 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 раз медленнее, чем выровненная обработка данных. Но стандарт не требует, чтобы компиляторы были готовы к этому... учитывая стоимость исполнения, он просто не пользуется достаточным спросом. Компиляторы, поддерживающие explicit контроль над заполнением обеспечивают свои собственные прагмы именно потому, что прагмы зарезервированы для нестандартной функциональности.

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


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


Если вам действительно нужны структуры без заполнения: определите типы данных замены для short, int,long и т. д., используя структуры или классы, состоящие только из 8-битных байтов. Затем составьте структуры более высокого уровня, используя типы данных замены.

C++очень удобна, но вы можете достичь того же эффекта в C, используя структуры вместо классов. В приведенных ниже реализациях cast и assignment предполагается, что CPU может обрабатывать несоосные 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)) за определение структуры. для EG.

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

-> использовать -fpack-struct флаг при компиляции C кода. для EG.

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

надеюсь, что это помогает. Спасибо.