Почему sizeof для структуры не равен сумме sizeof каждого члена?

Почему оператор sizeof возвращает размер структуры больше, чем общие размеры членов структуры?


person Kevin    schedule 23.09.2008    source источник
comment
См. Этот FAQ на C по выравниванию памяти. c-faq.com/struct/align.esr.html   -  person Richard Chambers    schedule 26.04.2013
comment
Анекдот: существовал настоящий компьютерный вирус, который поместил свой код в структурные дополнения в основной программе.   -  person Elazar    schedule 02.09.2013
comment
@Elazar Это впечатляет! Никогда бы не подумал, что можно использовать такие крошечные участки для чего-нибудь. Можете ли вы предоставить более подробную информацию?   -  person OmarL    schedule 14.11.2016
comment
@Wilson - Я уверен, что это было связано с большим количеством jmp.   -  person hoodaticus    schedule 28.06.2017
comment
См. Структуру заполнение, упаковка: Lost Art of C Structure Packing Эрик С. Реймонд   -  person EsmaeelE    schedule 09.12.2017
comment
SO, answer, _ 2_, другая ссылка   -  person EsmaeelE    schedule 09.12.2017
comment
Существует ли заполнение между двумя структурами, чтобы первый член следующей структуры начинался с выровненного адреса?   -  person AlphaGoku    schedule 07.02.2018
comment
@Akshay Обе структуры начнут выравниваться (если вы не заставите их не быть ... побайтной сериализацией и т. Д.)   -  person Matthew M.    schedule 14.04.2018
comment
У меня тоже есть эта проблема. Также у меня есть структура, в которой все битовые поля составляют до 128 бит. 128/8 = 16. Но когда я вызываю sizeof, я получаю 22 байта.   -  person    schedule 09.12.2019
comment
Любопытно, что спустя почти 12 лет после его выпуска и около 195 тысяч просмотров никто не заметил, что этот вопрос на самом деле совершенно неправильный.   -  person RobertS supports Monica Cellio    schedule 29.06.2020
comment
@RobertSsupportsMonicaCellio, это неправильно только формально. Буквально sizeof возвращает размер структуры. Просто его фактический размер больше, чем объем памяти, воспринимаемый человеком, просматривающим декларацию.   -  person Swift - Friday Pie    schedule 20.06.2021


Ответы (12)


Это связано с добавлением отступов для удовлетворения ограничений выравнивания. Выравнивание структуры данных влияет как на производительность, так и на правильность программ:

  • Неправильный доступ может быть серьезной ошибкой (часто SIGBUS).
  • Mis-aligned access might be a soft error.
    • Either corrected in hardware, for a modest performance-degradation.
    • Или исправлено эмуляцией в программном обеспечении для серьезного снижения производительности.
    • Кроме того, атомарность и другие гарантии параллелизма могут быть нарушены, что приведет к незаметным ошибкам.

Вот пример использования типичных настроек для процессора x86 (все использовались 32- и 64-битные режимы):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

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

ВАЖНОЕ ПРИМЕЧАНИЕ. В стандартах C и C ++ указано, что выравнивание структуры определяется реализацией. Поэтому каждый компилятор может выбирать выравнивание данных по-разному, что приводит к разным и несовместимым макетам данных. По этой причине при работе с библиотеками, которые будут использоваться разными компиляторами, важно понимать, как компиляторы выравнивают данные. Некоторые компиляторы имеют настройки командной строки и / или специальные операторы #pragma для изменения настроек выравнивания структуры.

person Community    schedule 23.09.2008
comment
Я хочу сделать здесь примечание: большинство процессоров наказывают вас за невыровненный доступ к памяти (как вы упомянули), но вы не можете забыть, что многие полностью его запрещают. В частности, большинство микросхем MIPS выдают исключение при невыровненном доступе. - person Serafina Brocious; 23.09.2008
comment
Чипы x86 на самом деле довольно уникальны тем, что они допускают невыровненный доступ, хотя и наказываются; AFAIK большинство чипов вызовут исключения, а не только некоторые. PowerPC - еще один распространенный пример. - person Dark Shikari; 23.09.2008
comment
Включение прагм для невыровненного доступа обычно приводит к увеличению размера вашего кода на процессорах, которые вызывают ошибки несовпадения, поскольку необходимо сгенерировать код для исправления каждого несовпадения. ARM также выдает ошибки несоосности. - person Mike Dimmick; 23.09.2008
comment
@Dark - полностью согласен. Но большинство настольных процессоров - x86 / x64, поэтому большинство чипов не вызывают ошибок выравнивания данных;) - person Aaron; 26.09.2008
comment
Невыровненный доступ к данным обычно является функцией архитектур CISC, и большинство архитектур RISC не включают ее (ARM, MIPS, PowerPC, Cell). Фактически, большинство микросхем НЕ являются процессорами для настольных ПК, для встраиваемых систем определяется количество микросхем, и подавляющее большинство из них являются архитектурами RISC. - person Lara Dougan; 19.10.2008
comment
Ловушка невыровненного доступа используется (или, конечно, использовалась) в реализациях функционального языка для маркировки значений, чтобы их сборщики мусора могли знать, на какую произвольную память они смотрят. В общем, очень умный хак (согласно изречению Кернигана, слишком умный, чтобы использовать его в моем коде). - person Donal Fellows; 22.12.2010
comment
Чтобы быть педантичным, разве стандарт не гарантирует выравнивания, если все члены структуры имеют тип char? - person Kerrek SB; 15.06.2011
comment
@Kerrek SB: Стандарт гарантирует выравнивание для любой структуры независимо от используемых типов. Однако для char размером 1 байт невозможно выровнять его. Итак, стандарт гарантирует выравнивание, если все члены структуры являются символами БЕЗ КАКИХ-ЛИБО ЗАПОЛНЕНИЙ. - person Bodo Thiesen; 26.11.2014
comment
@LaraDougan: да, и каким-то образом есть простое правило, с помощью которого мы можем понять, почему это так. Стоимость за чип. Чипы x86 для настольных ПК - это потребительский товар за сотни долларов. Нет ничего терпимого для большинства промышленных применений, обычно промышленность имеет дело с чипами стоимостью менее 1 доллара или около того. Легко увидеть, насколько это повлияло на широкое распространение. - person v.oddou; 14.06.2016
comment
Почему для первого char есть 3 байта заполнения, а для следующих 2 только 1 байт? - person WWZee; 26.01.2017
comment
@WayneO Количество отступов всегда достаточно, чтобы убедиться, что все, что будет дальше, выровнено в соответствии с его размером. Итак, в X есть 2 байта заполнения после short, чтобы гарантировать, что 4 байта int начинаются на 4-байтовой границе. В Y после char добавляется 1 байт, чтобы убедиться, что 2 байта short начинаются на 2-байтовой границе. Поскольку компилятор не может знать, что может быть после структуры в памяти (а это может быть много разных вещей), он готовится к худшему и вставляет достаточно заполнения, чтобы структура стала кратной 4 байтам. X нужно 3 байта, чтобы получить 12, Y только 1 для 8. - person 8bittree; 17.02.2017
comment
Микросхемы x86 имеют аппаратную поддержку невыровненного доступа True. Микросхемы x86 не выдают ошибок выравнивания данных Ложь. Это зависит от инструкции, инструкции SSE, в частности, имеют тенденцию к ошибкам из-за несовпадения (за исключением специальных невыровненных вариантов). - person Ben Voigt; 25.05.2017
comment
Существует ли заполнение между двумя структурами, чтобы первый член следующей структуры начинался с выровненного адреса? - person AlphaGoku; 07.02.2018
comment
@AkshayImmanuelD: Не между структурами, нет, это часть конца структуры. struct {long long a; char b;} обычно имеет 7 байтов заполнения в конце после b, что в сумме составляет 16 байтов. (на большинстве 64-битных архитектур yada yada) - person Mooing Duck; 24.01.2019
comment
Вопрос: если на одной машине в одной программе у меня есть две структуры S1 и S2 с одинаковыми членами. Требует ли стандарт, чтобы обе структуры имели одинаковый размер, или компилятор может решить использовать разные методы заполнения для S1 и S2? - person mercury0114; 07.11.2020
comment
@LaraDougan Я не думаю, что это было правдой, когда вы писали свой комментарий, и, вероятно, все еще ложно. Многие встроенные микросхемы не имеют концепции согласованного доступа к данным, потому что они обращаются только к 1 байту за один раз. Многие чипы по-прежнему 8-битные. (Возможно, это уже не большинство чипов, а 8-битные чипы, которых было большинство 12 лет назад). - person 12431234123412341234123; 15.01.2021
comment
@ 12431234123412341234123 не путайте микроконтроллеры и встроенные чипы. Встроенный процессор предположительно может быть полнофункциональным процессором. Например. большинство домашних интернет-маршрутизаторов, модемов DOXIS, точек доступа Wi-Fi работают под управлением Linux, BSD или аналогичной ОС, на MIPS, ARM или аналогичном семействе. Это то же самое, что микроконтроллер на USB-накопителе или USB-клавиатуре. Если мы посчитаем их, средний смартфон содержит около дюжины с половиной процессоров. Это разные инструменты для кодирования контроллеров, даже если они выглядят как разновидность C или C ++, они не являются ISO. - person Swift - Friday Pie; 20.06.2021

Упаковка и выравнивание байтов, как описано в здесь часто задаваемых вопросов по C:

Это для выравнивания. Многие процессоры не могут получить доступ к 2- и 4-байтовым числам (например, целым и длинным целым числам), если они переполнены повсюду.

Предположим, у вас есть такая структура:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

Теперь вы можете подумать, что эту структуру можно было бы упаковать в память следующим образом:

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

Но для процессора это намного проще, если компилятор устроит это так:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

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

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+
person EmmEff    schedule 23.09.2008
comment
Теперь в чем польза от слотов памяти pad1, pad2 и pad3. - person Lakshmi Sreekanth Chitla; 26.12.2016
comment
@EmmEff, это может быть неправильно, но я не совсем понимаю: почему в массивах нет слота памяти для указателя? - person Balázs Börcsök; 08.12.2019
comment
@ BalázsBörcsök Это массивы постоянного размера, поэтому их элементы хранятся непосредственно в структуре с фиксированными смещениями. Компилятор знает все это во время компиляции, поэтому указатель является неявным. Например, если у вас есть структурная переменная этого типа с именем s, то &s.a == &s и &s.d == &s + 12 (учитывая выравнивание, показанное в ответе). Указатель сохраняется только в том случае, если массивы имеют переменный размер (например, a был объявлен char a[] вместо char a[3]), но тогда элементы должны храниться в другом месте. - person kbolino; 31.03.2020

Если вы хотите, чтобы структура имела определенный размер с GCC, например, используйте _ 1_.

В Windows вы можете установить выравнивание в один байт при использовании компилятора cl.exe с / Zp option.

Обычно процессору проще получить доступ к данным, кратным 4 (или 8), в зависимости от платформы, а также от компилятора.

Так что в основном это вопрос согласования.

Для его изменения должны быть веские причины.

person Community    schedule 23.09.2008
comment
веские причины. Пример. Обеспечение согласованности двоичной совместимости (заполнения) между 32-разрядными и 64-разрядными системами для сложной структуры в демонстрационном коде подтверждения концепции, который будет продемонстрирован завтра. Иногда необходимость важнее приличия. - person Mr.Ree; 08.12.2008
comment
Все в порядке, кроме тех случаев, когда вы упоминаете операционную систему. Это проблема скорости ЦП, ОС вообще не задействована. - person Blaisorblade; 12.01.2009
comment
Еще одна веская причина - если вы вставляете поток данных в структуру, например при разборе сетевых протоколов. - person ceo; 20.10.2009
comment
@Blaisorblade Хотя архитектура ЦП является наиболее важным моментом, ОС также может иметь значение. Подумайте о процессоре x86, работающем в реальном режиме (MS-DOS) по сравнению с защищенным режимом (Windows, Linux ...). - person dolmen; 20.08.2013
comment
@dolmen Я только что указал, что для Operatin проще получить доступ к некорректным данным, так как ОС не имеет доступа к данным. - person Blaisorblade; 24.08.2013
comment
@dolmen На самом деле следует говорить о ABI (бинарном интерфейсе приложения). Выравнивание по умолчанию (используется, если вы не меняете его в исходном коде) зависит от ABI, и многие ОС поддерживают несколько ABI (скажем, 32- и 64-разрядные, или для двоичных файлов из разных ОС, или для разных способов компиляции одинаковые двоичные файлы для той же ОС). OTOH, какое выравнивание удобно с точки зрения производительности, зависит от процессора - доступ к памяти осуществляется одинаково, независимо от того, используете ли вы 32- или 64-разрядный режим (я не могу комментировать реальный режим, но в настоящее время вряд ли актуален для производительности). IIRC Pentium начал отдавать предпочтение 8-байтовому выравниванию. - person Blaisorblade; 24.08.2013
comment
__attribute__((packed)) в некоторых случаях потенциально небезопасно: stackoverflow.com/q/8568432/827263 - person Keith Thompson; 10.06.2015
comment
В компиляторах Microsoft вы должны использовать #pragma pack, делать это с параметром командной строки - зло. (GCC и clang в Windows используют __attribute__, как и в любой другой ОС) - person Ben Voigt; 31.03.2019
comment
Лучше использовать #pragma pack(1) - он поддерживается MSVC, gcc и clang, что делает ваш код более переносимым - person mvp; 21.03.2021
comment
В общем, вместо упаковки структуры для экономии памяти попробуйте переупорядочить поля таким образом, чтобы свести к минимуму необходимость упаковки - person user16217248; 27.06.2021

Это может быть связано с выравниванием байтов и заполнением, так что структура выходит на четное количество байтов (или слов) на вашей платформе. Например, в C в Linux следующие 3 структуры:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

Имейте члены, размеры которых (в байтах) составляют 4 байта (32 бита), 8 байтов (2x 32 бита) и 1 байт (2 + 6 бит) соответственно. Вышеупомянутая программа (в Linux с использованием gcc) печатает размеры как 4, 8 и 4 - где последняя структура дополняется так, чтобы это было одно слово (4 x 8-битных байта на моей 32-битной платформе).

oneInt=4
twoInts=8
someBits=4
person Kyle Burton    schedule 23.09.2008
comment
C в Linux с использованием gcc недостаточно для описания вашей платформы. Выравнивание в основном зависит от архитектуры процессора. - person dolmen; 20.08.2013
comment
- @ Кайл Бертон. Извините, я не понимаю, почему размер структуры someBits равен 4, я ожидаю 8 байтов, поскольку объявлено 2 целых числа (2 * sizeof (int)) = 8 байтов. Благодарность - person youpilat13; 04.07.2018
comment
Привет @ youpilat13, :2 и :6 на самом деле указывают 2 и 6 бит, а не полные 32-битные целые числа в этом случае. someBits.x, будучи всего 2 битами, может хранить только 4 возможных значения: 00, 01, 10 и 11 (1, 2, 3 и 4). Имеет ли это смысл? Вот статья об этой функции: geeksforgeeks.org/bit-fields-c - person Kyle Burton; 13.07.2018

Смотрите также:

для Microsoft Visual C:

http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx

и GCC заявляют о совместимости с компилятором Microsoft .:

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Structure_002dPacking-Pragmas.html

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

Я совершенно уверен, что порядок членов гарантирован в C, но я бы не стал на это рассчитывать при написании кроссплатформенной или кросс-компиляторной программы.

person lkanab    schedule 31.05.2011
comment
Я совершенно уверен, что членский порядок в C.Да, C99 говорит: внутри объекта структуры небитовые поля и единицы, в которых находятся битовые поля, имеют адреса, которые увеличиваются в том порядке, в котором они находятся. заявил. Дополнительные стандартные возможности по адресу: stackoverflow.com/a/37032302/895245 - person Ciro Santilli 新疆再教育营六四事件ۍ 04.05.2016

Размер конструкции больше суммы ее частей из-за того, что называется упаковкой. Конкретный процессор имеет предпочтительный размер данных, с которыми он работает. Предпочтительный размер большинства современных процессоров - 32 бита (4 байта). Доступ к памяти, когда данные находятся на такой границе, более эффективен, чем доступ к памяти, находящейся на границе этого размера.

Например. Рассмотрим простую структуру:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

Если машина 32-битная и данные выровнены по 32-битной границе, мы видим немедленную проблему (при условии отсутствия выравнивания структуры). В этом примере предположим, что данные структуры начинаются с адреса 1024 (0x400 - обратите внимание, что два младших бита равны нулю, поэтому данные выровнены по 32-битной границе). Доступ к data.a будет работать нормально, потому что он начинается на границе - 0x400. Доступ к data.b также будет работать нормально, потому что он находится по адресу 0x404 - еще одна 32-битная граница. Но невыровненная структура поместит data.c по адресу 0x405. 4 байта data.c находятся по адресу 0x405, 0x406, 0x407, 0x408. На 32-битной машине система будет читать data.c в течение одного цикла памяти, но получит только 3 из 4 байтов (4-й байт находится на следующей границе). Итак, система должна будет сделать второй доступ к памяти, чтобы получить 4-й байт,

Теперь, если вместо размещения data.c по адресу 0x405, компилятор дополнит структуру 3 байтами и поместит data.c по адресу 0x408, тогда системе потребуется всего 1 цикл для чтения данных, что сократит время доступа к этому элементу данных. на 50%. Padding меняет местами эффективность памяти для повышения эффективности обработки. Учитывая, что компьютеры могут иметь огромный объем памяти (много гигабайт), компиляторы считают, что подкачка (скорость превышает размер) является разумной.

К сожалению, эта проблема становится смертельной, когда вы пытаетесь отправить структуры по сети или даже записать двоичные данные в двоичный файл. Заполнение, вставленное между элементами структуры или класса, может нарушить отправку данных в файл или сеть. Чтобы написать переносимый код (тот, который будет использоваться несколькими разными компиляторами), вам, вероятно, придется обращаться к каждому элементу структуры отдельно, чтобы гарантировать надлежащую «упаковку».

С другой стороны, разные компиляторы имеют разные возможности для управления упаковкой структур данных. Например, в Visual C / C ++ компилятор поддерживает команду #pragma pack. Это позволит вам настроить упаковку и выравнивание данных.

Например:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

Теперь у меня должна быть длина 11. Без прагмы я мог бы быть чем угодно от 11 до 14 (а для некоторых систем до 32), в зависимости от упаковки компилятора по умолчанию.

person sid1138    schedule 10.06.2015
comment
Здесь обсуждаются последствия заполнения структуры, но это не дает ответа на вопрос. - person Keith Thompson; 10.06.2015
comment
... из-за того, что называется упаковкой. ... - Я думаю, вы имеете в виду отступы. Предпочтительный размер большинства современных процессоров, если 32 бита (4 байта) - это немного упрощение. Обычно поддерживаются размеры 8, 16, 32 и 64 бит; часто каждый размер имеет свое выравнивание. И я не уверен, что ваш ответ добавляет новую информацию, которой еще нет в принятом ответе. - person Keith Thompson; 12.06.2015
comment
Когда я сказал об упаковке, я имел в виду, как компилятор упаковывает данные в структуру (и он может делать это, заполняя небольшие элементы, но это не нужно для заполнения, но он всегда упаковывает). Что касается размера - я говорил об архитектуре системы, а не о том, что система будет поддерживать для доступа к данным (что сильно отличается от базовой архитектуры шины). Что касается вашего последнего комментария, я дал упрощенное и расширенное объяснение одного аспекта компромисса (скорость по сравнению с размером) - основной проблемы программирования. Я также описываю способ решения проблемы - этого не было в принятом ответе. - person sid1138; 13.06.2015
comment
Упаковка в этом контексте обычно относится к более жесткому распределению элементов, чем по умолчанию, как в случае с #pragma pack. Если члены выделяются с выравниванием по умолчанию, я бы обычно сказал, что структура не упакована. - person Keith Thompson; 13.06.2015
comment
Упаковка - это своего рода перегруженный термин. Это означает, как вы помещаете элементы структуры в память. Аналогично смыслу складывания предметов в коробку (упаковка для перемещения). Это также означает размещение элементов в памяти без заполнения (что-то вроде сокращения для плотно упакованных). Затем есть командная версия слова в команде #pragma pack. - person sid1138; 14.06.2015

Это возможно, если вы явно или неявно установили выравнивание структуры. Структура, выровненная по 4 байта, всегда будет кратна 4 байтам, даже если размер ее членов не кратен 4 байтам.

Также библиотека может быть скомпилирована под x86 с 32-битными целыми числами, и вы, возможно, сравниваете ее компоненты в 64-битном процессе, и это дало бы вам другой результат, если бы вы делали это вручную.

person Orion Adrian    schedule 23.09.2008

Стандартный черновик C99 N1256

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4 Оператор sizeof:

3 При применении к операнду, имеющему тип структуры или объединения, результатом является общее количество байтов в таком объекте, включая внутреннее и завершающее заполнение.

6.7.2.1 Спецификаторы структуры и объединения:

13 ... Внутри объекта структуры может быть безымянное заполнение, но не в его начале.

а также:

15 В конце структуры или объединения может быть безымянный отступ.

Новая функция C99 гибкий элемент массива (struct S {int is[];};) также может влиять на заполнение:

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

В Приложении J «Проблемы переносимости» повторяется:

Следующее не указано: ...

  • Значение байтов заполнения при сохранении значений в структурах или объединениях (6.2.6.1)

Проект стандарта C ++ 11 N3337

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3 Размер:

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

9.2 Члены класса:

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

Я знаю C ++ только для того, чтобы понять примечание :-)

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 04.05.2016

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

person JohnMcG    schedule 23.09.2008
comment
Не совсем. В типичных реализациях к структуре добавляется vtable pointer. - person Don Wakefield; 18.10.2008

Язык C оставляет компилятору некоторую свободу в расположении структурных элементов в памяти:

  • дыры в памяти могут появиться между любыми двумя компонентами и после последнего компонента. Это было связано с тем, что определенные типы объектов на целевом компьютере могут быть ограничены границами адресации.
  • Размер "дыр в памяти" включается в результат оператора sizeof. Размер только не включает размер гибкого массива, который доступен в C / C ++.
  • Некоторые реализации языка позволяют управлять размещением структур в памяти с помощью прагмы и параметров компилятора.

Язык C дает некоторую уверенность программисту в расположении элементов в структуре:

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

Проблемы, связанные с выравниванием элементов:

  • Разные компьютеры по-разному выравнивают края объектов
  • Различные ограничения на ширину битового поля
  • Компьютеры различаются тем, как хранить байты одним словом (Intel 80x86 и Motorola 68000)

Как работает выравнивание:

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

p.s Более подробная информация доступна здесь: «Сэмюэл П. Харбисон, Гай Стил C A Reference, (5.6.2 - 5.6.7)»

person bruziuz    schedule 28.07.2015

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

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

Архитектура x86 всегда могла получать несовпадающие адреса. Однако это медленнее, и когда несовпадение перекрывает две разные строки кэша, тогда оно вытесняет две строки кеша, тогда как выровненный доступ вытесняет только одну.

Некоторым архитектурам на самом деле приходится улавливать несогласованные операции чтения и записи, а ранние версии архитектуры ARM (той, которая эволюционировала во все современные мобильные процессоры) ... ну, на самом деле они просто возвращали для них неверные данные. (Они проигнорировали младшие биты.)

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

TL; DR: выравнивание важно.

person DigitalRoss    schedule 24.02.2016

Среди других хорошо объясненных ответов о выравнивании памяти и заполнении / упаковке структуры есть кое-что, что я обнаружил в самом вопросе, внимательно прочитав его.

Почему sizeof для структуры не равно сумме sizeof каждого члена?

Почему оператор sizeof возвращает размер структуры больше, чем общие размеры ее членов?

Оба вопроса предполагают нечто совершенно неправильное. По крайней мере, в общем, не ориентированном на примеры представлении, как здесь.

Результат применения операнда sizeof к объекту структуры может быть равен сумме sizeof, примененной к каждому члену в отдельности. Он не должен быть больше / отличаться.

Если нет причин для заполнения, память не будет заполнена.


Одна из самых реализаций, если структура содержит только члены одного типа:

struct foo {
   int a;   
   int b;
   int c;     
} bar;

Предполагая sizeof(int) == 4, размер структуры bar будет равен сумме размеров всех элементов вместе sizeof(bar) == 12. Здесь нет отступов.

То же самое, например, здесь:

struct foo {
   short int a;   
   short int b;
   int c;     
} bar;

Предполагая sizeof(short int) == 2 и sizeof(int) == 4. Сумма выделенных байтов для a и b равна байтам, выделенным для c, самого большого члена, и с этим все идеально выровнено. Таким образом, sizeof(bar) == 8.

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

person RobertS supports Monica Cellio    schedule 29.06.2020
comment
Если нет причин для заполнения, память не будет заполнена. Это бесполезно и вводит в заблуждение. В языке есть определение, и оно не основано на нем. Он принадлежит к разделу типовых / гипотетических реализаций. (Который у вас есть). И тогда это тавтология. (Я понимаю, что это может быть риторическим). - person philipxy; 12.10.2020