Тип bool имеет изменчивую историю с множеством несовместимых вариантов языковых сред. Это началось с исторического выбора дизайна, сделанного Деннисом Ричи, парнем, который изобрел язык C. У него не было типа bool , альтернативой была int, где значение 0 представляло ложь, а любое другое значение считалось истинным .
Этот выбор был перенесен в Winapi, основная причина использования pinvoke, у него есть typedef, для BOOL
которого является псевдонимом ключевого слова int компилятора C. Если вы не применяете явный атрибут [MarshalAs], то логическое значение C # преобразуется в BOOL, создавая, таким образом, поле длиной 4 байта.
Что бы вы ни делали, объявление вашей структуры должно соответствовать выбору среды выполнения, сделанному на языке, с которым вы взаимодействуете. Как уже отмечалось, BOOL для winapi, но большинство реализаций C ++ выбирают байт , большинство взаимодействия COM Automation используют VARIANT_BOOL, что является коротким .
Фактический размер C # bool
составляет один байт. Сильная цель разработки CLR - это то, что вы не можете узнать. Макет - это деталь реализации, которая слишком сильно зависит от процессора. Процессоры очень придирчивы к типам переменных и их выравниванию, неправильный выбор может существенно повлиять на производительность и вызвать ошибки времени выполнения. Делая макет недоступным для обнаружения, .NET может предоставить универсальную систему типов, не зависящую от фактической реализации среды выполнения.
Другими словами, вы всегда должны маршалировать структуру во время выполнения, чтобы закрепить макет. На этом этапе выполняется преобразование из внутреннего макета в макет взаимодействия. Это может быть очень быстро, если макет идентичен, и медленным, когда поля необходимо переупорядочить, поскольку для этого всегда требуется создание копии структуры. Технический термин для этого - непреобразуемый , передача непреобразуемой структуры в собственный код происходит быстро, потому что маршаллер pinvoke может просто передать указатель.
Производительность также является основной причиной, по которой логическое значение не является единичным битом. Есть несколько процессоров, которые обеспечивают прямую адресацию битов, наименьшая единица - байт. Для извлечения бита из байта требуется дополнительная инструкция, которая предоставляется не бесплатно. И он никогда не бывает атомным.
В остальном компилятор C # не стесняется говорить вам, что он занимает 1 байт, используйте sizeof(bool)
. Это все еще не фантастический предсказатель того, сколько байтов занимает поле во время выполнения, CLR также должна реализовывать модель памяти .NET, и она обещает, что простые обновления переменных являются атомарными . Это требует, чтобы переменные были правильно выровнены в памяти, чтобы процессор мог обновлять их за один цикл шины памяти. Довольно часто из-за этого для bool требуется 4 или 8 байтов в памяти. Дополнительный отступ, который был добавлен для обеспечения правильного выравнивания следующего члена.
CLR фактически использует то, что макет не может быть обнаружен, он может оптимизировать макет класса и переупорядочивать поля, чтобы минимизировать отступы. Итак, скажем, если у вас есть класс с членом bool + int + bool, тогда он займет 1 + (3) + 4 + 1 + (3) байта памяти, (3) - это заполнение, всего 12 байтов. 50% отходов. Автоматическая компоновка изменяется на 1 + 1 + (2) + 4 = 8 байт. Только класс имеет автоматическую компоновку, структуры по умолчанию имеют последовательную компоновку.
Еще более мрачно то, что для bool может потребоваться до 32 байтов в программе C ++, скомпилированной с помощью современного компилятора C ++, который поддерживает набор инструкций AVX. Что требует 32-байтового выравнивания, переменная bool может содержать 31 байт заполнения. Также основная причина, по которой джиттер .NET не испускает инструкции SIMD, если явно не завернут, он не может получить гарантию выравнивания.