Могу ли я использовать двоичный литерал в C или C ++?


191

Мне нужно работать с двоичным числом.

Я пытался написать:

const x = 00010000;

Но это не сработало.

Я знаю, что могу использовать шестнадцатеричное число с тем же значением, что 00010000и у меня, но я хочу знать, есть ли тип в C ++ для двоичных чисел, и если нет, есть ли другое решение для моей проблемы?


51
Вы знаете, что 00010000это восьмеричное, верно? (И в вашей декларации отсутствует тип.)
Кит Томпсон,

Здесь современный способ с использованием литералов C ++.
Lol4t0

2
C ++ 14 добавил функцию для этого. Смотрите мой новый ответ для более подробной информации в нижней части. Конечно, для этого нужен компилятор, который его реализует.
lpapp

1
@FormlessCloud: это синтаксические правила, приведенные в стандартах C и C ++ ( 0bтолько в C ++ 14). Они разработаны, чтобы быть однозначными.
Кит Томпсон

2
Возможный дубликат двоичных литералов?
MJ Rayburn

Ответы:


70

Вы можете использоватьBOOST_BINARY во время ожидания C ++ 0x. :) BOOST_BINARYвозможно, имеет преимущество перед реализацией шаблона, поскольку его можно использовать и в программах на C (он на 100% управляется препроцессором).

Чтобы сделать обратное (то есть распечатать число в двоичной форме), вы можете использовать непереносимую itoaфункцию или реализовать свою собственную .

К сожалению, вы не можете выполнять форматирование базы 2 с потоками STL (поскольку setbaseучитываете только базы 8, 10 и 16), но вы можете использовать либо std::stringверсию itoa, либо (более краткую, но несколько менее эффективную) std::bitset.

#include <boost/utility/binary.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <bitset>
#include <iostream>
#include <iomanip>

using namespace std;

int main() {
  unsigned short b = BOOST_BINARY( 10010 );
  char buf[sizeof(b)*8+1];
  printf("hex: %04x, dec: %u, oct: %06o, bin: %16s\n", b, b, b, itoa(b, buf, 2));
  cout << setfill('0') <<
    "hex: " << hex << setw(4) << b << ", " <<
    "dec: " << dec << b << ", " <<
    "oct: " << oct << setw(6) << b << ", " <<
    "bin: " << bitset< 16 >(b) << endl;
  return 0;
}

производит:

hex: 0012, dec: 18, oct: 000022, bin:            10010
hex: 0012, dec: 18, oct: 000022, bin: 0000000000010010

Также прочитайте книгу Херба Саттера « Струнные форматеры усадебной фермы» для интересного обсуждения.


2
Как говорит сама страница, на которую вы ссылаетесь, вы можете использовать только 8, 10 или 16 с setbase. Однако:int main() { cout << bitset<8>(42); }

@ Роджер, спасибо за bitsetподсказку, я уже исправил немного setbaseдо того, как увидел твой комментарий.
vladr

Вот учебник по пользовательским литералам в c ++ 11: akrzemi1.wordpress.com/2012/10/23/user-defined-literals-part-ii . Очевидно, что c ++ 1y (он же c ++ 14) будет включать двоичные литералы в стандарт.
Чеширеков

275

Если вы используете GCC, вы можете использовать расширение GCC (которое включено в стандарт C ++ 14 ) для этого:

int x = 0b00010000;

2
Несколько других компиляторов имеют этот или другие подобные способы выражения чисел в базе 2.
nategoose

4
Было бы неплохо стандартизировать это, но clang поддерживает те же обозначения.
Полемон

14
Он работает в Clang, GCC и TCC. Это не работает в PCC. У меня нет другого компилятора для тестирования.
Михас

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


98

Вы можете использовать двоичные литералы. Они стандартизированы в C ++ 14. Например,

int x = 0b11000;

Поддержка в GCC

Поддержка в GCC началась в GCC 4.3 (см. Https://gcc.gnu.org/gcc-4.3/changes.html ) как расширение семейства языков C (см. Https://gcc.gnu.org/onlinedocs/gcc/. C-Extensions.html # C-Extensions ), но, начиная с GCC 4.9, он теперь распознается как функция C ++ 14 или расширение (см. Различие между двоичными литералами GCC и C ++ 14? )

Поддержка в Visual Studio

Поддержка в Visual Studio была запущена в Visual Studio 2015 Preview (см. Https://www.visualstudio.com/news/vs2015-preview-vs#C++ ).


5
Вы можете использовать ', чтобы отделить каждую часть: "0b0000'0100'0100'0001
camino

1
@camino Хорошо, вы можете проиграть первым "
Никос

Это должен быть принятый ответ. Большинство других ответов ооочень устарели.
Алекс

73
template<unsigned long N>
struct bin {
    enum { value = (N%10)+2*bin<N/10>::value };
} ;

template<>
struct bin<0> {
    enum { value = 0 };
} ;

// ...
    std::cout << bin<1000>::value << '\n';

Самая левая цифра литерала все еще должна быть 1, но тем не менее.


4
Лучшая версия: bitbucket.org/kniht/scraps/src/tip/cpp/binary.hpp ( binary<10>::value == binary<010>::valueи некоторые проверки ошибок)

Как-то пропустил этот, прежде чем я опубликовал свой почти идентичный ответ. Но у меня первая цифра должна быть 0, а не 1.
Марк Рэнсом

4
Лучшая версия этого шаблона: code.google.com/p/cpp-binary-constants
Валентин Галеа,

@ValentinGalea - почему версия Google лучше, чем эта?
AJed

Это чертовски впечатляет. Жаль, что это не работает для большого количества битов.
Квантовый физик

31

В некоторых компиляторах (обычно для микроконтроллеров ) есть специальная функция, реализованная в распознавании буквенных двоичных чисел по префиксу «0b ...», предшествующему числу, хотя большинство компиляторов (стандарты C / C ++) не имеют такой возможности, и если это случай, вот мое альтернативное решение:

#define B_0000    0
#define B_0001    1
#define B_0010    2
#define B_0011    3
#define B_0100    4
#define B_0101    5
#define B_0110    6
#define B_0111    7
#define B_1000    8
#define B_1001    9
#define B_1010    a
#define B_1011    b
#define B_1100    c
#define B_1101    d
#define B_1110    e
#define B_1111    f

#define _B2H(bits)    B_##bits
#define B2H(bits)    _B2H(bits)
#define _HEX(n)        0x##n
#define HEX(n)        _HEX(n)
#define _CCAT(a,b)    a##b
#define CCAT(a,b)   _CCAT(a,b)

#define BYTE(a,b)        HEX( CCAT(B2H(a),B2H(b)) )
#define WORD(a,b,c,d)    HEX( CCAT(CCAT(B2H(a),B2H(b)),CCAT(B2H(c),B2H(d))) )
#define DWORD(a,b,c,d,e,f,g,h)    HEX( CCAT(CCAT(CCAT(B2H(a),B2H(b)),CCAT(B2H(c),B2H(d))),CCAT(CCAT(B2H(e),B2H(f)),CCAT(B2H(g),B2H(h)))) )

// Using example
char b = BYTE(0100,0001); // Equivalent to b = 65; or b = 'A'; or b = 0x41;
unsigned int w = WORD(1101,1111,0100,0011); // Equivalent to w = 57155; or w = 0xdf43;
unsigned long int dw = DWORD(1101,1111,0100,0011,1111,1101,0010,1000); //Equivalent to dw = 3745774888; or dw = 0xdf43fd28;

Недостатки (это не такие большие):

  • Двоичные числа должны быть сгруппированы 4 на 4;
  • Двоичные литералы должны быть только целыми числами без знака;

Преимущества :

  • Всего управляет препроцессор, не spending processor timeв бессмысленных операциях (like "?.. :..", "<<", "+" ) с исполняемой программой (это может быть выполнено сто раз в конечном приложении);
  • Работает и "mainly in C"компиляторы, и C ++ (template+enum solution works only in C++ compilers );
  • У него есть только ограничение «долготы» для выражения «буквальных констант» значений. Было бы раннее ограничение длинны (обычно 8 битов: 0-255), если бы было выражено постоянное значение путем анализа разрешения"enum solution" (usually 255 = reach enum definition limit) , иначе «буквальной константы», в компиляторе допускаются большие числа;
  • Некоторые другие решения требуют преувеличенного числа константных определений (на мой взгляд, слишком многих определений), в том числе длинных или several header files(в большинстве случаев не легко читаемых и понятных), и заставляют проект становиться излишне запутанным и расширенным, например, используя"BOOST_BINARY()" );
  • Простота решения: легко читаемый, понятный и настраиваемый для других случаев (может быть расширен для групп 8 на 8);

Почему например B_0100не используется (вместо 0100)? Как например char b = BYTE(0100,0001);.
Питер Мортенсен,

@PeterMortensen B_ добавляется _B2Hфункцией препроцессора.
mxmlnkn

20

Эта тема может помочь.

/* Helper macros */
#define HEX__(n) 0x##n##LU
#define B8__(x) ((x&0x0000000FLU)?1:0) \
+((x&0x000000F0LU)?2:0) \
+((x&0x00000F00LU)?4:0) \
+((x&0x0000F000LU)?8:0) \
+((x&0x000F0000LU)?16:0) \
+((x&0x00F00000LU)?32:0) \
+((x&0x0F000000LU)?64:0) \
+((x&0xF0000000LU)?128:0)

/* User macros */
#define B8(d) ((unsigned char)B8__(HEX__(d)))
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) \
+ B8(dlsb))
#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \
+ ((unsigned long)B8(db2)<<16) \
+ ((unsigned long)B8(db3)<<8) \
+ B8(dlsb))


#include <stdio.h>

int main(void)
{
    // 261, evaluated at compile-time
    unsigned const number = B16(00000001,00000101);

    printf("%d \n", number);
    return 0;
}

Оно работает! (Все кредиты отправляются Тому Торфсу.)


я не очень понял (я новичок в программировании и особенно в C ++), но это кажется интересным, поэтому я попытаюсь понять это после еще нескольких исследований C ++, спасибо
hamza

3
Макрос B8 работает путем преобразования двоичного литерала в шестнадцатеричный литерал и извлечения каждого 4-го бита.
Ден04

Интересно, что означает 0x ## n ## LU? Никогда не сталкивался с таким синтаксисом.
Федерико А. Рампони

@ Хамза: это действительно довольно сложно. Но что вам нужно понять, это только с #include <stdio> и далее.
Федерико А. Рампони

8
@Federico: ##оператор препроцессора вставляет токены вместе. Таким образом, в этом случае, если вы звоните HEX__(10), он расширяется до 0x10LU.
Джеймс МакНеллис

18

Как уже было сказано, стандарты C не имеют возможности напрямую записывать двоичные числа. Однако есть расширения компилятора, и, очевидно, C ++ 14 включает0b префикс для двоичного кода. (Обратите внимание, что этот ответ был первоначально опубликован в 2010 году.)

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

#define B00000000 0
#define B00000001 1
#define B00000010 2

Это приводит только к 256 #defineс, и, если требуются бинарные константы размером более 8 бит, эти определения могут быть объединены со сдвигами и OR, возможно, с вспомогательными макросами (например, BIN16(B00000001,B00001010)). (Наличие отдельных макросов для каждого 16-разрядного, не говоря уже о 32-разрядном значении не представляется возможным.)

Конечно, недостатком является то, что этот синтаксис требует записи всех начальных нулей, но это также может сделать его более понятным для таких применений, как установка битовых флагов и содержимого аппаратных регистров. Для функционально-подобного макроса, приводящего к синтаксису без этого свойства, см. bithacks.hСсылку выше.


2
Итак, какой размер файла должен прочитать CPP, если у вас есть все макросы для long long int?
wilhelmtell

3
@wilhelmtell: И каково это, когда я указал «все 8-битные шаблоны» (= 256 строк) и предложил объединить большие количества из этих? Даже BOOST_BINARY принятого ответа определяет все 8-битные шаблоны в заголовке ...
Arkku

16

Чрезмерное мышление C ++ уже хорошо учтено в других ответах здесь. Вот моя попытка сделать это с помощью мышления C, keep-it-simple-ffs:

unsigned char x = 0xF; // binary: 00001111

12

C не имеет нативной записи для чистых двоичных чисел. Ваша лучшая ставка здесь будет либо восьмеричной (например 07777) шестнадцатеричной (например 0xfff).


11

Вы можете использовать функцию из этого вопроса, чтобы получить до 22 бит в C ++. Вот код из ссылки, соответственно отредактированный:

template< unsigned long long N >
struct binary
{
  enum { value = (N % 8) + 2 * binary< N / 8 > :: value } ;
};

template<>
struct binary< 0 >
{
  enum { value = 0 } ;
};

Таким образом, вы можете сделать что-то вроде binary<0101011011>::value.


7

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

Что касается целочисленных литералов, вы можете работать только с десятичными (основание 10), восьмеричными (основание 8) или шестнадцатеричными (основание 16) числами. В C и C ++ нет двоичных литералов (base 2).

Восьмеричные числа имеют префикс, 0а шестнадцатеричные числа - 0x. Десятичные числа не имеют префикса.

В C ++ 0x вы сможете делать то, что вы хотите, кстати, через пользовательские литералы .


могу ли я хотя бы показать двоичное значение шестнадцатеричного числа в функции печати или cout?
Хамза

Да, вы можете <shameless_plug> stackoverflow.com/questions/2611764#2611883 </shameless_plug>
vladr

5
Некоторые компиляторы C поддерживают 0b100101 для двоичных литералов, но, к сожалению, это нестандартное расширение.
Джои Адамс

3
Обратите внимание, что, хотя это не определено в стандарте, некоторые компиляторы (в частности, для микроконтроллеров и встроенных систем) добавляют синтаксис для двоичного кода в форму 0b00101010для удобства. SDCC один, и я уверен, что есть и другие, которые тоже. (Правка: Ха, побей меня, @Джоу!)
Мэтт Б.

5

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

int i;

__asm {
    mov eax, 00000000000000000000000000000000b
    mov i,   eax
}

std::cout << i;

Хорошо, это может быть несколько излишним, но это работает.


3
Ваше решение не мультиплатформенное. Во многих архитектурах вы не можете включить ассемблерный код в C. В частности, в компилятор Microsoft Visual Studio вы можете (при компиляции для 32-битных x86). Но как вы узнаете, есть ли у вашего процессора регистр eax? Подумайте о процессорах ARM в мобильных телефонах, процессорах x64 и т. Д. У них нет «eax». У процессора MIPS даже нет команды 'mov'
DanielHsH

4

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

template<bool> struct BinaryLiteralDigit;

template<> struct BinaryLiteralDigit<true> {
    static bool const value = true;
};

template<unsigned long long int OCT, unsigned long long int HEX>
struct BinaryLiteral {
    enum {
        value = (BinaryLiteralDigit<(OCT%8 < 2)>::value && BinaryLiteralDigit<(HEX >= 0)>::value
            ? (OCT%8) + (BinaryLiteral<OCT/8, 0>::value << 1)
            : -1)
    };
};

template<>
struct BinaryLiteral<0, 0> {
    enum {
        value = 0
    };
};

#define BINARY_LITERAL(n) BinaryLiteral<0##n##LU, 0x##n##LU>::value

Пример:

#define B BINARY_LITERAL

#define COMPILE_ERRORS 0

int main (int argc, char ** argv) {
    int _0s[] = { 0, B(0), B(00), B(000) };
    int _1s[] = { 1, B(1), B(01), B(001) };
    int _2s[] = { 2, B(10), B(010), B(0010) };
    int _3s[] = { 3, B(11), B(011), B(0011) };
    int _4s[] = { 4, B(100), B(0100), B(00100) };

    int neg8s[] = { -8, -B(1000) };

#if COMPILE_ERRORS
    int errors[] = { B(-1), B(2), B(9), B(1234567) };
#endif

    return 0;
}

3

«Тип» двоичного числа совпадает с любым десятичным, шестнадцатеричным или восьмеричным числом: int (или даже символ, короткий, длинный длинный).

Когда вы назначаете константу, вы не можете назначить ее с 11011011 (любопытно и к сожалению), но вы можете использовать hex. Гекс немного легче мысленно перевести. Разбить на кусочки (4 бита) и перевести на символ в [0-9a-f].


2

Вы можете использовать битсет

bitset<8> b(string("00010000"));
int i = (int)(bs.to_ulong());
cout<<i;

2

Я расширил хороший ответ, данный @ renato-chandelier, обеспечив поддержку:

  • _NIBBLE_(…) - 4 бита, 1 клев в качестве аргумента
  • _BYTE_(…) - 8 бит, 2 клевы в качестве аргумента
  • _SLAB_(…) - 12 бит, 3 клевы в качестве аргумента
  • _WORD_(…) - 16 бит, 4 клев в качестве аргумента
  • _QUINTIBBLE_(…) - 20 бит, 5 кусков в качестве аргументов
  • _DSLAB_(…) - 24 бита, 6 кусков в качестве аргументов
  • _SEPTIBBLE_(…) - 28 бит, 7 кусков в качестве аргументов
  • _DWORD_(…) - 32 бита, 8 кусков в качестве аргументов

Я на самом деле не так уверен в терминах «quintibble» и «septibble». Если кто-нибудь знает какую-либо альтернативу, пожалуйста, дайте мне знать.

Вот макрос, переписанный:

#define __CAT__(A, B) A##B
#define _CAT_(A, B) __CAT__(A, B)

#define __HEX_0000 0
#define __HEX_0001 1
#define __HEX_0010 2
#define __HEX_0011 3
#define __HEX_0100 4
#define __HEX_0101 5
#define __HEX_0110 6
#define __HEX_0111 7
#define __HEX_1000 8
#define __HEX_1001 9
#define __HEX_1010 a
#define __HEX_1011 b
#define __HEX_1100 c
#define __HEX_1101 d
#define __HEX_1110 e
#define __HEX_1111 f

#define _NIBBLE_(N1) _CAT_(0x, _CAT_(__HEX_, N1))
#define _BYTE_(N1, N2) _CAT_(_NIBBLE_(N1), _CAT_(__HEX_, N2))
#define _SLAB_(N1, N2, N3) _CAT_(_BYTE_(N1, N2), _CAT_(__HEX_, N3))
#define _WORD_(N1, N2, N3, N4) _CAT_(_SLAB_(N1, N2, N3), _CAT_(__HEX_, N4))
#define _QUINTIBBLE_(N1, N2, N3, N4, N5) _CAT_(_WORD_(N1, N2, N3, N4), _CAT_(__HEX_, N5))
#define _DSLAB_(N1, N2, N3, N4, N5, N6) _CAT_(_QUINTIBBLE_(N1, N2, N3, N4, N5), _CAT_(__HEX_, N6))
#define _SEPTIBBLE_(N1, N2, N3, N4, N5, N6, N7) _CAT_(_DSLAB_(N1, N2, N3, N4, N5, N6), _CAT_(__HEX_, N7))
#define _DWORD_(N1, N2, N3, N4, N5, N6, N7, N8) _CAT_(_SEPTIBBLE_(N1, N2, N3, N4, N5, N6, N7), _CAT_(__HEX_, N8))

А вот пример использования Ренато:

char b = _BYTE_(0100, 0001); /* equivalent to b = 65; or b = 'A'; or b = 0x41; */
unsigned int w = _WORD_(1101, 1111, 0100, 0011); /* equivalent to w = 57155; or w = 0xdf43; */
unsigned long int dw = _DWORD_(1101, 1111, 0100, 0011, 1111, 1101, 0010, 1000); /* Equivalent to dw = 3745774888; or dw = 0xdf43fd28; */

0

Просто используйте стандартную библиотеку в C ++:

#include <bitset>

Вам нужна переменная типа std::bitset:

std::bitset<8ul> x;
x = std::bitset<8>(10);
for (int i = x.size() - 1; i >= 0; i--) {
      std::cout << x[i];
}

В этом примере я сохранил двоичную форму 10в x.

8ulопределяет размер ваших битов, 7ulзначит, семь битов и так далее.



-9

Вы можете попробовать использовать массив bool:

bool i[8] = {0,0,1,1,0,1,0,1}

2
Много негативов, без объяснений. Вот ваше объяснение: stackoverflow.com/questions/2064550/c-why-bool-is-8-bits-long Кроме того, каждый элемент в массиве находится по своему адресу памяти. Но нам нужна последовательность упакованных 1 и 0 по одному адресу.
JMI MADISON
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.