размер структуры с битовыми полями, не выровненными по границе байта

это моя программа

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    struct bitfield {
         unsigned a:3;
         char b;
         unsigned c:5;
         int d;
    }bit;

    printf("%lu \n",sizeof(bit));
    return 0;
}

Я ожидал, что размер этой структуры будет довольно большим, но на моей машине он оказался равным 8, потому что unsigned занимает 4 байта. Теперь причина, по которой я ожидал большего, заключалась в том, что я ожидал, что char b будет на границе байта, чтобы он правильно выровнялся в памяти. Теперь я предполагаю, что компилятор помещает a, b, c, все в эти 4 байта. Я новичок в C, поэтому, пожалуйста, потерпите меня. Является ли мое предположение, что все остальные типы данных, кроме битовых полей, обязательно должны быть по байту неверными? Если это правильно, я ожидаю, что a возьмет целое число без знака, а b возьмет байт, а затем заполнение 3 байтами и так далее. Что мне здесь не хватает?


person user3491702    schedule 30.07.2017    source источник
comment
Возможно, это отвечает на ваш вопрос: Как размер структуры с определенными/измеренными битовыми полями?   -  person Luboš Hemala    schedule 30.07.2017
comment
Нет, это не так. Я знаю, как измеряются размеры битового поля. Мой вопрос в том, если у меня есть char между ними, как они тогда измеряются   -  person user3491702    schedule 30.07.2017
comment
Проще говоря, держитесь как можно дальше от битовых полей. В мире нет ни одной причины их использовать. Сказав это, почему вы ожидаете, что a будет занимать то же место, что и целое число без знака? Это битовое поле, оно занимает 3 бита вне зависимости от того, что стоит перед ним или после него.   -  person n. 1.8e9-where's-my-share m.    schedule 30.07.2017
comment
Сделайте тест, насколько велика структура из 4 полей символов. Или один символ. Я думаю, что это 1 байт. Так что они подошли бы в вашем порядке (3+8+5 ‹ 32)   -  person Luboš Hemala    schedule 30.07.2017
comment
@н.м. В идеале я бы не ожидал, что a будет занимать то же пространство, что и весь беззнаковый int. У меня вопрос, как они будут располагаться в памяти, если у нас есть битовое поле a, а потом char b. Куда идет char b? Это идет в тех оставшихся битах от? Если нет, то как вы оправдываете размер структуры, которую я получаю?   -  person user3491702    schedule 30.07.2017
comment
@LubosHemala Итак, вы говорите, что char b входит в оставшиеся биты от a?   -  person user3491702    schedule 30.07.2017
comment
В идеале я бы не ожидал, что a будет занимать то же пространство, что и весь беззнаковый int. Ваши ожидания не основаны ни на экспериментах, ни на стандартах, так что просто отбросьте их и примите факт. a занимает 3 бита. Вот что говорит :3 в своем определении. b не имеет на это никакого влияния. Он переходит к следующему байту, в котором нет битов, занятых a.   -  person n. 1.8e9-where's-my-share m.    schedule 30.07.2017
comment
@н.м. Вам нужно расслабиться и терпеть людей, которые не понимают, что такое тип данных. Если нет, то эта платформа не для вас.   -  person user3491702    schedule 30.07.2017
comment
@user3491702 user3491702 Вы прочитали все ответы в моей предыдущей ссылке? В любом случае, взгляните на выравнивание структуры данных, возможно, это прояснит ситуацию. Я не уверен, что выравнивание байтов всегда применяется, когда вы используете битовые поля. Компилятор использует по умолчанию 32- или 64-битные поля в зависимости от вашей архитектуры, в которую он пытается упаковать как можно больше.   -  person Luboš Hemala    schedule 30.07.2017
comment
sizeof никак не связано с выравниванием по стандарту. Байт является минимальной единицей измерения для обоих (1). Объект не может быть не выровнен по байту.   -  person too honest for this site    schedule 30.07.2017


Ответы (2)


Кажется, я не понимаю конфликта.

у вас в основном есть:

struct bitfield {
     unsigned a:3;
     unsigned padding1:5;
     char b;
     unsigned c:5;
     unsigned padding2:3;
     unsigned padding3:8;
     int d;
}bit;

a находится на границе байта 1, он использует три бита + 5 бит заполнения (потому что больше нет битовых полей, чтобы использовать оставшиеся биты).

b находится на границе байта 2, он использует целый байт.

c находится на границе байта 3, он использует пять битов + 3 бита заполнения (потому что больше нет битовых полей, чтобы использовать оставшиеся биты).

- int padding приходит сюда -

d находится на границе int (4 байта на вашем компьютере). Он использует 1 байт для заполнения + 4 байта для данных.

Все вместе, 8 байт...

... хотя, как указал @ JonathanLeffler в комментариях, это зависит от реализации и не означает, что все компиляторы будут вести себя одинаково.

person Myst    schedule 30.07.2017
comment
Спасибо Мист. Как раз то объяснение, которое я искал. Извините, я новичок в этом языке, поэтому я все еще изучаю основы типов данных. - person user3491702; 30.07.2017
comment
Путаница, вероятно, возникает из-за того, что компилятору разрешено, но не требуется использовать тип (unsigned в этом примере) для хранения битового поля, определенного как относящегося к этому типу, и, следовательно, использовать 4 байта для первого битового поля. поле, 1 байт для char, 3 байта заполнения для выравнивания следующего битового поля по 4-байтовой границе, 4 байта для второго битового поля и 4 байта для int, всего 16 байтов. Все это зависит от реализации, как и почти все, что связано с битовыми полями. - person Jonathan Leffler; 30.07.2017
comment
@JonathanLeffler Когда я прочитал вопрос, мои расчеты сказали мне, что он должен быть 16 байт. Я думаю, что больше я читаю о битовых полях и объединениях, и о порядке следования байтов, больше я понимаю, насколько они специфичны для реализации. - person user3491702; 30.07.2017
comment
@ user3491702: Если вам нужен определенный макет, struct не подходит. Битовое поле struct просто добавляет дополнительные варианты. Используйте массив uint8_t или другого типа с фиксированной шириной для таких данных и битов/сдвигов для сортировки. - person too honest for this site; 30.07.2017
comment
@ Олаф, спасибо за предложение. Я обязательно посмотрю на это. Здесь я не пытался получить конкретное расположение, но пытался понять его. - person user3491702; 30.07.2017
comment
Спасибо @JonathanLeffler, я добавил комментарий об этом в ответ. - person Myst; 30.07.2017

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

Я проверил ваш код с некоторыми значениями и запустил программу в gdb.

bit.a = 1;
bit.b = 'a';
bit.c = 4;
bit.d = 1337;

При печати памяти в gdb вывод выглядит так

(gdb) x/8b &bit
0x7fffffffe118: 00000001    01100001    00000100    00000000
                00111001    00000101    00000000    00000000

Итак, мы видим, что первый байт из поля a полностью используется, хотя использует только 3 бита. Символ a в поле b также занимает один байт. Третий байт (00000100) соответствует значению 4 поля c, и вот что интересно:

Затем у вас есть целое число, которое использует 4 байта (в выводе gdb над последними 4 байтами, нижняя строка), но там есть дополнительный нулевой байт. Это обычная практика для компиляторов, поскольку они пытаются оптимизировать память.

person CRoemheld    schedule 30.07.2017