Почему bool и _Bool могут хранить только 0 или 1, если они занимают 1 байт в памяти? - PullRequest
2 голосов
/ 22 января 2020

Я прочитал ответы на этот вопрос: Почему char и bool одинакового размера в c ++? и провел эксперимент, чтобы определить размер выделенных байтов в памяти _Bool и bool (я знаю, что bool - это макрос для _Bool в stdbool.h, но для полноты я тоже его использовал) объект в C, а также объект bool в C ++ на моем реализация Linux Ubuntu 12.4:

Для C:

#include <stdio.h>
#include <stdbool.h>   // for "bool" macro.

int main()
{
    _Bool bin1 = 1;
    bool bin2 = 1; // just for the sake of completeness; bool is a macro for _Bool.    

    printf("the size of bin1 in bytes is: %lu \n",(sizeof(bin1)));
    printf("the size of bin2 in bytes is: %lu \n",(sizeof(bin2)));  

    return 0;
}

Вывод:

the size of bin1 in bytes is: 1
the size of bin2 in bytes is: 1

Для C ++:

#include <iostream>

int main()
{
    bool bin = 1;

    std::cout << "the size of bin in bytes is: " << sizeof(bin);

    return 0;
}

Вывод:

the size of bin in bytes is: 1 

Итак, объекты логического типа, независимо от того, что конкретно C или C ++, занимают 1 байт (8 бит) в памяти, а не только 1 бит.

My вопрос:

  • Почему объекты типов bool и _Bool в C и bool в C ++ могут хранить только значения 0 или 1, если они занимают 1 байт в памяти, которая может содержать 256 значений?

Конечно, их целью является представление только значений 0 и 1 или true и false, но какая единица или макрос решает, что он может хранить только 0 или 1?

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

  • И что произойдет, если значение логического типа * случайно будет изменено в памяти на большее значение так как он может храниться в памяти таким образом?

* С случайно Я имею в виду либо: Изменено «Не обнаруживаемые средства» - Что такое «необнаружимые средства» и как Могут ли они изменить объекты программы на C / C ++? или неправильное назначение fe bool a; a = 25;.

Ответы [ 5 ]

5 голосов
/ 22 января 2020

Язык C ограничивает то, что может храниться в _Bool, даже если он способен хранить другие значения, кроме 0 и 1.

Раздел 6.3.1.2 C standard говорит следующее относительно преобразований в _Bool:

Когда любое скалярное значение преобразуется в _Bool, результат равен 0, если значение сравнивается равным 0; в противном случае результат равен 1.

Стандарт C ++ 17 имеет аналогичный язык в разделе 7.14:

Значение арифметики c перечисление с незаданной областью, указатель или указатель на тип элемента могут быть преобразованы в значение типа bool. Нулевое значение, нулевое значение указателя или нулевое значение указателя члена преобразуется в ложь; любое другое значение преобразуется в true. Для прямой инициализации (11.6) значение типа std::nullptr_t может быть преобразовано в значение типа bool; результирующее значение равно false.

Так что даже если вы попытаетесь присвоить какое-то другое значение для _Bool, язык преобразует значение либо в 0, либо в 1 для C и в true или false для C ++. Если вы попытаетесь обойти это, записав _Bool через указатель на другой тип, вы вызываете неопределенное поведение .

3 голосов
/ 22 января 2020

Ответ для C ++:

Итак, объекты логического типа, независимо от конкретно C или C ++, занимают 1 байт (8 бит) в памяти, а не только 1 бит.

Это просто потому, что фундаментальной единицей хранения в модели памяти C ++ является байт .

Почему создаются объекты типа [...] bool в C ++ может хранить только значения 0 или 1, если они занимают 1 байт в памяти, которая может содержать 256 значений?

Но какой модуль или макрос решает, что он может хранить только 0 или 1?

Предположение здесь неверное. В C ++ bool не содержит 0 или 1, он содержит false или true: http://eel.is/c++draft/basic.fundamental#10.

Как эти два значения представлены в памяти находится до реализации . Реализация может использовать 0 и 1, или 0 и 255, или 0 и <any nonzero value>, или все, что она действительно хочет. Вы не гарантированно найдете 0 или 1 при проверке памяти bool, потому что ...

  • Если вы «назначаете», например, целое число или указатель в bool, он неявно преобразуется в true или false в соответствии с обычными правилами: http://eel.is/c++draft/conv.bool#1

  • Если вы "прочитали "целое число из числа bool, оно неявно преобразуется в 0, если оно содержит значение false или 1, если оно содержит значение true: http://eel.is/c++draft/conv.prom#6

Работа компилятора состоит в том, чтобы гарантировать, что вышеупомянутые две вещи выполняются независимо от того, как значения bool представлены в памяти. Помните, что C ++ указан на абстрактной машине, и ваша программа должна вести себя , как если бы выполнялась на абстрактной машине.

И что будет, если значение логического значения тип случайно изменен в памяти на большее значение?

Неопределенное поведение. См. Один из них:

1 голос
/ 22 января 2020

Почему объекты типов bool и _Bool в C и bool в C ++ могут хранить только значения 0 или 1, если они занимают 1 байт в памяти, которая может содержать 256 значений?

Если bool может хранить весь диапазон значений char, то почему бы просто не использовать char?


Конечно, их целью является представление только значения 0 и 1 или true и false, но какой модуль или макрос решает, что он может хранить только 0 или 1?

Компилятор будет обрабатывать преобразование, когда вы присваиваете значение bool переменная. Если это правда, то переменная будет содержать true. Такое поведение было определено в стандартах C и C ++. Это означает, что bool a; a = 25; полностью действителен, а не "неуместное назначение" , как вы. После этого a всегда будет содержать true / 1. Вы никогда не сможете установить bool на что-либо кроме 0 и 1 с помощью обычного присваивания переменной

Нет проблем с использованием char или int в качестве bool, как это было до современных C и C ++, но путем ограничения Диапазон значений также позволяет компилятору выполнять множество оптимизаций. Например, bool x = !y; будет выполнено с помощью простой инструкции XOR, которая не будет работать, если y содержит какие-либо значения, отличные от 0 и 1. Если y является нормальным целочисленным типом, вам нужно сначала нормализовать y до 0 и 1. См. demo

На самом деле, не все биты в представлении должны участвовать в вычислении значения, и не все битовые комбинации должны быть действительными. C и C ++ позволяют типам содержать биты заполнения и представления прерываний , поэтому 32-битный тип может иметь только 30 битов значения или может хранить только 2 32 -4 разные значения. Это не означает, что bool определенно содержит биты заполнения, просто доказательство того, что вам разрешено иметь тип, более узкий, чем возможный диапазон

Единственное исключение, о котором мы знаем, это _Bool (как наблюдается Джозефом Майерсом в отношении G CC). Кажется, что можно (а) принять не {0,1} значения, чтобы быть представлениями ловушек в текущем смысле, или (b) рассматривать операции с не {0,1} значениями этого типа как выдачу неопределенного значения. Последнее связывало бы возможное неправильное поведение, которое было бы хорошо для программистов; единственный возможный недостаток, о котором мы знаем, это то, что он может ограничить компиляцию с помощью вычисляемых таблиц ветвлений, проиндексированных по непроверенным значениям _Bool.

N2091: уточнение представлений ловушек (предварительный отчет о дефектах или предложение для C2x)

Однако некоторые реализации считают их представлениями ловушек

Фактически, как реализовано в G CC и Clang, тип _Bool имеет два значения и ловушку 254 представления.

Представления ловушек и биты заполнения - Pascal Cuoq


И что произойдет, если значение логического типа случайно изменяется в памяти на большее значение?

Если вы манипулируете значением bool для другого значения напрямую через указатель, то в C ++ неопределенное поведение произойдет

6.9.1 Фундаментальные типы

Значения типа bool либо true, либо false. 50 [ Примечание: Нет типов или значений типа подпись, без знака, коротких или длинных значений. - конец примечания ] Значения типа bool участвуют в интегральных повышениях (7.6).

50) Использование значения bool способами, описанными в этом международном стандарте как «неопределенные» », Например, путем проверки значения неинициализированного автоматического объекта c, он может вести себя так, как если бы он не был ни истинным, ни ложным.

C ++ 17

Не удалось найти ссылку в C99 , но поведение будет неопределенным, если заданное вами значение будет представлением прерывания

6.2.6 Представления типы

Определенные представления объектов не обязательно должны представлять значение типа объекта. Если сохраненное значение объекта имеет такое представление и читается выражением lvalue, которое не имеет символьного типа, поведение не определено. Если такое представление создается побочным эффектом, который изменяет весь или любую часть объекта выражением lvalue, которое не имеет символьного типа, поведение не определено. 41) Такое представление называется представление ловушек .

Уже есть много вопросов относительно этого "странного" поведения

1 голос
/ 22 января 2020

Почему объекты типов bool и _Bool в C и bool в C ++ могут хранить только значения 0 или 1, если они занимают 1 байт в памяти, которая может содержать 256 значений?

Потому что, в конце концов, спецификация языка не говорит о том, насколько велик bool, она только определяет, что он может делать. В спецификации языка C сказано, что _Bool может содержать 0 или 1. Размер типа данных bool является подробным описанием отдельных реализаций, а не частью самой спецификации. Возможно иметь реализацию, которая фактически выделяет отдельные биты для bool, можно иметь спецификацию, которая выделяет несколько байтов для bool. Поэтому, чтобы оставаться в соответствии со спецификацией, важной частью является не размер выделенной памяти, а то, что она работает в соответствии со спецификацией, что означает, что она содержит 0 или 1.

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

Неопределенное поведение, которое я ожидаю. Я не думаю, что спецификация говорит, что происходит, и в результате то, что происходит, зависит от разработчика. Одна реализация может исследовать первый бит основной памяти и игнорировать остальные. Другая реализация может исследовать всю базовую область памяти и, если какой-либо из битов установлен, дать значение 1.

Слово предостережения ...

Вы можете написать программу, чтобы увидеть, что ваша реализация делает с такими данными, и написать программы, которые будут работать для вашей реализации, но знаете, что вы не тестируете то, что делает 'C', вы тестируете то, что будет делать эта конкретная реализация / компилятор делать. Кроме того, знайте, что, как только вы начинаете наступать в воды неопределенного поведения, вы также начинаете наступать в воды вещей, которые нарушают программы по причинам, которые вы, возможно, не понимаете. Компиляторы будут применять широкий спектр оптимизаций на основе ряда допущений. Компилятор может написать программу, которая прекрасно работает, когда вы выполняете кучу работы, вы заканчиваете sh это, вы говорите компилятору создать оптимизированную версию релиза, и поскольку вы копаетесь в неопределенном поведении, вы сломали Предположение, что компилятор сделал это, и он может применить оптимизацию, которая внезапно сломает ваш код, и отслеживание его может оказаться чрезвычайно трудным. Всегда старайтесь придерживаться четко определенного поведения.

1 голос
/ 22 января 2020

(отвечая на C.)

Но какой модуль или макрос решает, что он может хранить только 0 или 1?

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

А что будет, если значение логического типа случайно будет изменено в памяти на большее значение?

Это зависит от реализации C. Большее значение может рассматриваться как 1, если реализация тестирует ноль, а не ноль. Большее значение может быть обработано в соответствии с его младшим битом, если реализация использует это. Большее значение может вести себя по-разному в разных обстоятельствах, если реализация использует различные инструкции в зависимости от обстоятельств. Большее значение также может привести к результатам, которые в противном случае были бы бессмысленными. Например, учитывая int x = 4; и некоторые _Bool y, которые были ненадлежащим образом изменены путем записи в его память, int z = x + y; может установить z на 10, даже если только 4 или 5 были бы возможны, если y было бы правильным _Bool. Когда вы изменяете представление типа на нечто, отличное от битов, которые представляют правильное значение, как определено реализацией, результирующее поведение не определяется стандартом C или, как правило, реализацией C.

Возможно ли и допустимо ли присвоить большее значение логическому типу?

Нет, присваивания преобразуют правый операнд в тип выражения присваивания (который является тип левого операнда, кроме как значение, а не значение l).

...