2013-12-03 4 views
5
struct A 
{ 
    char c; 
    double d; 
} a; 
  • В mingw32-gcc.exe: sizeof a = 16
  • В GCC 4.6.3 (Ubuntu): sizeof a = 12

Почему они разные? Я думаю, что это должно быть 16, делает gcc4.6.3 некоторые оптимизации?Почему значения, возвращаемые компилятором sizeof(), зависят?

+0

Проверить это [размер типов данных] (http://stackoverflow.com/questions/14821738/size-of-basic-data-types-in-c) –

+10

Структура упаковки различна в разных составителей. –

ответ

11

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

Например, для большинства (но не всех) команд SSE2 требуются данные для выравнивания по 16-байтовой границе. Проще говоря, все в памяти компьютера имеет адрес. Скажем, у нас есть простой массив удваивается, например:

double data[256]; 

Для того, чтобы использовать SSE2 инструкции, требующие согласования 16-байтовый, необходимо убедиться, что адрес &data[0] кратен 16.

Требования к выравниванию отличаются от одной архитектуры к другой. На x86_64 рекомендуется, чтобы все структуры размером более 16 байт выровнены по 16-байтовым границам. В общем, для лучшей производительности, выровнять данные следующим образом:

  • Юстировка 8-битовые данные по любому адресу
  • Align 16-битные данные, которые должны содержаться в пределах выровненной четыре байта слова
  • Юстировка 32 -разрядные данные так, что его базовый адрес является кратным четырем
  • Юстировки 64-битовых данных таким образом, что его базовый адрес является кратным восьми
  • Юстировки 80-битовых данных таким образом, что его базовый адрес является кратным шестнадцати
  • Выровняйте 128-битные данные так, чтобы их базовые e адрес является кратным шестнадцати

Интересно, что большинство процессоров x86_64 будут работать как с выровненными, так и с выровненными данными. Однако, если данные не выровнены должным образом, CPU выполняет код значительно медленнее.

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

struct A { 
    char a; 
    int b; 
}; 

Предполагая, x86_64, размер int составляет 32 бит или 4 байта. Поэтому рекомендуется всегда делать адрес b кратным 4. Но поскольку размер поля a составляет всего 1 байт, это будет невозможно.Таким образом, компилятор добавит 3 байт заполнения между a и b неявно:

struct A { 
    char a; 
    char __pad0[3]; /* This would be added by compiler, 
         without any field names - __pad0 is for 
         demonstration purposes */ 
    int b; 
}; 

Как компилятор делает это зависит не только от компилятора и архитектур, но и на настройках компилятора (флаги) вы передаете компилятор. Это поведение также может быть затронуто с помощью специальных языковых конструкций. Например, можно попросить компилятор не выполнять никаких отступов с packed атрибута, как это:

struct A { 
    char a; 
    int b; 
} __attribute__((packed)); 

В вашем случае, mingw32-gcc.exe просто добавили 7 байт между c и d для выравнивания d 8 границы байта. В то время как gcc 4.6.3 на Ubuntu добавила только 3, чтобы выровнять d на границе 4 байта.

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

Для получения дополнительной информации, пожалуйста, проверьте:

Надеется, что это помогает. Удачи!

Как минимизировать отступы:

Это всегда хорошо, чтобы все ваши элементы этой структуры правильно выровнены и в то же время сохранить свой размер структуры разумно. Рассмотрим эти 2 структура варианты с членами rearanged (теперь предположим, SizeOf полукокса, короткие, Int, долго, долго долго, чтобы быть 1, 2, 4, 4, 8, соответственно):

struct A 
{ 
    char a; 
    short b; 
    char c; 
    int d; 
}; 

struct B 
{ 
    char a; 
    char c; 
    short b; 
    int d; 
}; 

Обе структуры должны сохранить те же данные, но в то время как SizeOf (структура A) будет 12 байт, SizeOf (структура в) будет 8 из-за хорошо, хотя отказа члена порядок, который устраняется неявное заполнение:

struct A 
{ 
    char a; 
    char __pad0[1]; // implicit compiler padding 
    short b; 
    char c; 
    char __pad1[3]; // implicit compiler padding 
    int d; 
}; 

struct B // no implicit padding 
{ 
    char a; 
    char c; 
    short b; 
    int d; 
}; 

Перегруппировка члены структуры могут быть подвержены ошибкам с увеличением количества членов. Для того, чтобы сделать его меньше ошибок - поместить самый длинный в начале и кратчайшим в конце:

struct B // no implicit padding 
{ 
    int d; 
    short b; 
    char a; 
    char c; 
}; 

неявной обивка в конце stuct:

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

struct abcd 
{ 
    long long a; 
    char b; 
}; 

может занимать 12 или 16 байт (худшие компиляторы позволят ему иметь 9 байтов). Это дополнение можно легко упустить, но очень важно, если ваша структура будет элементом массива. Он будет гарантировать, что ваш член в последующих ячейках ячеек/элементов будет правильно выровнен.

Заключительные и случайные мысли:

Он никогда не повредит (и может реально сэкономить) вам, если - при работе с структурами - вы будете следовать этим советам:

  • Не полагайтесь на компилятор чередуйте элементы структуры с правильным заполнением.
  • Убедитесь, что ваша структура (если внешний массив) выровнена с границей, требуемой ее самым длинным элементом.
  • Удостоверьтесь, что вы упорядочиваете элементы структуры так, чтобы самые длинные были размещены первым и последним членом.
  • Убедитесь, что вы явно вставляете свою структуру (если необходимо), чтобы при создании массива структур каждый член структуры имел правильное выравнивание.
  • Убедитесь, что массивы ваших структур правильно выровнены, так как хотя вашей структуре может потребоваться 8-байтовое выравнивание, ваш компилятор может выровнять ваш массив с 4-байтовой границей.
+2

@ Vlad Lazarenko: Это такой хороший ответ, я даю вам 100 очков репутаций. Нужно ждать до завтра. – Bathsheba

+0

@ Bathsheba:^O | O^thanks :) –

+0

@VladLazarenko: Чтобы сделать ваш ответ более полным, вы можете сказать о хороших практиках, таких как программисты, перестраивающие структурные камеры от самого длинного до кратчайшего, чтобы уменьшить общий размер структуры и уменьшить заполнение. – Artur

7

Значения, возвращаемые sizeof для structs, не подкреплены никаким стандартом C. Это зависит от архитектуры компилятора и машины.

Например, может быть оптимальным выравнивание элементов данных на 4 байтовых границах: в этом случае эффективный упакованный размер char c будет составлять 4 байта.

+0

в случае 2, sizeof (a) = 12 = 4 + 8, как это может быть – tczf

+0

Случай 2 имеет 4 байтовую упаковку: 'c' начинается с байта 0 структуры и имеет натуральный размер 1. Но это упакован до 4 байтов. Итак, следующий член 'd' начинается с байта 3 структуры. Он имеет натуральный размер 8, который кратен 4, поэтому упаковка не требуется. Общий размер поэтому 12. – Bathsheba

+0

спасибо, почему случай 1 имеет 8 байтов упаковки? – tczf

Смежные вопросы