繁体   English   中英

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

[英]Why doesn't C have 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 = (1 << 1) | (1 << 2) | (1 << 6); 但我个人认为这比写 `reg = 0b01000110;' 更不清晰。 我同意它不能很好地扩展到 8 位或 16 位架构之外。 并不是说我曾经需要制作 32 位掩码。

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

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

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

 i = 0b101010;

请参阅此处了解详细信息。

这就是推动十六进制成为......十六进制的原因。 “......十六进制表示法的主要用途是计算和数字电子学中二进制编码值的人性化表示...... ”。 如下所示:

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

十六进制:

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

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

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

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

你所有的例子都可以写得更清楚:

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

(我冒昧地将评论修正为从零开始计数,就像大自然所希望的那样。)

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

我认为可读性是首要考虑的问题。 尽管是低级的,但阅读和维护您的代码的是人,而不是机器。

你很容易发现你错误地输入了0b1000000000000000000000000000000(0x40000000) ,你的意思是0b10000000000000000000000000000000(0x80000000)

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

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

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

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

为此,我建议使用 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");
    }
}

我的方法是:

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

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

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

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

以下内容仅限于 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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM