зачем нужны битовые поля
Расставим точки над структурами C/C++
Недавно познакомился со структурами C/C++ — struct. Господи, да «что же с ними знакомиться» скажете вы? Тем самым вы допустите сразу 2 ошибки: во-первых я не Господи, а во вторых я тоже думал что структуры — они и в Африке структуры. А вот как оказалось и — нет. Я расскажу о нескольких жизненно-важных подробностях, которые кого-нибудь из читателей избавят от часовой отладки…
Выравнивание полей в памяти
Обратите внимание на структуру:
Оно выглядит вот так:
Посмотрим на размещение полей в памяти:
Битовые поля
В комментариях мне указали на то, что битовые поля в структурах по стандарту являются «implementation defined» — потому их использования лучше избежать, но для меня соблазн слишком велик.
Мне становится не то что неспокойно на душе, а вообще становится хреново, когда я вижу в коде заполнение битовых полей при помощи масок и сдвигов, например так:
Всё это пахнет такой печалью и такими ошибками и их отладкой, что у меня сразу же начинается мигрень! И тут из-за кулис выходят они — Битовые Поля. Что самое удивительное — были они ещё в языке C, но кого ни спрашиваю — все в первый раз о них слышат. Этот беспредел надо исправлять. Теперь буду давать им всем ссылку, ну или хотя бы ссылку на эту статью.
Как вам такой кусок кода:
А дальше в коде мы можем работать с полями как и всегда работаем с полями в C/C++. Всю работу по сдвигам и т.д. берет на себя компилятор. Конечно же есть некоторые ограничения… Когда вы перечисляете несколько битовых полей подряд, относящихся к одному физическому полю (я имею ввиду тип который стоит слева от имени битового поля) — указывайте имена для всех битов до конца поля, иначе доступа к этим битам у вас не будет, иными словами кодом:
Также порядок размещения битовых болей в байте зависит от порядка байтов. При порядке LITTLE_ENDIAN битовые поля раздаются начиная со первых байтов, при BIG_ENDIAN — наоборот…
Порядок байтов
Ну тут я буду краток. Я в одной из своих предыдущих статей уже писал что нужно делать с порядками байтов. Есть возможность описать структуры, которые внешне работают как числа, а внутри сами определяют порядок хранения в байтах. Таким образом наша структура IP-заголовка будет выглядеть так:
«Язык С++ достаточно сложен, чтобы позволить нам писать на нём просто» © Как ни странно — Я
З.Ы. Планирую в одной из следующих статей выложить идеальные, с моей точки зрения, структуры для работы с заголовками протоколов стека TCP/IP. Отговорите — пока не поздно!
Национальная библиотека им. Н. Э. Баумана
Bauman National Library
Персональные инструменты
Битовое поле
Битовое поле – это элемент структуры, определенный как некоторое число битов, обычно меньшее, чем число битов в целом числе (оно по величине не превосходит машинного слова и зависит от реализации компилятора). Они предназначены для экономного размещения в памяти данных небольшого диапазона, обеспечивают удобный доступ к отдельным битам данных. Кроме того, с помощью битовых полей можно формировать объекты с длиной внутреннего представления, не кратной байту. Битовые поля обычно применяются в низкоуровневом программировании. [Источник 1]
Значение отдельных битов в поле определяется программистом, например, первый бит в битовое поле (расположенном в поле базового адреса) иногда используется для определения состояния конкретного атрибута, связанного с битовым полем.
В микропроцессорах и других логических устройств, наборы бит поля «флаги» обычно используются для контроля или для обозначения промежуточного состояния или результатов конкретной деятельности. Микропроцессоры, как правило, имеют статус регистра, который состоит из таких флагов, используемые для обозначения различных состояния после операций, например, арифметическое переполнение. Флаги можно прочитать и используются для принятия решения о последующих операциях, например, при обработке условного скачка инструкции. Например, je (перейти если равно) инструкция на х86 ассемблере в результате скачка если Z (ноль) флаг был установлен на некоторую предыдущую операцию.
Битовое поле отличается от битового массива тем, что последний используется для хранения большой набор битов, пронумерованных целыми числами и часто шире, чем любой целочисленный тип поддерживаемых языков. Битовые поля, с другой стороны, как правило, помещается в машину, слово, и денотата бит не зависит от их числовым индексом.
Содержание
Применение
Битовые поля могут использоваться для уменьшения потребления памяти, когда программа требует ряд целочисленных переменных, которые всегда будут иметь низкие значения. Например, во многих системах хранения целочисленное значение требуется два байта (16 бит) памяти; иногда значения будут храниться только в одном или двух битах. Имея некоторые из этих крошечных переменные битового поля позволяет эффективно упаковывать данные в памяти. В C и C++, собственной реализации определенных битовые поля могут быть созданы, используя беззнаковый int, подписанный int, или (в c99:) _Bool. В этом случае, программист может объявить структуру битового поля метки и определяет ширину из нескольких подполей. При заявленной битовые поля того же типа могут быть упакованы компилятор в меньшее количество слов, по сравнению с памятью используется если в каждом «поле» будет объявлена отдельно. Для языков, не хватает родных полей, или где программист хочет строго контролировать результирующий бит представлении, можно вручную манипулировать битами в рамках более крупного типа Word. В этом случае программист может установить, проверить и изменить биты в поле с помощью комбинации маскировки и побитовые операции.
Компиляторы
Операции которые компиляторы могут произвести с битовыми полями довольно ограничены. Компиляторы способны лишь произвести чтение значения из битового поля, а также запись в битовое поле. Компиляторы распознают битовое поле как число без знака. Аппаратная платформа и тип компилятора прямо влияет на расположение битовых полей в структуре данных: в зависимости от реализации компилятора расположение битовых полей может начинаться с младших или старших битов. [Источник 2]
Объявление битовых полей
Элементом структуры может быть битовое поле, обеспечивающее доступ к отдельным битам памяти. Вне структур или объединений битовые поля объявлять нельзя. Нельзя также организовывать массивы битовых полей и нельзя применять к полям операцию определения адреса или получить ссылку на них.
Синтаксис объявления типа структуры с битовыми полями:
где struct – спецификатор типа;
Битовые поля длиной 1 должны объявляться как unsigned, поскольку 1 бит не может иметь знака. Битовые поля могут иметь длину от 1 до 16 бит для 16-битных сред и от 1 до 32 бит для 32-битных сред.
Разрешается поле без имени (для этого надо указать только двоеточие и ширину), с помощью которого в структуру вводятся неиспользуемые биты (промежуток между значимыми полями). Нулевая ширина поля вводится, когда необходимо, чтобы следующее в данной структуре поле разместилось с начала очередного машинного слова.
Например, если нам нужны только биты cts и dsr, то можно объявить структуру status_type следующим образом:
Регистр состояния процессора
Простой пример регистра состояния битового поля включен в конструкцию восьмибитового процессора 6502. В одном восьмибитовом поле хранилось семь фрагментов информации: Bit 7. Навигационный флаг Bit 6. Флаг переполнения Bit 5. Неиспользованный Bit 4. флаг перерыва Bit 3. десятичный флаг Bit 2. флаг применения-отключения Bit 1. флаг переноса Bit 0. Нулевой флаг
Unix код выхода процесса
Другим примером может служить системы Unix статус выхода код, который может быть использован в качестве флага для передачи информации о состоянии в другой процесс. Например, программа, которая контролирует состояние восьми переключателей охранной сигнализации может установить биты в код выхода, проходя по другому обрабатывая информацию о том, какие выключатели закрытые или открытые.
Изменение битов в словах флагов
Битовые поля в C++
Классы и структуры могут содержать члены, которые занимают меньше пространства в памяти, чем целочисленный тип. Эти члены определяются как битовые поля. Синтаксис для спецификации объявления члена в битовом поле приведен ниже:
Синтаксис
декларатор : константное выражение
Remarks
Декларатор (необязательный) — это имя, по которому осуществляется доступ к элементу в программе. Он должен иметь один из целочисленных типов (включая перечисляемые типы). Константа-выражение указывает количество битов, занимаемых элементом в структуре. Анонимные битовые поля, (т. е. битовые поля без идентификатора) можно использовать для заполнения.
В следующем примере объявляется структура, которая содержит битовые поля:
Структура памяти объекта типа Date
Блок, относящийся только к системам Microsoft
Данные, объявленные в качестве битовых полей, упорядочиваются от младшего бита к старшему, как показано на рисунке выше.
Завершение блока, относящегося только к системам Майкрософт
Объявление структуры может содержать неименованное поле длиной 0, как показано в следующем примере.
затем макет памяти показан на следующем рисунке:
Структура объекта типа Date с битовым полем нулевой длины
Базовый тип битового поля должен быть целочисленным типом, как описано в разделе встроенные типы.
Ограничения для битовых полей
В следующем списке указаны ошибочные операции с битовыми полями:
Получение адреса битового поля.
Инициализация не const ссылки с битовым полем.
Когда использовать битовые поля в C?
на вопрос «почему нам нужно использовать битовые поля», в Google я обнаружил, что битовые поля используются для флагов. Теперь мне любопытно, это единственный способ, которым бит-поля используются практически? Нужно ли использовать битовые поля для экономии места?
способ определения битового поля из книги:
почему мы используем int? Сколько места занято? Я смущен, почему мы используем int, но не short или smith меньше, чем int. Как я понимаю, только 1 бит занимает в памяти, но не все значение unsigned int. Правильно ли это?
14 ответов
теперь мне любопытно, [флаги] единственный способ битовые поля используются практически?
нет, флаги-это не единственный способ использования битовых полей. Они также могут использоваться для хранения значений больше одного бита, хотя флаги более распространены. Например:
нужно использовать битовые поля для экономии места?
однако, делая это вручную, вам нужно будет написать что-то вроде:
эта улучшенная читаемость, возможно, более важна, чем сохранение нескольких байтов здесь и там.
почему мы используем int? Сколько пространство занято?
как я понимаю, только 1 бит занят в памяти, но не целое значение unsigned int. Правильно ли это?
нет, это неправильно. Весь unsigned int будет существовать, даже если вы используете только 8 его бит.
основной причиной является уменьшение размера. Например, если вы пишете:
вы будете использовать по крайней мере 3 * sizeof(unsigned int) или 12 байт для представления 3 маленьких флагов, которые должны нуждаться только в 3 битах.
Итак, если вы пишете:
это своего рода эквивалент классического битового поля домашнего пива:
но синтаксис битового поля чище, сравните:
и, очевидно, менее подвержен ошибкам.
еще одним местом, где bitfields являются общими, являются аппаратные регистры. Если у вас есть 32-битный регистр, где каждый бит имеет определенное значение, вы можете элегантно описать его с помощью bitfield.
такое битовое поле по своей сути зависит от платформы. В этом случае переносимость не имеет значения.
в этих случаях, битовые поля используются, потому что они правильно моделировать проблему мы решаем: что мы имеем дело с не совсем 8-бит (или 16-битные или 24-битные или 32-разрядное) число, а набор из 8 (или 16 или 24 или 32), связанных, но различных элемента информация.
Итак, когда вы пытаетесь работать с битами, это зависит от вас или компилятора (в зависимости от того, на каком языке вы пишете), чтобы написать дополнительные операции, которые выполняют маскировку битов и лишают структуру всего, кроме информации, которую вы действительно хотите работать. Если в «упаковке» информации нет преимуществ (а в большинстве случаев их нет), то используйте битовые поля для booleans будет вводить только накладные расходы и шум в вашем коде.
зачем нам использовать битовые поля?
когда вы хотите сохранить некоторые данные, которые могут быть сохранены меньше байта, такие данные могут быть связаны в структуре с использованием битовых полей. В embedded word, когда один 32-битный мир любого регистра имеет другое значение для другого слова, вы также можете использовать битовые файлы, чтобы сделать их более читаемыми.
Я обнаружил, что битовые поля используются для флагов. Теперь мне любопытно, это единственный способ использования битовых полей практически?
нет это не единственный способ. Вы можете использовать его и по-другому.
нужно использовать битовые поля для экономии места?
Как я понимаю, только 1 бит занят в памяти, но не все значение unsigned int. Правильно ли это?
нет. Память может быть занята только в нескольких байтах.
хорошим использованием было бы реализовать кусок для перевода В-и из-base64 или любой несогласованной структуры данных.
другой пример: использование этой функции для разбейте заголовок пакета TCP на его компоненты (или другие сети заголовок пакета протокола, который вы хотите обсудить), хотя это более продвинутый и менее конечный пример. В общем: это полезно в отношении внутренних частей ПК, так, драйверов, систем кодирования.
другой пример: анализируя float количество.
(отказ от ответственности: не знаю имя файла / имя типа, где это применяется, но в C это объявлено в заголовке; не знаю, как это можно сделать для 64-битных flaots, так как мантисса должна иметь 52bits и-в 32bit target-ints имеют 32 бита).
вывод: как показывают концепция и эти примеры, это редко используемая функция, потому что она в основном для внутренних целей, а не для повседневного программного обеспечения.
чтобы ответить на исходный вопрос » Когда использовать битовые поля в C?». по книге Брайана Хука «напиши портативный код» (ISBN 1-59327-056-9, я читал немецкое издание ISBN 3-937514-19-8) и по личному опыту:
никогда не используйте идиому bitfield языка C, но сделайте это самостоятельно.
многие детали реализации специфичны для компилятора, особенно в сочетании с объединениями, и вещи не гарантированы над разными компиляторами и разными endianess. Если есть только крошечный шанс, что ваш код должен быть портативным и будет скомпилирован для разных архитектур и/или с разными компиляторами, не используйте его.
у нас был случай, когда перенос кода от маленьких микро-контроллера с прямым, с некоторым несвободным компилятором, чтобы другой большой микро-контроллера с прямым, с помощью GCC, и это было не весело. :-/
нет необходимости в объединении с типом int и тогда какая-то структура bitfield. Если Вы читаете много встроенного кода, эти шаблоны тестирования, набора и очистки станут общими, и вы легко обнаружите их в своем коде.
битовые поля могут использоваться для экономии места в памяти (но использование битового поля для этой цели редко). Он используется там, где есть ограничение памяти. например) при программировании во встроенных системах.
но это должно использоваться только в случае чрезвычайной необходимости.
потому что мы не можем получить адрес битового поля. Так что адрес оператора & нельзя использовать с ними.
их можно использовать для расширения числа типов без знака, которые переносятся. Обычный вы имели бы только полномочия 8,16,32,64. но с битовыми полями можно получить любую власть.
чтобы ответить на части вопроса, никто не ответил:
Ints не шорты
причина использования ints, а не шорты и т. д. заключается в том, что в большинстве случаев никакое пространство не будет сохранено.
современные компьютеры имеют 32 или 64-битную архитектуру, и эти 32 или 64 бита будут необходимы, даже если вы используете меньший тип хранения, такой как короткий.
меньшие типы полезны только для экономии памяти, если вы можете упаковать их вместе (например, короткий массив может использовать меньше памяти, чем массив int, поскольку шорты могут быть упакованы вместе более плотно в массиве). В большинстве случаев при использовании bitfields это не так.
другие виды использования
битовые поля намного компактнее, и это является преимуществом.
но не забывайте, что упакованные структуры медленнее, чем обычные структуры. Их также сложнее построить, так как программист должен определить количество битов для каждого поля.Это недостаток
использовать пространство памяти, мы можем использовать битовые поля.
насколько я знаю в реальном программировании, если нам нужно, мы можем использовать булевы вместо объявления его целыми числами, а затем сделать битовое поле.
Если это также значения, которые мы часто используем, мы не только экономим пространство, мы также можем получить peformance, так как нам не нужно загрязнять кэши. Однако кэширование также опасно при использовании битовых полей, поскольку одновременное чтение и запись в разные биты приведет к гонке данных, а обновления полностью отдельных битов могут перезаписать новые значения со старыми значениями..
почему мы используем int? Сколько места занято?
один ответ на этот вопрос, который я не видел ни в одном из других ответов, заключается в том, что стандарт C гарантирует поддержку int. В частности:
битовое поле должно иметь тип, который является квалифицированной или неквалифицированной версией _Bool, signed int, unsigned int или другого определенного типа реализации.
обычно для компиляторов разрешить дополнительные типы битовых полей, но не обязательно. Если вы действительно беспокоитесь о переносимости, int-лучший выбор.
Объединения и битовые поля
Объединения
О бъединения в си похожи на структуры, с той разницей, что все поля начинаются с одного адреса. Это значит, что размер объединения равен размеру самого большого его поля. Так как все поля начинаются с одного адреса, то их значения перекрываются. Рассмотрим пример:
Здесь было создано объединение, которое содержит три поля – одно поле целого типа (4 байта), два поля типа short int (2 байта каждое) и 4 поля по одному байту. После того, как значение было присвоено полю dword, оно также стало доступно и остальным полям.
Напоминаю, что на x86 байты располагаются справа налево. Все поля объединения «обладают» одинаковыми данными, но каждое поле имеет доступ только до своей части.
Вот ещё один пример: рассмотрим представление числа с плавающей точкой:
Обратите внимание, что объединение можно инициализировать, как и структуру. При этом значение будет приводиться к типу, который имеет самое первое поле. Сравните результаты работы
Битовые поля
Битовые поля в си объявляются с помощью структур. Они позволяют получать доступ до отдельных битов или групп битов. Доступ до отдельных битов можно осуществлять и с помощью битовых операций, но использование битовых полей часто упрощает понимание программы.
Синтаксис объявления битового поля
В этом примере каждое поле структуры обозначено как битовое поле, длина каждого поля равна единице. Обращаться к каждому полю можно также, как и к полю обычной структуры. Битовые поля имеют тип unsigned int, так как имеют длину один бит. Если длина поля больше одного бита, то поле может иметь и знаковый целый тип.
Размер структуры, содержащей битовые поля, всегда кратен 8. То есть, если одно поле содержит 5 бит, а второе 4, то второе поле начинается с восьмого бита и три бита остаются неиспользованными.
Неименованное поле может иметь нулевой размер. В этом случае следующее за ним поле смещается так, чтобы добрать до 8 бит.
Если же адрес поля уже кратен 8 битам, то нулевое поле не добавит сдвига.
Кроме того, если имеются обычные поля и битовые поля, то первое битовое поле будет сдвинуто так, чтобы добрать до 8 бит.
Те же самые действия можно было сделать и с помощью обычного сдвига
Рассмотрим ещё один пример – знакопостоянный сдвиг вправо. Сдвиг вправо (>>) выталкивает самый левый бит и справа записывает ноль. Из-за этого операцию сдвига вправо нельзя применить, например, для чисел со знаком, так как будет потерян бит знака. Исправим ситуацию, сделаем знакопостоянный сдвиг: будем проверять последний бит числа (напомню, что мы работаем с архитектурой x86 и биты расположены «задом наперёд»)