хранение bool в c под разными компиляторами и уровнями оптимизации

тривиальный пример программы:

#include <stdio.h>
main()
{
    bool tim = true;
    bool rob = false;
    bool mike = true;

    printf("%d, %d, %d\n", tim, rob, mike);

}

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

0x4004fc <main()+8>          movb      $0x1,-0x3(%rbp)
0x400500 <main()+12>         movb      $0x0,-0x2(%rbp)
0x400504 <main()+16>         movb      $0x1,-0x1(%rbp)

если, однако, включить оптимизацию, существует ли уровень оптимизации, который заставит gcc хранить эти логические значения как биты в байте, или нужно будет поместить логические значения в объединение некоторых логических значений и короткого целого? Другие компиляторы? Я пробовал '-Os', но должен признать, что не могу разобраться в дизассемблированном выводе.


person tallen    schedule 26.07.2013    source источник
comment
Нет. Наименьшая адресная единица в C — это байт, поэтому у нас не может быть реального типа данных bool.   -  person    schedule 26.07.2013
comment
Если объем памяти ограничен, вы можете хранить эти три бита в одном символе, используя битовые маски.   -  person jsp    schedule 26.07.2013
comment
@ H2CO3: как насчет битовых полей? в любом случае, вопрос был о внутренних представлениях, поэтому мы не можем иметь bool в битах, если компилятор так решит (возможно, за счет дополнительного связующего кода)? или текущий стандарт запрещает это?   -  person collapsar    schedule 26.07.2013
comment
@collapsar: Да и нет; см. мой ответ. Но эта оптимизация сэкономит 2 байта за счет (вероятно) нескольких дополнительных инструкций для доступа к отдельным битам. Это плохой компромисс, если только хранение данных не очень дорого по сравнению с размером кода (как это может быть в некоторых небольших встроенных системах).   -  person Keith Thompson    schedule 26.07.2013
comment
@KeithThompson: ты подтвердил мои догадки. Спасибо.   -  person collapsar    schedule 26.07.2013


Ответы (3)


Хороший ответ @Keith Thompson может объяснить, что произошло с примером кода в вопросе. Но я предполагаю, что компилятор не преобразует программу. По стандарту bool (макрос в stdbool.h такой же, как и ключевое слово _Bool) должен иметь размер один байт.

C99 6.2.6.1 Общие

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

Это означает, что любой тип (кроме битовых полей, в том числе bool) объектов должен иметь минимум один байт.

C99 6.3.1.1 Логические значения, символы и целые числа

Ранг of_Bool должен быть меньше ранга всех других стандартных целочисленных типов.

Это означает, что размер bool не превышает char (целочисленный тип). И мы также знаем, что размер char гарантированно равен одному байту. Таким образом, размер bool должен быть не более одного байта.

Вывод: размер bool должен быть один байт.

person Yu Hao    schedule 26.07.2013
comment
аааа, прочитайте источник (документ), Люк - я должен был прочитать стандартный документ C99 - person tallen; 26.07.2013
comment
Я думаю, что вы пропускаете шаг там. Как тот факт, что _Bool имеет ранг ниже, чем char, означает, что это может быть только один байт? Почему _Bool не может иметь, скажем, 15 или 31 бит заполнения? Может быть преимущество в производительности при хранении _Bool в одном слове, а не в одном байте - каким бы ни было слово на данной машине. - person Keith Thompson; 26.07.2013
comment
@KeithThompson Подумав, я думаю, вы правы, более низкий ранг _Bool не означает порядок размера. Я неправильно понял это. Я удалю этот ответ, пока OP не примет его. - person Yu Hao; 26.07.2013

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

Эта программа:

#include <stdio.h>
#include <stdbool.h>
int main(void)
{
    bool tim = true;
    bool rob = false;
    bool mike = true;

    printf("%d, %d, %d\n", tim, rob, mike);

}

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

#include <stdio.h>
int main(void)
{
    puts("1, 0, 1");
}

Таким образом, три объекта bool не просто хранятся в отдельных битах, они вообще не хранятся.

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

Но такая агрессивная оптимизация, вероятно, не то, о чем вы спрашиваете.

В «абстрактной машине» объект bool должен быть не менее одного байта, если только это не битовое поле. Объект bool или любой объект, кроме битового поля, должен иметь уникальный адрес и размер, кратный 1 байту. Если вы напечатаете значение sizeof (bool) или sizeof tim, результатом будет как минимум 1. Если вы напечатаете адреса трех объектов, они будут уникальными и разделены не менее чем одним байтом.

person Keith Thompson    schedule 26.07.2013
comment
хороший ответ, потому что он точно описывает, что происходит в этом конкретном коде ... использование clang для генерации кода llvm - настоящий победитель в этом вопросе. голосуй за! - person Stefan; 26.07.2013
comment
В вашем использовании абстрактной машины я предполагаю, что мы предполагаем, что нет такой машины, которая может адресовать отдельный бит; поэтому, поскольку байт является минимальной адресуемой единицей, в большинстве случаев логические значения (биты) хранятся как отдельные байты. - person tallen; 26.07.2013
comment
@tallen Нет, выражение «абстрактная машина» относится к формулировке, используемой в стандарте C. Язык C нацелен на так называемую абстрактную машину для описания поведения и обеспечения независимости от платформы. - person ; 26.07.2013
comment
увеличение размера кода, необходимого для доступа к отдельным битам, перевесит любую экономию на размере данных. Разве это не зависит полностью от аппаратного обеспечения процессора и поддерживаемых им кодов операций? - person endolith; 22.06.2017
comment
@endolith: я полагаю, да, но код для доступа к одному биту по сравнению с 8-битным байтом должен указывать 3 дополнительных бита информации, чтобы определить, к какому биту обращаться. Возможно, эти лишние 3 бита можно втиснуть в опкод, но в целом они не бесплатны. - person Keith Thompson; 22.06.2017

Вы бы не использовали объединение логических значений. Вместо этого вы можете сказать

struct a
{
unsigned char tim : 1;
unsigned char rob : 1;
unsigned char mike : 1;
} b;

b.tim=1;
b.rob=0;
b.mike=1;

и все это будет храниться в одном символе. Однако у вас не было бы никаких гарантий относительно того, как он расположен в памяти или как он выровнен.

person jsp    schedule 26.07.2013
comment
Я знал, что есть способы сделать это, например. битовые поля или объединения. Чего я не понял, так это почему компилятор не хочет этого делать. Я думаю, что эта абстрактная концепция машины — это то, что я ищу. - person tallen; 26.07.2013
comment
@tallen Он не хочет этого делать, потому что это сложнее, и если он хочет извлечь логические значения, ему придется выполнять побитовые операции, которые требуют больше инструкций, чем простая загрузка/сохранение байта. C — это все для эффективности и простоты. - person ; 26.07.2013