47

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

4

9 回答 9

52

根据国际标准的基本原理 - 编程语言 C §6.4.4.1整数常量

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

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

 i = 0b101010;

详情见这里

于 2013-08-15T01:06:40.747 回答
21

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

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

十六进制:

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

通过一些练习,十六进制和二进制之间的转换将变得更加自然。尝试手动写出您的转换,而不是使用在线二进制/十六进制符号转换器 - 然后在几天内它会变得自然(并且因此更快)。

旁白:即使二进制文字不是 C 标准,如果您使用 GCC 编译,也可以使用二进制文字,它们应该以 '0b' 或 '0B' 为前缀。有关详细信息,请参阅此处的官方文档。例子:

int b1 = 0b1001; // => 9
int b2 = 0B1001; // => 9
于 2013-08-15T01:01:09.817 回答
15

All of your examples can be written even more clearly:

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

(I have taken the liberty of fixing the comments to count from zero, like Nature intended.)

Your compiler will fold these constants, so there is no performance penalty to writing them this way. And these are easier to read than the 0b... versions.

于 2013-08-15T01:15:14.263 回答
11

I think readability is a primary concern. Although low-level, it's human beings who read and maintain your code, not machine.

Is it easy for you to figure out that you mistakenly typed 0b1000000000000000000000000000000(0x40000000), where you really mean 0b10000000000000000000000000000000(0x80000000) ?

于 2013-08-15T01:18:24.300 回答
3

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

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

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

然后,您不仅可以节省 10 秒尝试计算二进制代码的时间,而且可能会花费更长的时间来计算它的功能!

于 2015-08-14T10:51:32.173 回答
2

为此,我建议在 C 中使用 C 宏以避免编译器警告或其他问题。我使用 Ox 而不是 0x(就像在“Ohio”中一样)。

#define Ob00000001 1
#define Ob10000000 (1 << (8-1))
#define Ob00001111 15
#define Ob11110000_8 (Ob00001111 << (8 - 4))
#define Ob11110000_16 (Ob00001111 << (16 - 4))
#define Ob11110000_32 (((uint32_t) Ob00001111) << (32 - 4))
#define Ob11110000_64 (((uint64_t) Ob00001111) << (64 - 4))
#define Ox0F Ob00001111
#define OxF0 Ob11110000_8
#define OxF000 Ob11110000_16
#define OxF0000000 Ob11110000_32
#define OxF000000000000000 Ob11110000_64

int main() {
    #define Ob00001110 14
    // bitwise operations work
    if (Ob00001110 == (Ob00001111 & ~Ob00000001)) {
        printf("true\n");
    }
}
于 2020-12-27T21:22:20.253 回答
0

在控制器上设置特定输出时,二进制最有用。我使用的 hack 在技术上是非法的,但仍然有效。如果您只需要打开一个 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;

它们在单独使用时很容易解决,或者为了更彻底的控制,它们可以被视为无符号整数,公然无视 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

它总是对我有用,它可能不是便携式的,但是无论如何,谁真正移植了这样的东西?搏一搏。

于 2019-02-19T23:56:29.043 回答
0

我的方法是:

/* binmacro.h */

#define BX_0000 0
#define BX_0001 1
#define BX_0010 2
#define BX_0011 3
#define BX_0100 4
#define BX_0101 5
#define BX_0110 6
#define BX_0111 7
#define BX_1000 8
#define BX_1001 9
#define BX_1010 A
#define BX_1011 B
#define BX_1100 C
#define BX_1101 D
#define BX_1110 E
#define BX_1111 F

#define BIN_A(x) BX_ ## x

#define BIN_B(x,y) 0x ## x ## y
#define BIN_C(x,y) BIN_B(x,y)

#define BIN_B4(x,y,z,t) 0x ## x ## y ## z ## t
#define BIN_C4(x,y,z,t) BIN_B4(x,y,z,t)

#define BIN(x,y) BIN_C(BIN_A(x),BIN_A(y))
#define BIN4(x,y,z,t) BIN_C4(BIN_A(x),BIN_A(y),BIN_A(z),BIN_A(t))

/*---- test ... ---*/

BIN(1101,0100)

BIN4(1101,0010,1100,0101)

哪些预处理...

$  cpp binmacro.h
0xD4

0xD2C5
于 2021-08-26T02:16:10.930 回答
-2

以下限制为 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))

下面的函数被编译成返回常量的代码18

int test(void) {
    return B_(10010);
}

在线尝试!

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

#define B_(x) strtoull(#x, 0, 2)
于 2013-08-15T02:07:54.187 回答