C: выравнивание структур данных

Я работаю со структурами и имею несколько вопросов о них. Как я понимаю, структурные переменные будут размещаться в памяти последовательно. Длина блоков (слов) зависит от архитектуры машины (32 бита - 4 байта, 64 бита - 8 байтов).

Допустим, у нас есть 2 структуры данных:

struct ST1 {
    char c1;
    short s;
    char c2;
    double d;
    int i;
};

В памяти это будет:

32 bit - 20 bytes    
 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
------------------------------------------------------------------------------------------
 c1| PB| s | s | c1| PB| PB| PB| d | d | d  | d  | d  | d  | d  | d  | i  | i  | i  | i  |

64 bit - 24 bytes    | 20 | 21 | 22 | 23 |
previous sequence +  ---------------------
                     | PB | PB | PB | PB |

Но мы можем изменить его, чтобы эти данные вписались в машинное слово. Как это:

struct ST2 {
    double d;
    int i;
    short s;
    char c1;
    char c2;
};

В этом случае как для 32-разрядного, так и для 64-разрядного он будет представлен одинаково (16 байт):

 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
----------------------------------------------------------------------
 d | d | d | d | d | d | d | d | i | i | i  | i  | s  | s  | ch1| ch2|

У меня есть пара вопросов:

Это'это как дикая догадка, но главное правило дляstruct это определить переменные с большим размером в начале?Как я понимаю'не работает с автономными переменными. Подобно ?char str[] = "Hello";Заполняющий байт, какой у него код? Это где-то за столом ASCII? Извини не смогне могу найти это.2 структуры со всеми членами, представленными в памяти разными адресами, и они могут быть размещены не последовательно в памяти?Такая структура:struct ST3 { char c1; char c2; char c3;} st3; имеетsize = 3Я понимаю, что если мы добавим в него члена с другим типом, он будет выровнен. Но почему этоне выравнивается до этого?

Ответы на вопрос(5)

Отвечая на ваши вопросы как заданные (игнорируя вашу очень красивую картину структуры)

Это'Это как дикая догадка, но главное правило для структуры - определить переменные большего размера в начале?

Всегда ставьте вещи, которые требуют наибольшего выравнивания в первую очередь. Я бы неположитьchar[99] первый например. В целом это работает как указатели, 64-битные собственные типы, 32-битные собственные типы и т. Д., Но вы должны быть очень осторожны, если ваша структура содержит элементы, которые являются другими структурами.

Как я понимаю'не работает с автономными переменными. подобноchar str[] = "Hello";

Я неЯ действительно не понимаю этого. Если вы определяете массив символов в стеке, он имеет выравнивание символов. Если вы определите массив символов, за которым следует int,Вероятно, будет заполнение в стеке, вы просто не можете его найти.

Заполняющий байт, какой у него код? Это где-то за столом ASCII? Извини не смогне могу найти это.

У него нет ни кода, ни данных. Это вставка компилятором и может содержать любое значение, которое может отличаться или не различаться в разных экземплярах структуры при одинаковых или разных запусках программы.

2 структуры со всеми членами, представленными в памяти разными адресами, и они могут быть размещены не последовательно в памяти?

Я этого не понимаю. Вы спрашиваете, может ли компилятор вставить отступ между структурами? Если нет, пожалуйста, уточните, потому что этот ответ не сильно поможет;

Когда компилятор создает структуру, он должен позволить вам разумно создать массив таких структур. Учти это:

struct  S {
    int wibble;
    char wobble;
};

S stuff[2];

Если компилятор неt вставить 3 байта заполнения после колебания, доступ кstuff[1].wobble победил'быть правильно выровненным, что приведет к сбоям на некоторых аппаратных средствах (и к ужасной производительности на других аппаратных средствах). По сути, компилятор должен обеспечить заполнение в конце, чтобы гарантировать, что наиболее выровненный элемент структуры всегда правильно выровнен для массива таких структур.

Такая структура:struct ST3 { char c1; char c2; char c3;} st3; Имеет размер = 3, я понимаю, что если мы добавим в него элемент с другим типом, он будет выровнен. Но почему этоне выровнены до этого?

Ты имеешь ввиду 'Почему нетт компилятор поместил его в место, где онправильно выровнен? Потому что язык нене позволяй. Компилятор неРазрешено изменить порядок членов вашей структуры. Разрешается только вставлять отступы.

Выравнивание членов структур (и классов) зависит от платформы, правда, но также и от компилятора. Причина выравнивания элементов по размеру - из соображений производительности. Выравнивание всех целочисленных типов по размеру сокращает доступ к памяти.

Обычно вы можете заставить компилятор уменьшить выравнивание, но это не очень хорошая идея, за исключением особых причин (например, для совместимости данных между различными платформами, например, для передачи данных). В Visual C ++ существует#pragma pack для этого, например:

#pragma pack(1)
struct ST1 {
    char c1;
    short s;
    char c2;
    double d;
    int i;
};

assert(sizeof(ST1) == 16);

Но, как я уже говорил, обычно это не очень хорошая идея.

Имейте в виду, что компилятор не просто добавляет байты pad после некоторых полей. Это's также гарантирует, что структура выделена в памяти для всех полей, выровненных по правому краю. Я имею в виду, что в вашем примере ST1, поскольку больший тип поля равен double, компилятор будет уверенd поле будет выровнено на 8 байтов (кроме случаев использования#pragma pack или аналогичные варианты):

ST1 st1;

assert(&st1.d % 8 == 0);

О ваших вопросах:

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

Например:

short   s[27];
int32_t i32[34];
int64_t i64[45];

assert(s % 2 == 0);
assert(i32 % 4 == 0);
assert(i64 % 8 == 0);
Байты заполнения могут содержать все что угодно. Обычно инициализируются данные (по крайней мере, вы инициализируете их). Иногда может содержать определенный байтовый шаблон компилятором по причинам отладки.О структурах со всеми членами, представленными в памяти разными адресами: извините, я нене понимаю, что, как вы спрашиваете.Стандарт C ++ говорит, что адрес структуры / класса должен совпадать с адресом первого поля такой структуры / класса. Тогда, только возможно заполнение послеc3, но никогда раньше.c1

Из N3337 (C ++ 11) [9.2 class.menu, стр.20]:

Указатель на объект структуры стандартной компоновки, соответствующим образом преобразованный с использованиемreinterpret_cast, указывает на свой начальный элемент (или, если этот элемент является битовым полем, то на модуль, в котором он находится), и наоборот. [Примечание: Следовательно, в объекте структуры стандартной компоновки может быть безымянный отступ, но не в его начале, что необходимо для достижения соответствующего выравнивания. -конечная нота]

Основные правила просты:

члены должны быть там по порядку (если только в C ++ вы не используете private: public: ... section)допускается заполнение между членами и после последнего

Тот'об этом. Остальное оставлено для реализации: память, занимаемая типами, сумма заполнения. Обычно вы можете ожидать, что это будет должным образом задокументировано вABI или непосредственно в компиляторе, и даже есть инструменты для манипулирования.

На практике дополнения необходимы на некоторых архитектурах, скажем, SPARC требует32-битный "Интс» выровнены по адресу, кратному 4. Для других это не является обязательным требованием, но выровненные объекты могут занять больше времени для обработки, скажем,80286 процессор берет дополнительный цикл для чтения 16-битного объекта с нечетного адреса. (Прежде чем я забыл: представление типов само по себе другое!)

Обычно требование выравнивания или наилучшая производительность точно совпадают: вы должны выровнять по границе, равной размеру. Хорошим контрпримером является80-битный Числа с плавающей запятой (доступны как double или long double в некоторых компиляторах), которые любят выравнивание 8 или 16 байтов, а не 10.

Чтобы возиться с компилятором заполнения, обычно дают вам возможность установить значение по умолчанию. Это меняется от версии к версии, поэтому лучше учитывать при обновлении. И внутри объекта переопределения кода, как_attribute__(packed) вНКУ а также#pragma упаковать вМИЗ и много других. Это все расширения стандарта, очевидно.

Суть в том, что если вы хотите возиться с компоновкой, вы начинаете читать dox всех компиляторов, на которые вы рассчитываете, сейчас и в будущем, чтобы знать, что они делают и как им управлять. Возможно также читать Dox целевых платформ, в зависимости от того, почему выИнтересует макет в первую очередь.

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

Другая мотивация - производительность. Это намного сложнее, так как правила быстро меняются, а эффект трудно предсказать сразу. Скажи по инве основной "перекос» наказание уходит в течение длительного времени, вместо этого важно быть внутри строки кэша. Где размер строки кэша зависит от процессора. Кроме того, использование большего количества заполнителей может дать лучшую индивидуальность, в то время как полностью упакованные структуры более экономичны в использовании кэша.

А некоторые операции требуют правильного выравнивания, но компилятор не предписывает их напрямую, вам может потребоваться применить специальные прагмы выравнивания (как, например,SSEсвязанные вещи).

Повторяемая нижняя строка: прекратите гадать, определите свои цели и прочитайте правильный докс. (Кстати, для меня, читая руководства по архитектуре дляSPARC,IA32 а другим было огромное удовольствие и выигрыш во многих отношениях.)

Будь осторожен, ты неУбедитесь, что ваши переменные выровнены (но это часто так). Если вы используете GCC, вы можете использоватьатрибут упакован, чтобы быть уверенным, что ваши данные выровнены.

Пример :

struct foo {
    char c;
    int x;
} __attribute__((packed));

Как я понимаю'не работает с автономными переменными. Нравится char str [] = "Привет";?

Эта таблица будет выровнена в вашей памяти.

 Christoph07 июн. 2013 г., 11:04
чего ждать? с помощьюpacked избавится от набивки, возможно, заставляя членовне быть правильно выровненным, что имеет последствия даже для x86 (например, доступ кdouble только атомно, если оноправильно выровнены)
 Casey07 июн. 2013 г., 18:10
Как говорит Кристоф, этот ответ - полная дезинформация.

для gcc на архитектуре Intel требуется больше инструкций и циклов для доступа (чтения / записи) адреса памяти с нечетным номером. поэтому добавляется заполнение для получения четного номера памяти адреса

Ваш ответ на вопрос