我能否在C或C++中使用二进制字面量?

241

我需要处理一个二进制数。

我尝试写了以下代码:

const char x = 00010000;
但这并没有起作用。 我知道可以使用一个十六进制数,它具有与00010000相同的值,但我想知道在C++中是否有一种二进制数类型,如果没有,是否有其他解决方案?

61
你知道00010000是八进制吗?(而且你的声明缺少类型。) - Keith Thompson
2
C++14增加了这个功能。请查看我在底部的新答案以获取更多详细信息。当然,它需要一个实现它的编译器。 - László Papp
@KeithThompson 这些是用来唯一识别给定基数的数字的约定吗?因为 x 不是十六进制 (A, B, C, D, E, F),在二进制或十六进制数字前面写零不会影响它们中的任何一个,所以选择 0b 作为二进制的前缀是否是任意的呢? - Aurelius
1
@FormlessCloud:这些是C和C++标准中给出的语法规则(0b仅出现在C++14中)。它们旨在保持明确无歧义。 - Keith Thompson
3
可能是二进制字面量? 的重复问题。 - M.J. Rayburn
显示剩余4条评论
24个回答

327

如果您使用的是GCC编译器,那么可以使用GCC扩展(已包含在C++14标准中)来实现这一点:

int x = 0b00010000;

4
最好能够将此标准化,但clang支持相同的表示法。 - polemon
16
它在Clang、GCC和TCC中可用。它在PCC中不可用。我没有其他编译器进行测试。 - Michas
7
我看过很多支持它的嵌入式系统编译器。我不知道为什么它不能成为一种标准语言特性。 - supercat
5
@polemon http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3472.pdf (C++14.) - Jonathan Baldwin
2
记录一下,遗憾的是,MSVC2010不支持这个。 - Phlucious
显示剩余3条评论

146
您可以使用二进制文字。它们在C++14中已经标准化。例如,
int x = 0b11000;

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的特性或扩展(请参阅Difference between GCC binary literals and C++14 ones?)。

Visual Studio的支持

在Visual Studio 2015 Preview中开始支持(请参阅https://www.visualstudio.com/news/vs2015-preview-vs#C++)。


16
这是一个二进制数,可以将其翻译为十六进制或十进制。在十六进制中,它等于0x041,而在十进制中,它等于65。 - camino
2
@camino 很好,你可以去掉第一个“ - KeyC0de
2
这应该是被接受的答案。其他大多数答案都太过时了。 - Alex

76
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
更好的版本:http://bitbucket.org/kniht/scraps/src/tip/cpp/binary.hpp(`binary<10>::value == binary<010>::value`,并带有一些错误检查) - Roger Pate
在我发布几乎相同的答案之前,不知何故错过了这个。但是在我的答案中,领先的数字必须为0,而不是1。 - Mark Ransom
4
这个模板想法的改进版本:http://code.google.com/p/cpp-binary-constants/ - Valentin Galea
@ValentinGalea - 为什么谷歌版本比这个更好? - AJed
这太令人印象深刻了。可惜它不能处理高位数。 - The Quantum Physicist

72

在等待C++0x到来时,你可以使用 BOOST_BINARY。相比模板实现,BOOST_BINARY 的优点是它也可以在 C 语言中使用(它完全是预处理器驱动的)。

如果要反过来(即以二进制形式输出数字),你可以使用不可移植的itoa函数自己实现

不幸的是,在STL流中无法进行2进制格式化(因为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

同时阅读Herb Sutter的The String Formatters of Manor Farm,这篇文章讨论得非常有趣。


2
正如您链接的页面所说,您只能在setbase中使用8、10或16。但是:int main() { cout << bitset<8>(42); } - Roger Pate
@Roger 感谢你的 bitset 提示,不过在看到你的评论之前我已经纠正了关于 setbase 的错误。 - vladr
这是一篇关于C++11中用户自定义字面量的教程:http://akrzemi1.wordpress.com/2012/10/23/user-defined-literals-part-ii/. 显然,C++1y(又称C++14)将在标准中包含二进制字面量。 - cheshirekow

34

一些编译器(通常是针对微控制器的编译器)实现了一个特殊功能,通过在数字前加上前缀"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个为一组分组;
  • 二进制字面量必须仅为无符号整数。

优点

  • 完全由预处理器驱动,不会让可执行程序在无意义操作(例如"?.. :..", "<<", "+")中花费处理器时间(它可能在最终应用程序中执行数百次);
  • 它主要适用于C编译器和C++编译器 (模板+枚举解决方案仅适用于C++编译器);
  • 它仅对"字面常量"值表达"长度"存在限制。如果使用"枚举解决方案"的解析结果表达常量值,则通常会有早期的长度限制(通常为8位:0-255),相反,在编译器中允许更大的数字的"字面常量"限制;
  • 某些其他解决方案需要过多的常量定义(我认为定义太多),包括长或多个头文件(在大多数情况下不易读和理解,并使项目变得不必要地混乱和扩展,例如使用"BOOST_BINARY()");
  • 解决方案的简单性:易于阅读、理解和调整以适应其他情况(也可以扩展为每8个一组)。

为什么不使用例如 B_0100(而是使用 0100)?例如在 char b = BYTE(0100,0001); 中。 - Peter Mortensen
@PeterMortensen _B2H 预处理器函数会添加 B_。 - mxmlnkn

21

这个帖子可能会有所帮助。

/* 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;
}

成功了!(感谢Tom Torfs的所有贡献。)


3
B8宏通过将“二进制”文字转换为十六进制文字,并提取每4位,来实现其功能。 - dan04
我想知道0x##n##LU是什么意思?从未遇到过这样的语法。 - Federico A. Ramponi
@hamza:确实相当复杂。但你需要理解的只是从#include<stdio>开始的部分。 - Federico A. Ramponi
8
## 预处理操作符会将标记粘贴在一起。因此,在这种情况下,如果调用 HEX__(10),它会扩展为 0x10LU - James McNellis
奇怪的是,这组宏将为以下代码产生一些有趣的结果: B16(ABCDEF89,01234567); 不过,非常聪明! - abelenky
显示剩余2条评论

20

正如先前回答的那样,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
那么,如果您拥有long long int的所有宏定义,CPP需要读取多大的文件? - wilhelmtell
3
当我指定“所有8位模式”(= 256行)并建议从中组合更大数量时,这有何相关性?即使接受的答案中BOOST_BINARY也在头文件中定义了所有8位模式。请翻译此内容。 - Arkku
不确定是点赞还是踩。一方面,这很聪明,因为在编译之前进行了简单的文本替换。而且你可以简单地编写一个生成器,以创建比8位更大的数据类型的头文件。另一方面,我不知道预处理器可以处理多少#define,也不知道它们是否可以高效地处理。也许可以使用帮助宏与##连接来减少必要定义的数量。 - tangoal
如我在答案中所说,我只建议用于8位的宏,并明确表示我认为16位或更高位不可行,并且我还建议将更高位(超过8位)与辅助宏组合使用。因此,如果您拒绝投票,我希望原因不是因为老生常谈的“没有看答案,但这不适用于超过8位”的问题,因为这并非本意。=) - Arkku
关于使用##进行连接,我认为这需要一个语法,例如BIN16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1),但我认为这并不比答案中建议的BIN16(B00000001,B00001010)更好。或者你有其他想法吗?(不可否认,具有智能自动完成功能的编辑器可能有助于前一种语法,并放置带有占位符的确切参数数量,尽管这不太容易手动输入,当您必须计数到16时。) - Arkku

19

这里已经有其他答案对于C++过度工程化的心态进行了详细说明。以下是我尝试用C的“保持简单”的思路来解决:

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

13

C语言没有原生的纯二进制数表示法。你最好使用八进制(例如07777)或十六进制(例如0xfff)。


11

您可以使用此问题中找到的函数来在C++中获取高达22位的二进制常量。以下是链接中适当编辑过的代码:

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接