为什么C不具有二进制文字?

时间:2013-08-15 00:57:14

标签: c syntax binary literals

我经常希望我能在c:

中做这样的事情
val1 &= 0b00001111; //clear high nibble
val2 |= 0b01000000; //set bit 7
val3 &= ~0b00010000; //clear bit 5

使用这种语法似乎是C的一个非常有用的补充,没有我能想到的缺点,对于低级语言来说似乎是很自然的事情,其中​​比特笨拙很常见。

编辑:我看到了其他一些很好的选择,但是当有更复杂的面具时它们都会崩溃。例如,如果reg是一个控制微控制器上I / O引脚的寄存器,我想将引脚2,3和7设置为高电平,同时我可以写reg = 0x46;但我有花10秒时间考虑一下(每次看完这些代码后我都不得不再花10秒钟看一两天)或者我可以写reg = (1 << 1) | (1 << 2) | (1 << 6);但我个人认为这不仅仅是写'reg = 0b01000110;'。我同意它不会超出8位或16位架构。并不是说我需要制作一个32位的掩码。

6 个答案:

答案 0 :(得分:34)

根据Rationale for International Standard - Programming Languages C§6.4.4.1整数常量

  

由于缺乏先例和效用不足,提议添加二进制常量。

它不在标准C中,但GCC支持它作为扩展名,前缀为0b0B

 i = 0b101010;

有关详细信息,请参阅here

答案 1 :(得分:15)

这就是推动hexadecimal为...十六进制的原因。 “... 十六进制表示法的主要用途是计算和数字电子设备中二进制编码值的人性友好表示...... ”。它将如下:

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

<强>十六进制:

  1. 一个十六进制数字可以表示半字节(4位或半个八进制)。
  2. 两个十六进制数字可以表示一个字节(8位)。
  3. 当缩放到更大的蒙版时,十六进制更加紧凑。
  4. 通过一些练习,十六进制和二进制之间的转换将变得更加自然。尝试手动写出你的转换,而不是使用在线bin / hex符号转换器 - 然后在几天内它将变得自然(并且因此更快)。

    旁白:尽管二进制文字不是C标准,但如果使用GCC进行编译,则可以使用二进制文字,它们应该以“0b”或“0B”作为前缀。有关详细信息,请参阅官方文档here。例如:

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

答案 2 :(得分:9)

您可以更清楚地编写所有示例:

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

(我已经冒昧地将评论从零开始计算,就像大自然一样。)

您的编译器将折叠这些常量,因此以这种方式编写它们不会有性能损失。这些比0b...版本更容易阅读。

答案 3 :(得分:7)

我认为可读性是一个主要问题。虽然是低级别的,但是人类会阅读并维护您的代码,而不是机器。

您是否容易弄清楚您错误地键入了0b1000000000000000000000000000000(0x40000000),您的真正意思是0b10000000000000000000000000000000(0x80000000)

答案 4 :(得分:-1)

在控制器上设置特定输出时,二进制最有用。我使用的骇客在技术上是非法的,但始终可以使用。如果您只需要打开一个LED,则冒犯了使用整个int甚至是char进行工作的所有敏感性。别忘了我们可能没有在谈论这些事情的最终复杂性。因此,对于结合组控制的个人清晰度,我使用位域:-

struct DEMAND
{
    unsigned int dOil   :   1; // oil on
    unsigned int dAir   :   1; // air on
    unsigned int dHeat  :   1; // heater on
    unsigned int dMtr1  :   1; // motor 1 on
    unsigned int dMtr2  :   1; // motor 2 on
    unsigned int dPad1  :   10;// spare demand o/p's
    unsigned int dRunCycle: 1; // GO !!!!
    unsigned int dPad2  :   15;// spare o/p's
    unsigned int dPowerOn:  1; // Power on
}DemandBF;

单独使用它们很容易解决,或者为了更彻底的控制,可以将它们视为未签名的int,而无视K&R:-

void *bitfPt = &DemandBF;
unsigned int *GroupOuts = (unsigned int *)bitfPt;

DemandBF.dAir = 1;   // Clearly describes what's turning on
DemandBF.dPowerOn = 1;

*GroupOuts ^= 0x04; // toggle the heater

*GroupOuts = 0; // kill it all

它一直为我工作,它可能不是便携式的,但是究竟是谁移植了这样的东西呢?放手吧。

答案 5 :(得分:-2)

如果您不需要实际的文字,您可以这样做:

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