Битовые поля C/С++ по сравнению с побитовыми операторами для выделения битов, которые быстрее, лучше и более переносимы?

Мне нужно собрать несколько бит в байте следующим образом:

struct  
{  
  char bit0: 1;  
  char bit1: 1;  
} a;  

if( a.bit1 ) /* etc */

или

if( a & 0x2 ) /* etc */

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

Примечания. Если вы планируете отвечать на "профиль", я буду, но, поскольку я ленив, если у кого-то уже есть ответ, гораздо лучше.
Код может быть неправильным, вы можете исправить меня, если хотите, но помните, какой смысл в этом вопросе, и попробуйте ответить и на него.

Ответы

Ответ 1

Я предпочел бы использовать второй пример для максимальной переносимости. Как отметил Нил Баттерворт, использование битполей - это только для собственного процессора. Хорошо, подумайте об этом, что произойдет, если Intel x86 завтра выйдет из бизнеса, код застрянет, а это значит, что нужно повторно реализовать битподы для другого процессора, скажем, RISC-чипа.

Вы должны посмотреть на картинку большего размера и спросить, как OpenBSD удалось перенести свои BSD-системы на множество платформ, используя одну кодовую базу? Хорошо, я признаю, что это немного выше, и дискуссионный и субъективный, но реалистично, если вы хотите перенести код на другую платформу, его способ сделать это, используя второй пример, который вы использовали в своем вопросе.

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

Никогда не полагайтесь на битполы как волшебную пулю. Если вам нужна скорость для процессора и будет зафиксирована на нем, то есть нет намерения портировать, тогда не стесняйтесь использовать битовые поля. У вас не может быть обоих!

Ответ 2

Битвые поля делают код более понятным, если они используются соответствующим образом. Я бы использовал битовые поля только как компактное устройство. Одно из распространенных мест, которое я видел, они используются в компиляторах: часто информация о типе или символе состоит из группы флагов true/false. Битвые поля идеальны здесь, так как у типичной программы будет много тысяч этих узлов, созданных при компиляции.

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

Что касается скорости, хороший компилятор даст тот же код для бит-полей, которые будут маскироваться.

Ответ 3

С битовыми полями были мертворожденные с момента их изобретения - по неизвестной причине. Люди не любили их и вместо этого использовали побитовые операторы. Вы должны ожидать, что другие разработчики не поймут код битового поля C.

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

Ответ 4

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

Итак, используйте первый.

Ответ 5

Если вы хотите переносимость, избегайте битполей. И если вы заинтересованы в производительности конкретного кода, нет альтернативы написанию собственных тестов. Помните, битполы будут использовать побитовые команды процессора под капотом.

Ответ 6

Я думаю, что программист С будет стремиться ко второму варианту использования бит-масок и логических операций, чтобы вывести значение каждого бита. Вместо того, чтобы иметь код, замусоренный шестнадцатеричными значениями, будут перечислены перечисления или, как правило, при использовании более сложных операций, макросы для получения/установки определенных бит. Я слышал на виноградной лозе, что эта структура реализовала битподы медленнее.

Ответ 7

Не читайте много в "не переносимых битовых полях". Существуют два аспекта битовых полей, которые определены реализацией: подпись и макет, а также один неуказанный: выравнивание единицы распределения, в которой они упакованы. Если вам не нужно что-либо еще, что эффект упаковки, используя их, является переносимым (при условии, что вы явно указываете ключевое слово signed, где это необходимо), как вызовы функций, которые также имеют неопределенные свойства.

Относительно производительности, профиль - лучший ответ, который вы можете получить. В идеальном мире между этими двумя статьями не было бы разницы. На практике могут быть некоторые, но я могу думать о многих причинах в одном направлении, как о другом. И он может быть очень чувствительным к контексту (логически бессмысленная разница между неподписанными и подписанными для примера), поэтому измерять в контексте...

Подводя итог, различие, таким образом, является главным образом различием стиля в тех случаях, когда у вас действительно выбор (т.е. если важна точная компоновка). В этом случае это оптимизация (по размеру, а не по скорости), и поэтому я бы предпочел сначала написать код без него и добавить его, если необходимо. Таким образом, бит-поля являются очевидным выбором (модификации, которые нужно выполнить, являются наименьшими для достижения результата и содержатся в уникальном месте определения вместо того, чтобы распространяться на все места использования).