Подтвердить что ты не робот

Почему 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-битную маску.

4b9b3361

Ответ 1

Согласно Обоснованию международного стандарта - Языки программирования C §6.4.4.1 Целочисленные константы

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

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

 i = 0b101010;

Подробнее см. Здесь.

Ответ 2

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

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

шестнадцатеричный:

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

Ответ 5

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

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

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

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

Ответ 6

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

#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;

Если вы настаиваете на константах времени компиляции, следующее не является общим, но работает для 8 бит.

#define B0_(X) ((X) % 8 + B1_((X)/8) * 2)
#define B1_(X) ((X) % 8 + B2_((X)/8) * 2)
#define B2_(X) ((X) % 8 + B3_((X)/8) * 2)
#define B3_(X) ((X) % 8 + B4_((X)/8) * 2)
#define B4_(X) ((X) % 8 + B5_((X)/8) * 2)
#define B5_(X) ((X) % 8 + B6_((X)/8) * 2)
#define B6_(X) ((X) % 8 + B7_((X)/8) * 2)
#define B7_(X) ((X) % 8)

#define B_(x) B0_(0##x)