2013-08-15 2 views
23

Я часто жалея, что я мог бы сделать что-то подобное в C:Почему у C нет бинарных литералов?

val1 &= 0b00001111; //clear high nibble 
val2 |= 0b01000000; //set bit 7 
val3 &= ~0b00010000; //clear bit 5 

Имея этот синтаксис кажется, невероятно полезным дополнением к C без каких-либо недостатков, что я могу думать, и это кажется естественным вещь для язык низкого уровня, где бит-twiddling довольно распространен.

Редактировать: Я вижу некоторые другие отличные альтернативы, но все они разваливаются, когда есть более сложная маска. Например, если reg является регистром, который управляет выводами ввода-вывода на микроконтроллере, и я хочу установить контакты 2, 3 и 7 в то же время, я мог бы написать reg = 0x46;, но мне пришлось потратить 10 секунд, думая об этом (и мне, вероятно, придется потратить 10 секунд снова каждый раз, когда я прочитаю этот код после того, как не посмотрю на него в течение дня или двух), или я мог бы написать reg = (1 << 1) | (1 << 2) | (1 << 6);, но лично я думаю, что это менее понятно, чем просто писать `reg = 0b01000110 ;» Я могу согласиться, что он не масштабируется далеко за пределы 8 бит или, может быть, 16-разрядных архитектур. Не то, чтобы мне когда-либо понадобилось сделать 32-битную маску.

+1

У этого есть шестерка, которая, на мой взгляд, даже лучше, если вы потратите 10 минут, чтобы почувствовать отношения – vroomfondel

+0

Должен ли последний комментарий читать «clear bit 4»? (Или, что еще лучше, «очистить бит 3», поскольку программисты должны рассчитывать с нуля по умолчанию?) – Nemo

+0

ИМХО, потому что есть шестнадцатеричный. Гораздо проще использовать программист, чем двоичные числа. Это очень подвержено ошибкам. Не важно, насколько вы опытны. –

ответ

12

Это толкаемый hexadecimal быть ... шестнадцатеричным. «... первичное использование шестнадцатеричной нотации - это удобное для человека представление двоично-кодированных значений в вычислительной и цифровой электронике ...». Это будет выглядеть следующим образом:

val1 |= 0xF; 
val2 &= 0x40; 
val3 |= ~0x10; 

Hexadecimal:

  1. Одна шестнадцатеричная цифра может представлять собой полубайт (4 бита или половину восьмеричной).
  2. Две шестнадцатеричные цифры могут представлять собой байты (8 бит).
  3. Hex намного компактнее при масштабировании до более крупных масок.

С некоторой практикой преобразование между шестнадцатеричным и двоичным будет становиться более естественным. Попытайтесь написать свои конверсии вручную, а не использовать онлайн-конвертер записей в формате bin/hex - затем через пару дней он станет естественным (и быстрее в результате).

Помимо: Даже если бинарные литералы не является стандартным C, если вы собираете с GCC можно использовать бинарные литералы, они должны начинаться с префикса «0b» или «0B». Для получения дополнительной информации см. Официальную документацию here. Пример:

int b1 = 0b1001; // => 9 
int b2 = 0B1001; // => 9 
+2

Да, это то, что я всегда делаю взамен, но мне всегда приходится делать кучу вычислений в моем, чтобы помнить, что такое бинарный файл в hax. Особенно, если я хочу, например. очистите самые младшие 6 бит. И я согласен с тем, что бинарные литералы получат много времени для 32-разрядных платформ, но в этом случае вы просто не сможете их использовать. – Drew

+3

Мышление в гексагоне становится второй натурой после небольшой практики. Преимущество Hex также состоит в том, что его легче читать, чем двоичный. – markgz

+1

@Drew, я понимаю вашу точку зрения, так как визуально намного легче подумать о масках меньшего размера. После того, как вы достаточно практиковали, это будет достаточно естественным (как все приходит в жизни).Я рекомендую делать все вычисления вручную и дважды проверять себя на калькуляторе при создании масок, чтобы вы могли улучшаться и конвертировать между двумя нотами. –

21

Согласно Rationale for International Standard - Programming Languages C §6.4.4.1 целочисленные константы

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

Это не в стандарте C, но GCC поддерживает его как расширение, префиксом 0b или 0B:

i = 0b101010; 

См here для деталей.

+0

Thats awesome, но я не использую GCC: (знаете ли вы, что его не в стандарте? – Drew

+0

@Drew См. Обновление. В другом слове комитет считает, что его использование может быть покрыто шестнадцатеричными константами, я думаю. –

8

Все ваши примеры могут быть написаны еще более ясно:

val1 &= (1 << 4) - 1; //clear high nibble 
val2 |= (1 << 6); //set bit 6 
val3 &=~(1 << 3); //clear bit 3 

(я взял на себя смелость фиксируя комментарии считать от нуля, как и предназначен Nature.)

Ваш компилятор сбросит эти константы, поэтому для их написания не существует штрафа за исполнение. И их легче читать, чем версии 0b....

+0

@ Спасибо, 0: – Nemo

+0

Нет проблем [filler]. –

+0

Если принять во внимание конечность, то '(1 << 4) - 1' действительно то же самое, что' 0xF' ? ... может быть, нет. –

5

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

Легко ли вам понять, что вы ошибочно набрали 0b1000000000000000000000000000000(0x40000000), где вы действительно имеете в виду 0b10000000000000000000000000000000(0x80000000)?

+0

Это кажется лучшей причиной до сих пор. Тем не менее, в этих случаях вам не нужно использовать двоичный код. И как часто вы делаете 32-битную маску? – Drew

+0

Поскольку в большинстве случаев у них есть лучшая альтернатива (шестнадцатеричная), я думаю, что комитет просто закрывает двери для таких ошибок, не предоставляя их. –

+1

, но это затрудняет понимание некоторых других случаев, например, если это регистр, в котором бит может иметь разное значение, вынуждение вывести калькулятор, чтобы увидеть, какой бит включен/отключен, является просто осложнением. Это общий случай при работе с датчиками i2c/spi. – Lesto

0

Если вам не нужен фактический буквальным, вы можете сделать что-то вроде этого:

#define B_(x) strtoull(#x, 0, 2) 

unsigned char low_nibble = B_(00001111); 
unsigned char bit_7 = B_(01000000); 
unsigned char bit_5 = B_(00010000); 

val1 |= low_nibble; 
val2 &= bit_7; 
val3 |= ~bit_5; 
+2

Это будет выполнять декодирование строки выполнения три раза, каждый раз при запуске этого кода. Это не будет хорошо для производительности. –

+0

@DavidGiven: Требование к производительности означает, что литерал должен использоваться. В ответе уже говорится, что это можно использовать, если литерал не требуется. – jxh

2

«Например, если р является регистр, который контролирует контакты ввода/вывода на микроконтроллере»

Я не могу не думать, что это плохой пример. Биты в контрольных регистрах имеют определенные функции (как и любые устройства, подключенные к отдельным битам ввода-вывода).

Было бы гораздо разумнее предоставить символические константы для битовых шаблонов в файле заголовков, а не для разработки двоичного кода внутри кода. Преобразование двоичного в шестнадцатеричное или восьмеричное тривиально, помня, что происходит, когда вы пишете 01000110 в регистр ввода-вывода, нет, особенно если у вас нет таблицы данных или схемы.

Вы не только сохраните эти 10 секунд, пытаясь выработать двоичный код, но, возможно, несколько больше времени, пытаясь понять, что он делает!