Защо sizeof за структура не е равен на сумата от sizeof на всеки член?

Защо операторът sizeof връща размер, по-голям за структура от общите размери на членовете на структурата?


person Kevin    schedule 23.09.2008    source източник
comment
Вижте този C ЧЗВ относно подравняването на паметта. c-faq.com/struct/align.esr.html   -  person Richard Chambers    schedule 26.04.2013
comment
Анекдот: Имаше действителен компютърен вирус, който постави кода си в struct paddings в хост програмата.   -  person Elazar    schedule 02.09.2013
comment
@Elazar Това е впечатляващо! Никога не бих си помислил, че е възможно да използвам такива малки площи за нещо. Можете ли да предоставите повече подробности?   -  person OmarL    schedule 14.11.2016
comment
@Wilson - Сигурен съм, че включваше много jmp.   -  person hoodaticus    schedule 28.06.2017
comment
Вижте структура padding, packing: The Lost Art of C Structure Packing Eric S. Raymond   -  person EsmaeelE    schedule 09.12.2017
comment
SO, answer, Geeks4Geeks, друга връзка   -  person EsmaeelE    schedule 09.12.2017
comment
Съществува ли подложка и между 2 структура, за да може първият член на следващата структура да започва от подравнен адрес?   -  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 000 гледания никой не забеляза, че този въпрос всъщност е абсолютно грешен.   -  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 байт, няма начин да бъде неподравнен. И така, стандартът гарантира подравняване, ако всички членове на структурата са char БЕЗ НИКАКВО ПОДПЪЛЖАВАНЕ. - 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 има 1 байт подложка след char, за да се гарантира, че 2 байта short започва на граница от 2 байта. Тъй като компилаторът не може да знае какво може да има след дадена структура в паметта (а това може да са много различни неща), той се подготвя за най-лошото и вмъква достатъчно подпълване, за да направи структурата кратна на 4 байта. X се нуждае от 3 байта, за да стигне до 12, Y се нуждае само от 1 за 8. - person 8bittree; 17.02.2017
comment
x86 чиповете имат хардуерна поддръжка за неподравнен достъп Вярно. x86 чиповете не издават грешки при подравняване на данни False. Зависи от инструкцията, по-специално SSE инструкциите са склонни да дават грешка при неправилно подравняване (с изключение на специалните неподравнени варианти). - person Ben Voigt; 25.05.2017
comment
Съществува ли подложка и между 2 структура, за да може първият член на следващата структура да започва от подравнен адрес? - person AlphaGoku; 07.02.2018
comment
@AkshayImmanuelD: Не между структурите, не, това е част от края на структурата. struct {long long a; char b;} обикновено има 7 байта запълване в края след b, което го прави общо 16 байта. (на повечето 64-битови архитектури дяда дяда) - 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 модеми, WiFi AP работят с Linux, BSD или подобна операционна система на борда, на MIPS, ARM или подобно семейство. Това е същото като микроконтролер в USB памет или USB клавиатура. Ако преброим тези, един среден смартфон съдържа около дузина и половина процесори. Това са различни инструменти за кодиране на контролери, дори и да изглеждат като разновидност на C или C++, те не са ISO. - person Swift - Friday Pie; 20.06.2021

Пакетиране и подравняване на байтове, както е описано в често задаваните въпроси за C тук:

Това е за подравняване. Много процесори не могат да получат достъп до 2- и 4-байтови количества (напр. int и long int), ако са натъпкани по всякакъв начин.

Да предположим, че имате тази структура:

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
@YoYoYonnY това не е възможно. На компилатора не е разрешено да пренарежда членовете на структурата, въпреки че gcc има експериментална опция за това - person phuclv; 02.03.2017
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 например, използвайте __attribute__((packed)).

В Windows можете да зададете подравняването на един байт, когато използвате компилатора cl.exe с /Zp опция.

Обикновено за процесора е по-лесно да има достъп до данни, които са кратни на 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 CPU, работещ в реален режим (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 - имайте предвид, че най-ниските 2 бита са нула, така че данните са подравнени към 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%. Подплънките разменят ефективността на паметта за ефективност на обработката. Като се има предвид, че компютрите могат да имат огромни количества памет (много гигабайти), компилаторите смятат, че размяната (скорост над размера) е разумна.

За съжаление, този проблем се превръща в убиец, когато се опитате да изпратите структури през мрежа или дори да запишете двоичните данни в двоичен файл. Подложката, вмъкната между елементи на структура или клас, може да наруши данните, изпратени до файла или мрежата. За да напишете преносим код (този, който ще отиде в няколко различни компилатора), вероятно ще трябва да имате достъп до всеки елемент от структурата поотделно, за да осигурите правилното "опаковане".

От друга страна, различните компилатори имат различни способности да управляват опаковането на структурата на данните. Например във 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-битови int и може да сравнявате нейните компоненти на 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 Sizeof:

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

9.2 Членове на класа:

Указател към структурен обект със стандартно оформление, подходящо преобразуван с помощта на reinterpret_cast, сочи към неговия първоначален член (или ако този член е битово поле, тогава към единицата, в която се намира) и обратно. [ Бележка: Следователно може да има неименувано подпълване в структурен обект със стандартно оформление, но не и в началото му, както е необходимо за постигане на подходящо подравняване. — крайна бележка]

Знам само достатъчно C++, за да разбера бележката :-)

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

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

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

Езикът C оставя на компилатора известна свобода относно местоположението на структурните елементи в паметта:

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

Езикът C предоставя известна увереност на програмиста за оформлението на елементите в структурата:

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

Проблеми, свързани с подравняването на елементите:

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

Как работи подравняването:

  • Обемът, зает от структурата, се изчислява като размер на подравнения единичен елемент от масив от такива структури. Структурата трябва да завършва така, че първият елемент от следващата структура да не нарушава изискванията за подравняване

p.s. По-подробна информация е налична тук: „Samuel P.Harbison, Guy L.Steele 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