Размер 64-битной структуры Windows зависит от типа содержащихся данных?

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

struct pix1 { 
    unsigned char r; 
    unsigned char g;
    unsigned char b; 
    unsigned char a; 
    unsigned char y[2]; 
 }; 

struct pix2 { 
    unsigned char r; 
    unsigned char g;
    unsigned char b; 
    unsigned char a; 
    unsigned short y; 
 }; 

Затем я группирую четыре таких пикселя вместе, как таковые:

struct pix4 {
    pix1 pixels[4]; // or pix2 pixels[4]
    unsigned char mask;
};

... но оказывается, что размер такой группировки меняется в соответствии с sizeof(pix4) в зависимости от того, использую ли я pix1 или pix2. Индивидуальный sizeof (pix1) == sizeof (pix2), поэтому я не понимаю, почему группировка квартетов пикселей меняет размер. Меня это волнует, потому что проще писать программы с короткими, чем с двумя беззнаковыми символами, но это стоит мне 0,25 байта на пиксель.

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

Заранее благодарны за Вашу помощь.


person Erika Electra    schedule 05.06.2012    source источник
comment
#pragma pack(1) для получения упаковки с выравниванием по байтам (визуальная студия), тогда вы должны получить одинаковый размер в обоих случаях.   -  person AndersK    schedule 05.06.2012
comment
Правильная инкапсуляция означает, что вам не нужно точное представление, а только предоставляемый интерфейс. Легко иметь геттер и сеттер для y, которые принимают и возвращают uint16_t, даже если внутреннее представление использует два отдельных байта.   -  person Matthieu M.    schedule 05.06.2012
comment
@Matthieu M.: Вы абсолютно правы. Геттеры и сеттеры должны абстрагироваться от доступа к отдельным байтам, но, поскольку я не очень хорошо разбираюсь в различиях в обработке противоположного порядка байтов, я нервничаю, что внесу ошибки в код, если неправильно обработаю порядок следования байтов, вот и все.   -  person Erika Electra    schedule 05.06.2012
comment
@Cindeselia: если вы это сделаете, исправление локализовано для кода получения и установки, это всего две функции :)   -  person Matthieu M.    schedule 05.06.2012


Ответы (3)


Размер структур одинаков, но требования к их выравниванию различны.

Согласованность структуры — это максимальное согласование всех ее членов. Таким образом, pix1 имеет выравнивание 1, потому что оно содержит только символы, а pix2 имеет выравнивание 2 из короткого члена. Выравнивание pix4 затем получает выравнивание от члена pixels, так что это 1 в первом и 2 во втором случае.

Теперь, чтобы убедиться, что все члены массива правильно выровнены, размер структуры округляется до следующего кратного ее выравнивания. В обоих случаях размер pixels равен 24, но тогда есть 1 байт mask. В первом случае выравнивание равно 1, поэтому 25 кратно ему, а sizeof(pix4) равно 25, но во втором выравнивание равно 2, поэтому sizeof(pix4) нужно округлить до следующего четного числа, 26.

Это одинаково на всех платформах.

person Jan Hudec    schedule 05.06.2012

Да, это связано с выравниванием. Компилятор хочет выровнять переменные по естественным границам, поэтому короткое замыкание будет 16 бит (2 байта) с выравниванием. И, следовательно, структура, содержащая шорт, также будет выровнена по 16-битной границе.

person janneb    schedule 05.06.2012
comment
@LuchianGrigore: Эээ, бит, а не байт. Естественная граница == размер шрифта. - person janneb; 05.06.2012
comment
Я никогда не слышал об этом термине. Не могли бы дать ссылку? - person Luchian Grigore; 05.06.2012
comment
@LuchianGrigore: для x86 см., например, intel.com/Assets/PDF/manual/253665.pdf - person janneb; 05.06.2012
comment
Что ж, требования к выравниванию всегда зависят от платформы, и, поскольку ОП спросил о 64-разрядной версии Windows, я предположил, что его интересует x86-64 (не так много пользователей Windows IA-64!). Кроме того, IMHO, выровненное по словам, не описывает всю степень проблемы, поскольку разные типы имеют разные требования к выравниванию - в соответствии с упомянутой вами статьей в Википедии естественная граница == выровнена, неестественная граница == смещена. Кроме того, если вы погуглите выравнивание естественной границы, вы обнаружите, что оно используется и в других местах, например. в документации компилятора XLC для AIX/POWER. - person janneb; 05.06.2012

Тот же эффект наблюдается и в 32-разрядной версии Linux.

Это связано с заполнением по причинам выравнивания.

Если вы используете struct pix1, у вас будут только символы, и поэтому struct pix4 можно оставить как есть. Но если вы используете struct pix2, он содержит шорт. Таким образом, вся структура должна быть выровнена, так что даже в массиве struct pix4 каждый элемент выровнен для чистого доступа к y.

Более подробно: массив strict pix4 с двумя элементами будет иметь следующую форму:

                +-----     [0]     -----+++-----    [1]     -----+
First version:  rgbayyrgbayyrgbayyrgbayyMrgbayyrgbayyrgbayyrgbayyM
Second version: rgbayyrgbayyrgbayyrgbayyM rgbayyrgbayyrgbayyrgbayyM
                ---------25--------------+ <- +1

Почему? Потому что часть ---ed составляет 25 байт, что является нечетным числом. Это не проблема для первой версии — 2-й элемент может спокойно начинаться с нечетного адреса — но для 2-й версии. Там yy всегда должен быть на четном (выровненном) адресе, поэтому struct pix4 составляет 26 байт.

person glglgl    schedule 05.06.2012
comment
всегда должен быть на четном (выровненном) адресе, означает ли это, что он выровнен? - person Luchian Grigore; 05.06.2012
comment
выровненный немного больше: чет просто делится на 2 - что достаточно для short - но в общем случае (например, int64_t и прочее) - четного недостаточно. IOW, термин «выровненный» является более общим. - person glglgl; 05.06.2012