为什么C没有二进制文字?

问题描述 投票:34回答:7

我经常希望我可以在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位掩码。

c syntax binary literals
7个回答
38
投票

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

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

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

 i = 0b101010;

请参见here以获取详细信息。


16
投票

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

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

十六进制:

  1. 一个十六进制数字可以表示一个半字节(4位或八进制的一半)。
  2. 两个十六进制数字可以表示一个字节(8位)。
  3. 当缩放到更大的蒙版时,十六进制要紧凑得多。

通过一些实践,在十六进制和二进制之间进行转换将变得更加自然。尝试手动写出转换,而不使用在线的bin / hex表示法转换器-几天之内它就会变得自然(结果更快)。

Aside:即使二进制文字不是C标准,但如果您使用GCC进行编译,也可以使用二进制文字,但它们应以'0b'或'0B'作为前缀。有关更多信息,请参见官方文档here。示例:

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

9
投票

您的所有示例都可以写得更清楚:

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

((我采取了将评论固定为从零开始计数的自由,就像《自然》杂志所提出的。)

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


8
投票

我认为可读性是主要关注的问题。尽管级别很低,但是阅读和维护您的代码的是人,而不是机器。

您是否容易发现自己误输入了0b1000000000000000000000000000000(0x40000000),而您的意思是0b10000000000000000000000000000000(0x80000000)


3
投票

“例如,如果reg是控制微控制器上I / O引脚的寄存器,”

我不禁以为这是一个不好的例子。控制寄存器中的位具有特定功能(与连接到各个IO位的任何设备一样)。

在头文件中为位模式提供符号常量,而不是在代码中计算出二进制数,这将更为明智。将二进制转换为十六进制或八进制是微不足道的,要记住将01000110写入IO寄存器时不会发生什么,尤其是在没有数据手册或电路图的情况下。

然后,您不仅可以节省出10秒钟的时间来尝试执行二进制代码,而且还可以花费更长的时间来尝试执行二进制代码!


-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

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


-3
投票

以下内容仅限于8位,尽管应该直接扩展。虽然它不会产生C文字,但是会产生编译时间常数。

#define B_(X) B8_("00000000" #X)
#define B8_(X) B8__(X+sizeof(X)-9)
#define B8__(X) \
        (B___((X), 7) | B___((X), 6) | B___((X), 5) | B___((X), 4) | \
         B___((X), 3) | B___((X), 2) | B___((X), 1) | B___((X), 0))
#define B__(X, I) (((X)[7-(I)] - '0') << (I))

Try it online!

如果性能不是问题,则可以做一些简单的事情:

#define B_(x) strtoull(#x, 0, 2)
© www.soinside.com 2019 - 2024. All rights reserved.