Почему bool весит целый байт?
Ведь для его хранения по идее достаточно одного бита, а он весит как тип данных byte, в который можно запихнуть 255 различных чисел.
- Вопрос задан более трёх лет назад
- 13095 просмотров
Комментировать
Решения вопроса 3
Потому, что архитектура вычислительной системы не способна адресовать БИТ памяти, минимальная единица — байт.
Ответ написан более трёх лет назад
Нравится 5 2 комментария
@pygame Так. Для «специалиста».
Нет ничего общего между приведенными вами командами и адресацией. Повторюсь: минимальная единица памяти, к которой вы можете обратится это байт. Даже если вас интересу какой-то бит, вы должны сначала выделить интересующий вас байт, а потом уже обращаться к битам. Адресовать, непосредственно, биты у вас не получится.
batchar: Это именно железом определено (хранением в банках памяти ОЗУ)?
а где почитать про Хранение в Памяти подробней и чтоб понятно было?
«I’m here to consult you» © Dogbert
Предыдущие ораторы ( @batChar @gleb_kudr ) говорили про адресацию памяти. Я думаю, что они неправы: у нас, например, есть битовые поля, которые, по определению, могут быть отдельными битами, не смотря на то, что «байт — минимальная единица адресации в памяти». Невозможность адресовать отдельные биты памяти — не основная причина.
Проблема не в адресации, а скорее в стандарте: если посмотреть определение оператора sizeof (ISO/IEC 14882:1998, п.5.3.3), то стандарт запрещает применять его к битовым полям, но разрешает к типу bool (говоря, что результат, кстати, не обязан быть равен 1). А поскольку sizeof возвращает размер объекта в байтах, а конеченые объекты должны иметь ненулевой размер, то и размер bool должен быть не меньше байта.
Я думаю, что стандарт вполне мог бы запретить применять sizeof к bool и разрешить паковать значения bool, но не сделал этого, т.к. это, скорее всего, неэффективно.
С другой стороны, у std::vector есть специализация std::vector, «булевский вектор», которая обычно требует меньше памяти, чем массив значений типа bool.
Bool сколько байт
Особенности присвоения значений переменным
Вид константы или переменной (глобальная, локальная) задается оператором, с помощью которого данная переменная (константа) определяется (см. Операторы определения переменных в разделе Операторы Техно ST ). Синтаксис операторов определения переменных предполагает обязательное указание типа данных:
//определение локальной строковой
VAR myVar: STRING; END_VAR
Тип данных определяет размер выделяемой памяти. Для указания типа в Техно ST определены следующие ключевые слова (в круглых скобках указано соответствие типу данных C):
BOOL (bool) – булево значение размерностью 1 байт (true (1) или false (0));
SINT (__int8) – целое со знаком размерностью 1 байт (-128 . 127);
USINT (unsigned __int8) – целое без знака размерностью 1 байт (0 . 255);
INT (short) – целое со знаком размерностью 2 байта (-32768 . 32767);
UINT (unsigned short) – целое без знака размерностью 2 байта (0 . 65535);
DINT (long) – целое со знаком (4 байта) (-2147483648 . 2147483647);
UDINT (unsigned long) – целое без знака (4 байта) (0 . 4294967295);
TIME, DATE, TIME_OF_DAY, DATE_AND_TIME – соответствуют DINT. Значения переменных этих типов задаются аналогично соответствующим временным константам (см. Числовые константы Техно ST );
REAL (float) – вещественное число (4 байта) (максимальное значение 3.402823466e+38);
LREAL (double) – вещественное число (8 байт) (максимальное значение 1.7976931348623158e+308);
STRING (WCHAR*) – 256 символов в кодировке utf-8 (512 байт, см. также Строковые константы Техно ST );
HANDLE – специальный тип, используемый для хранения внешних данных в виде числа, имеет размерность 4 байта, не может быть использован в арифметических, логических и т.п. операциях.
Кроме указанных типов, переменной может быть присвоен структурный тип, созданный пользователем. Такая переменная является конкретным объектом указанного типа (см. Структуры Техно ST ).
При определении переменной может быть задано ее значение:
VAR i: INT:=0; END_VAR
Если при определении переменной ее значение не задано, то этой переменной по умолчанию присваивается следующее начальное значение:
числовая переменная – 0;
переменная типа BOOL – FALSE;
переменная типа STRING – пустая строка;
переменная типа HANDLE – 16#00000000 (0 в формате HEX);
переменная типа TIME, DATE, TIME_OF_DAY или DATE_AND_TIME – 0.
При определении константы задание ее значения обязательно:
VAR CONSTANT myConst: INT:=13; END_VAR
В отличие от переменной, значение константы в программе изменять нельзя.
Особенности присвоения значений переменным
При присвоении значения переменной типа TYPE1 переменной типа TYPE2 нужно учитывать следующее:

присвоение корректно только в том случае, если тип TYPE2 заключает в себе все числа типа TYPE1:
VAR a: REAL := -1564.343; END_VAR
VAR b: USINT := 50; END_VAR
a = b; // корректная операция
b = a; // некорректная операция

присвоение корректно, если один из типов – BOOL, а другой – любой численный. Логическое значение TRUE соответствует единице, FALSE – нулю; нуль соответствует FALSE, любое ненулевое значение, в том числе отрицательное, соответствует TRUE:
VAR a: BOOL; END_VAR
VAR b: SINT := -50; END_VAR
a = b; // a = TRUE , корректная операция
b = a; // b = 1 , корректная операция
Размер типа boolean
Для всех примитивных типов данных, кроме типа boolean , в Java однозначно определен размер. Размер типа boolean , насколько я понимаю, зависит от реализации конкретной виртуальной машины. Вопрос: почему для типа boolean однозначно не определили размер?
Отслеживать
задан 8 фев 2017 в 23:39
417 1 1 золотой знак 4 4 серебряных знака 11 11 бронзовых знаков
Встречный вопрос. А с какими целями это, по-вашему, может быть надо? Полагаю, что ответ «определять размер однозначно было просто незачем» вас не устроит.
– user181100
8 фев 2017 в 23:48
@D-side, Это же ровно то, что я спрашиваю.
8 фев 2017 в 23:50
Потому что boolean — 1 бит. А адресовать один бит нельзя. А количество бит/байт которые можно адресовать (читай, минимальный размер памяти, который можно прочитать) в теории зависит от реальной машины, на которой исполняется java. Думаю причина в этом.
8 фев 2017 в 23:53
3 ответа 3
Сортировка: Сброс на вариант по умолчанию
А зачем? Языку не должно быть до этого дела.
Если поведение однозначно определено, всё остальное вторично.
Тип boolean прекрасно определяется множеством своих допустимых значений. Математически оно ограничивает минимальный размер значения одним битом.
Но использовать именно один бит (и ни битом более) эффективно далеко не всегда, поскольку на популярных архитектурах нельзя адресовать отдельные биты. А потому значения отдельных boolean ‘ов нельзя быстро сохранить в отдельные биты оперативной памяти — необходимо использовать комбинацию побитовых операций, что почти наверняка будет медленнее, чем запись целого отдельного регистра в оперативную память (размер которого может быть различным на разных платформах!).
Но при этом, к примеру, я вполне себе представляю, как на x86 компилятор может использовать в нативном коде в качестве какого-нибудь конкретного boolean -значения один бит регистра флагов, при условии, что это значение никогда не попадает в оперативную память (отдельно от других). Поэтому любые ограничения на размер более одного бита тоже могут потенциально мешать.
И эта свобода представления позволяет авторам виртуальных машин Java использовать любые реализации, которые они считают наиболее эффективными в каждом конкретном контексте. Как видно из примеров выше, любые ограничения будут только мешать.
Управление памятью в Swift
Если на собеседовании вам не задавали вопросы про управление памятью, то уверяю вас, это дело времени. Что же вообще такое память? Это длинная последовательность байтов (если кто-то забыл, то 1 байт = 8 бит). Байты расположены упорядоченно, каждый байт имеет свой адрес в памяти. Когда мы создаем Value или Reference Type, то под наши объекты выделяется память (те самые упорядоченные байты). В этой статье мы будем учиться определять свойства объекта в памяти с помощью MemoryLayout.
Терминология
При выделении памяти программа используется две области в оперативной памяти — стек(stack) и кучу(heap). Про разницу между стеком и кучей можно почитать в этой статье: чем стек отличается от кучи? Ниже приведу лишь краткий перечень:
Стек:
- Переменные, выделенные в стеке хранятся непосредственно в памяти, доступ к этой памяти очень быстрый. Выделение этой памяти происходит во время компиляции программы;
- Используется для статического выделения памяти. Организована по принципы LIFO, то есть последним пришел, первым вышел;
- Есть ограничение — данные, которые хранятся в стеке должны быть статичными и конечными. Это значит, что на этапе компиляции их размер должен быть известен;
- Каждый поток в приложении имеет доступ к своему собственному стеку.
Куча:
- Используется для динамического выделения памяти;
- Операции происходят медленнее, чем в стеке, так как требуются дополнительные операции, чтобы найти данные в куче;
- Куча общая для всех потоков приложения.
Очень важно знать и понимать сколько памяти занимает создаваемый вами объект. И мы можем узнать это с помощью встроенного типа перечисления MemoryLayout.
Перед тем, как мы рассмотрим MemoryLayout, давайте введем терминологию:
- Бит — наименьшая единица измерения количества информации. Байт — вторая по величине единица измерения. 1 байт = 8 бит;
- Size (размер) — количество байтов для хранения типа в памяти;
- Alignment (выравнивание) — способ упорядочения данных и доступа к ним в памяти;
- Stride (шаг) — количество байтов между двумя элементами в памяти;
- Type (тип) — представление типа данных, например, Integer или Bool.
Мы можем узнать о расположении памяти типов Swift, используя MemoryLayout. Это дает нам информацию об объекте в памяти, например, размер, выравнивание и шаг рассматриваемого типа. Шаг типа всегда должен быть больше или равен его размеру.
Рассчитываем размер памяти (Size)
MemoryLayout(T).size означает нечто иное, чем sizeof в Objective-C. Swift показывает нам количество байтов, используемых для хранения типа во время компиляции.
Давайте рассмотрим пример того, как можно получить размер типа с помощью MemoryLayout:
MemoryLayout.size // 16 байт MemoryLayout.size // 8 байт MemoryLayout.size // 4 байта MemoryLayout.size // 2 байта MemoryLayout.size // 1 байт
В результате мы видим, что размер типа String занимает 16 байт, Int — 8 байт, Int32 — 4 байта, Int16 — 2 байта, а Bool — 1 байт. Вроде все просто. Давайте усложним пример.
Рассмотрим следующую структуру:
struct TestStruct
Так как структура — тоже тип, значит мы можем узнать ее размер в памяти!
MemoryLayout.size // 2 байта
Почему размер равен 2? Все просто, повторим: размер (size) — необходимое количество Байтов для хранения типа в памяти. Так как тип Bool занимает в памяти 1 байт, то логично предположить, что структура из двух типов Bool занимает 2 байта. Отсюда можно скорректировать определение размера: размер типа — сумма всех его полей.
Вроде с этим разобрались. Давайте усложнять пример:
struct TestStruct < let one: String let two: Int let three: Bool >MemoryLayout.size // 25 байт
Выше мы уже определили размер каждого из этих типов в структуре. Сумма всех размеров будет равна 25 байт. Давайте изменим порядок переменных в нашей структуре (переместим Bool в самое начало):
struct TestStruct < let three: Bool let one: String let two: Int >MemoryLayout.size // 32 байта
Почему размер увеличился? В данном примере размер переменной three занял 8 байт, one — как и раньше, 16 байт, two — как и раньше, 8 байт. Чтобы понять, почему three занимает теперь 8, а не 1 байт, давайте рассмотрим следующие свойства — Stride(шаг) Alignment(выравнивание).
Рассчитываем шаг в блоках памяти (Stride)
Как мы уже говорили ранее, шаг представляет собой расстояние между двумя объектами в памяти. Давайте рассмотрим следующий пример:
struct TestStruct < var one: Int32 var two: Bool >MemoryLayout.size // 5 байт MemoryLayout.stride // 8 байт
С размером, думаю, все понятно: Int32 занимает 4 байта, а Bool — 1 байт, сумма этих типов и дает результат в 5 байт. Но что такое на самом деле шаг? Давайте разбираться.
Если у нас будет две структуры TestStuct, то картинка с блоками памяти будет следующая:

Благодаря шагу мы знаем, на сколько байтов нужно двигать указатель, чтобы добраться до следующего объекта. Как мы видим, между первой и второй структурой находится 3 неиспользуемых байта. Причиной этому является последнее наше свойство — выравнивание.
Рассчитываем выравнивание в блоках памяти (Alignment)
Суть выравнивания заключается в том, чтобы оптимизировать блоки памяти таким образом, чтобы к ним было как можно меньше обращений. Это ускорит программу. Если бы в нашем примере блоки памяти шли друг за другом, то есть на шестом байте начинался бы блок с Int32, то программе было бы сложно понять, где заканчивается первая структура и где начинается вторая. Для этого потребовалось бы больше операций.
В Swift у всех типов есть свои выравнивания:
- String и Int выравниваются по 8 байт;
- Int32 по 4 байта;
- Int16 по 2 байта;
- Bool по 1 байту.
MemoryLayout.alignment // 8 байт MemoryLayout.alignment // 4 байта MemoryLayout.alignment // 2 байта MemoryLayout.alignment // 1 байт
То есть блоки памяти будут выравниваться по значениям, кратным своим выравниваниям.
Разбираем примеры
Давайте рассматривать примеры и разбираться, что же из себя представляет управление памятью.
Пример №1
Начнем с простого:
struct TestStruct < var one: Bool var two: Bool >MemoryLayout.size // 2 байта MemoryLayout.stride // 2 байта MemoryLayout.alignment // 1 байт
Представим, что у нас две такие структуры. Тогда их блоки памяти можно изобразить следующим образом:

- Размер: размер структуры равен сумме двух типов Bool, каждый из которых равен 1 байту;
- Выравнивание: так как структура состоит только из типов Bool, выравнивание которых равно 1, то и выравнивание будет равно 1 байту;
- Шаг: так как выравнивание равно 1 байту, а размер структуры 2 байтам, то вторую структуру мы сможем расположить в памяти следом за первой (выравнивание = размеру, округленному в большую сторону, кратному выравниванию).
Выравнивание — это длина байта, по которому мы выравниваемся, то есть по 1 байту (поскольку самый большой элемент нашей структуры имеет длину 1 байт).
Пример №2
struct TestStruct < var one: Int32 var two: Bool >MemoryLayout.size // 5 байт MemoryLayout.stride // 8 байт MemoryLayout.alignment // 4 байта

- Размер: Int32 = 4 байта, а Bool = 1 байт. Суммируем, получаем результат;
- Выравнивание: Берем наибольшее выравнивание из всех свойств — Int32;
- Шаг: Так как размер нашей структуры равен 5 байтам, то округляем размер в большую сторону к числу, кратному выравниваю, получаем 8 байт.
Из полученных нами знаний давайте упростим определения рассматриваемых свойств, касательно структур:
Размер (size) — это сумма размеров всех типов объекта (в нашем случае структуры);
Шаг (stride) — размер, округленный в большую сторону, кратный выравниванию;
Выравнивание (alignment) — это наибольшее выравнивание из всех свойств объекта (в нашем случае структуры).
Пример №3
Наконец, рассмотрим последний пример:
struct TestStruct < let one: Int let two: Bool >MemoryLayout.size // 9 байт MemoryLayout.stride // 16 байт MemoryLayout.alignment // 8 байт

- Размер: сумма Int и Bool дает значение 9 Байт;
- Выравнивание: наибольшее выравнивание из свойств структуры = 8 Байт;
- Шаг: так как размер равен 9, то наибольшее значение, кратное выравниванию = 16 Байт.
Давайте посмотрим как работает управление памятью и немного изменим этот же пример — поставим Bool на первое место:
struct TestStruct < let one: Bool let two: Int >MemoryLayout.size // 16 байт MemoryLayout.stride // 16 байт MemoryLayout.alignment // 8 байт
Размер увеличился! Давайте разбираться.

- Размер: из-за того что Int имеет выравнивание равное 8, то Bool должен начинаться с байта, кратному 8, поэтому и образовывается пустое место между Bool и Int, что и влечет за собой увеличения размера структуры;
- Выравнивание: как и раньше, наибольшее значение = 16 байт;
- Шаг: так как размер кратен выравниванию, то и шаг остается равным 16 байт.
Рассчитываем размер класса
Попробуем определить размер класса с помощью MemoryLayout:
class TestClass < let one: Bool let two: Int init(oneValue: Bool, twoValue: Int) < one = oneValue two = twoValue >> MemoryLayout.size // 8 байт MemoryLayout.stride // 8 байт MemoryLayout.alignment // 8 байт
Для классов размер ссылки составляет 8 байт, поэтому все значения и будут равны 8 байт. Но не путайте: размер ссылки равен 8 байт, но не реальный размер объекта в куче.
Чтобы определить реальный размер объекта в куче, вы можете использовать Objective-C runtime — функцию class_getinstanceSize(_:), которая возвращает размер экземпляра класса.
Пример вызова функции:
class TestClass < let one: Bool let two: Int init(oneValue:Bool, twoValue: Int ) < one = oneValue two = twoValue >> class_getInstanceSize(TestClass.self)
Дополнительное чтение на тему Управление памятью:
- Value Type и Reference Type или чем стек отличается от кучи?
- Управление памятью в Swift
- Память в Swift от 0 до 1
Выразить благодарность или найти уникальный материал вы можете в boosty.
Подписывайтесь на мой Telegram-канал iOS Interview Channel, чтобы не пропустить новый материал.
