二进制文字?
在代码中,我有时会看到人们以hex格式指定常量,如下所示:
const int has_nukes = 0x0001; const int has_bio_weapons = 0x0002; const int has_chem_weapons = 0x0004; // ... int arsenal = has_nukes | has_bio_weapons | has_chem_weapons; // all of them if(arsenal &= has_bio_weapons){ std::cout << "BIO!!" }
但在这里使用hex格式对我来说没有任何意义。 有没有办法直接在二进制? 像这样的东西:
const int has_nukes = 0b00000000000000000000000000000001; const int has_bio_weapons = 0b00000000000000000000000000000010; const int has_chem_weapons = 0b00000000000000000000000000000100; // ...
我知道C / C ++编译器不会编译这个,但是必须有一个解决方法? 在Java等其他语言中可能吗?
我会使用一个移位运算符:
const int has_nukes = 1<<0; const int has_bio_weapons = 1<<1; const int has_chem_weapons = 1<<2; // ... int dangerous_mask = has_nukes | has_bio_weapons | has_chem_weapons; bool is_dangerous = (country->flags & dangerous_mask) == dangerous_mask;
比0的洪水还要好。
在C ++ 14中,您将能够使用以下语法使用二进制文字:
0b010101010 /* more zeros and ones */
这个特性已经在最新的clang
和gcc
。 如果使用-std=c++1y
选项运行这些编译器,可以试试它。
顺便说一下,下一个C ++版本将支持用户定义的文字。 他们已经被纳入工作草案。 这允许这样的东西(让我们希望我没有太多的错误):
template<char... digits> constexpr int operator "" _b() { return conv2bin<digits...>::value; } int main() { int const v = 110110110_b; }
conv2bin
将会是这样一个模板:
template<char... digits> struct conv2bin; template<char high, char... digits> struct conv2bin<high, digits...> { static_assert(high == '0' || high == '1', "no bin num!"); static int const value = (high - '0') * (1 << sizeof...(digits)) + conv2bin<digits...>::value; }; template<char high> struct conv2bin<high> { static_assert(high == '0' || high == '1', "no bin num!"); static int const value = (high - '0'); };
那么,由于上面的“constexpr”,我们得到的是在编译时已经完全评估的二进制文字。 上面使用了一个硬编码的int返回types。 我想甚至可以使它取决于二进制string的长度。 它使用以下function,对任何感兴趣的人:
- 广义常量expression式。
- 可变模板 。 在这里可以find一个简短的介绍
- 静态断言(static_assert)
- 用户定义的文字
实际上,目前的GCC中继已经实现了可变模板和静态断言。 我们希望很快能支持另外两个。 我认为C ++ 1x会震动房子。
你可以使用<<如果你喜欢。
int hasNukes = 1; int hasBioWeapons = 1 << 1; int hasChemWeapons = 1 << 2;
C ++标准库是你的朋友:
#include <bitset> const std::bitset <32> has_nukes( "00000000000000000000000000000001" );
自4.3开始,GCC支持二进制常量作为扩展。 请参阅公告 (请参阅“新语言和语言特定改进”一节)。
你想要的术语是二进制文字
Ruby有他们给你的语法。
另一种方法是定义助手macros来为你转换。 我在http://bytes.com/groups/c/219656-literal-binaryfind了下面的代码;
/* Binary constant generator macro By Tom Torfs - donated to the public domain */ /* All macro's evaluate to compile-time constants */ /* *** helper macros *** / /* turn a numeric literal into a hex constant (avoids problems with leading zeroes) 8-bit constants max value 0x11111111, always fits in unsigned long */ #define HEX__(n) 0x##n##LU /* 8-bit conversion function */ #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 *** / /* for upto 8-bit binary constants */ #define B8(d) ((unsigned char)B8__(HEX__(d))) /* for upto 16-bit binary constants, MSB first */ #define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) \ + B8(dlsb)) /* for upto 32-bit binary constants, MSB first */ #define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \ + ((unsigned long)B8(db2)<<16) \ + ((unsigned long)B8(db3)<<8) \ + B8(dlsb)) /* Sample usage: B8(01010101) = 85 B16(10101010,01010101) = 43605 B32(10000000,11111111,10101010,01010101) = 2164238933 */
这个讨论可能是有趣的 …可能已经,因为链接是不幸的死了。 它描述了一个类似于其他答案的基于模板的方法。
还有一个叫BOOST_BINARY的东西。
C ++的下一个版本C ++ 0x将引入用户定义的文字 。 我不确定是否二进制数字将成为标准的一部分,但最坏的情况下,您可以自己启用它:
int operator "" _B(int i); assert( 1010_B == 10);
我写这样的二进制文字:
const int has_nukes = 0x0001; const int has_bio_weapons = 0x0002; const int has_chem_weapons = 0x0004;
它比您的build议符号更紧凑,更易于阅读。 例如:
const int upper_bit = 0b0001000000000000000;
与:
const int upper_bit = 0x04000;
你有没有注意到二进制版本不是4位的倍数? 你认为这是0x10000吗?
有一点练习hex或八进制对于人来说比二进制更容易。 而且,在我看来,使用移位操作符更容易阅读。 但是我会承认,我多年的汇编语言工作可能会使我对这一点产生偏见。
Java不支持二进制文字,不幸的是。 但是,它具有可以与EnumSet
一起使用的枚举 。 EnumSet
内部用位字段表示枚举值,并提供一个用于操作这些标志的Set
接口。
或者,您可以在定义值时使用位偏移 (十进制):
const int HAS_NUKES = 0x1 << 0; const int HAS_BIO_WEAPONS = 0x1 << 1; const int HAS_CHEM_WEAPONS = 0x1 << 2;
我同意有一个二进制文字选项是很有用的,它们以许多编程语言存在。 在C中,我决定使用这样一个macros:
#define bitseq(a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \ a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \ (a31|a30<< 1|a29<< 2|a28<< 3|a27<< 4|a26<< 5|a25<< 6|a24<< 7| \ a23<< 8|a22<< 9|a21<<10|a20<<11|a19<<12|a18<<13|a17<<14|a16<<15| \ a15<<16|a14<<17|a13<<18|a12<<19|a11<<20|a10<<21|a09<<22|a08<<23| \ a07<<24|a06<<25|a05<<26|a04<<27|a03<<28|a02<<29|a01<<30|(unsigned)a00<<31)
用法非常简单=)
一个稍微可怕的方法是通过生成一个带有大量#define的.h文件。
#define b00000000 0 #define b00000001 1 #define b00000010 2 #define b00000011 3 #define b00000100 4
等等。这可能对8位数有意义,但可能不适用于16位或更大。
或者,这样做(类似于Zach Scrivena的回答):
#define bit(x) (1<<x) int HAS_NUKES = bit(HAS_NUKES_OFFSET); int HAS_BIO_WEAPONS = bit(HAS_BIO_WEAPONS_OFFSET);
在C ++中没有文字二进制常量的语法,有hex和八进制。 看起来最接近你想要做的事情可能是学习和使用bitset 。
另外:
尤其是如果你正在处理一个大集合,而不是通过写一系列移位量的[小事]心理努力,你可以使每个常量依赖于以前定义的常量:
const int has_nukes = 1; const int has_bio_weapons = has_nukes << 1; const int has_chem_weapons = has_bio_weapons << 1; const int has_nunchuks = has_chem_weapons << 1; // ...
看起来有点多余,但是不太容易出错。 而且,你可以简单地在中间插入一个新的常量,而不必触碰除紧跟在其后面的那一行之外的任何其他行:
const int has_nukes = 1; const int has_gravity_gun = has_nukes << 1; // added const int has_bio_weapons = has_gravity_gun << 1; // changed const int has_chem_weapons = has_bio_weapons << 1; // unaffected from here on const int has_nunchuks = has_chem_weapons << 1; // ...
相比于:
const int has_nukes = 1 << 0; const int has_bio_weapons = 1 << 1; const int has_chem_weapons = 1 << 2; const int has_nunchuks = 1 << 3; // ... const int has_scimatar = 1 << 28; const int has_rapier = 1 << 28; // good luck spotting this typo! const int has_katana = 1 << 30;
和:
const int has_nukes = 1 << 0; const int has_gravity_gun = 1 << 1; // added const int has_bio_weapons = 1 << 2; // changed const int has_chem_weapons = 1 << 3; // changed const int has_nunchuks = 1 << 4; // changed // ... // changed all the way const int has_scimatar = 1 << 29; // changed *sigh* const int has_rapier = 1 << 30; // changed *sigh* const int has_katana = 1 << 31; // changed *sigh*
除此之外,可能同样很难发现这样的错字:
const int has_nukes = 1; const int has_gravity_gun = has_nukes << 1; const int has_bio_weapons = has_gravity_gun << 1; const int has_chem_weapons = has_gravity_gun << 1; // oops! const int has_nunchuks = has_chem_weapons << 1;
所以,我认为这种级联语法的主要优点是在处理常量的插入和删除时。
另一种方法:
template<unsigned int N> class b { public: static unsigned int const x = N; typedef b_<0> _0000; typedef b_<1> _0001; typedef b_<2> _0010; typedef b_<3> _0011; typedef b_<4> _0100; typedef b_<5> _0101; typedef b_<6> _0110; typedef b_<7> _0111; typedef b_<8> _1000; typedef b_<9> _1001; typedef b_<10> _1010; typedef b_<11> _1011; typedef b_<12> _1100; typedef b_<13> _1101; typedef b_<14> _1110; typedef b_<15> _1111; private: template<unsigned int N2> struct b_: public b<N << 4 | N2> {}; }; typedef b<0> _0000; typedef b<1> _0001; typedef b<2> _0010; typedef b<3> _0011; typedef b<4> _0100; typedef b<5> _0101; typedef b<6> _0110; typedef b<7> _0111; typedef b<8> _1000; typedef b<9> _1001; typedef b<10> _1010; typedef b<11> _1011; typedef b<12> _1100; typedef b<13> _1101; typedef b<14> _1110; typedef b<15> _1111;
用法:
std::cout << _1101::_1001::_1101::_1101::x;
在CityLizard ++(citylizard / binary / b.hpp)中实现 。
如果你想使用bitset,auto,variadic模板,用户定义的文字,static_assert,constexpr 和 noexcept,请试试这个:
template<char... Bits> struct __checkbits { static const bool valid = false; }; template<char High, char... Bits> struct __checkbits<High, Bits...> { static const bool valid = (High == '0' || High == '1') && __checkbits<Bits...>::valid; }; template<char High> struct __checkbits<High> { static const bool valid = (High == '0' || High == '1'); }; template<char... Bits> inline constexpr std::bitset<sizeof...(Bits)> operator"" bits() noexcept { static_assert(__checkbits<Bits...>::valid, "invalid digit in binary string"); return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'}); }
像这样使用它:
int main() { auto bits = 0101010101010101010101010101010101010101010101010101010101010101bits; std::cout << bits << std::endl; std::cout << "size = " << bits.size() << std::endl; std::cout << "count = " << bits.count() << std::endl; std::cout << "value = " << bits.to_ullong() << std::endl; // This triggers the static_assert at compile-time. auto badbits = 2101010101010101010101010101010101010101010101010101010101010101bits; // This throws at run-time. std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101bits"); }
感谢@ johannes-schaub-litb