Битовые поля C / C ++ по сравнению с побитовыми операторами для выделения битов, что быстрее, лучше, более переносимо? - PullRequest
13 голосов
/ 24 января 2010

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

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

if( a.bit1 ) /* etc */

или

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

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

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

Ответы [ 7 ]

12 голосов
/ 24 января 2010

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

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

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

7 голосов
/ 24 января 2010

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

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

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

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

6 голосов
/ 24 января 2010

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

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

5 голосов
/ 24 января 2010

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

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

4 голосов
/ 24 января 2010

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

1 голос
/ 24 января 2010

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

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

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

1 голос
/ 24 января 2010

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

...