简体   繁体   English

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

[英]Why doesn't C have binary literals?

I am frequently wishing I could do something like this in c:我经常希望我能在 c 中做这样的事情:

val1 &= 0b00001111; //clear high nibble
val2 |= 0b01000000; //set bit 7
val3 &= ~0b00010000; //clear bit 5

Having this syntax seems like an incredibly useful addition to C with no downsides that I can think of, and it seems like a natural thing for a low level language where bit-twiddling is fairly common.拥有这种语法似乎是对 C 的一个非常有用的补充,没有我能想到的缺点,而且对于一种位旋转相当普遍的低级语言来说,这似乎是很自然的事情。

Edit: I'm seeing some other great alternatives but they all fall apart when there is a more complex mask.编辑:我看到了其他一些很棒的选择,但是当有更复杂的面具时,它们都会分崩离析。 For example, if reg is a register that controls I/O pins on a microcontroller, and I want to set pins 2, 3, and 7 high at the same time I could write reg = 0x46;例如,如果reg是控制微控制器上 I/O 引脚的寄存器,我想同时将引脚 2、3 和 7 设置为高电平,我可以编写reg = 0x46; but I had to spend 10 seconds thinking about it (and I'll likely have to spend 10 seconds again every time I read those code after a not looking at it for a day or two) or I could write reg = (1 << 1) | (1 << 2) | (1 << 6);但是我不得不花 10 秒来考虑它(在一两天不看这些代码后,我每次阅读这些代码时可能都得再花 10 秒)或者我可以写reg = (1 << 1) | (1 << 2) | (1 << 6); reg = (1 << 1) | (1 << 2) | (1 << 6); but personally I think that is way less clear than just writing `reg = 0b01000110;'但我个人认为这比写 `reg = 0b01000110;' 更不清晰。 I can agree that it doesn't scale well beyond 8 bit or maybe 16 bit architectures though.我同意它不能很好地扩展到 8 位或 16 位架构之外。 Not that I've ever needed to make a 32 bit mask.并不是说我曾经需要制作 32 位掩码。

According to Rationale for International Standard - Programming Languages C §6.4.4.1 Integer constants根据国际标准的基本原理 - 编程语言 C §6.4.4.1整数常量

A proposal to add binary constants was rejected due to lack of precedent and insufficient utility.由于缺乏先例和实用性不足,添加二进制常量的提议被拒绝。

It's not in standard C, but GCC supports it as an extension, prefixed by 0b or 0B :它不在标准 C 中,但 GCC 支持将其作为扩展名,以0b0B为前缀:

 i = 0b101010;

See here for detail.请参阅此处了解详细信息。

This is what pushed hexadecimal to be... hexadecimal.这就是推动十六进制成为......十六进制的原因。 The "... primary use of hexadecimal notation is a human-friendly representation of binary-coded values in computing and digital electronics ... ". “......十六进制表示法的主要用途是计算和数字电子学中二进制编码值的人性化表示...... ”。 It would be as follows:如下所示:

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

Hexadecimal:十六进制:

  1. One hex digit can represent a nibble (4 bits or half an octal).一个十六进制数字可以代表一个半字节(4 位或半个八进制)。
  2. Two hex digits can represent a byte (8 bits).两个十六进制数字可以表示一个字节(8 位)。
  3. Hex is much more compact when scaling to larger masks.当缩放到更大的掩码时,十六进制要紧凑得多。

With some practice, converting between hexadecimal and binary will become much more natural.通过一些练习,十六进制和二进制之间的转换将变得更加自然。 Try writing out your conversions by hand and not using an online bin/hex notation converter -- then in a couple days it will become natural (and quicker as a result).尝试手动写出您的转换,而不是使用在线 bin/hex 符号转换器 - 然后几天后它会变得自然(因此更快)。

Aside: Even though binary literals are not a C standard, if you compile with GCC it is possible to use binary literals, they should be prefixed with '0b' or '0B'.旁白:尽管二进制文字不是 C 标准,但如果您使用 GCC 进行编译,则可以使用二进制文字,它们应该以“0b”或“0B”为前缀。 See the official documentation here for further information.有关更多信息,请参阅此处的官方文档。 Example:例子:

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

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.这些比0b...版本更容易阅读。

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) ?你很容易发现你错误地输入了0b1000000000000000000000000000000(0x40000000) ,你的意思是0b10000000000000000000000000000000(0x80000000)

"For example, if reg is a register that controls I/O pins on a microcontroller" “例如,如果 reg 是控制微控制器上 I/O 引脚的寄存器”

I can't help thinking this is a bad example.我不禁认为这是一个不好的例子。 Bits in control registers have specific functions (as will any devices connected to individual IO bits).控制寄存器中的位具有特定功能(与连接到各个 IO 位的任何设备一样)。

It would be far more sensible to provide symbolic constants for bit patterns in a header file, rather than working out the binary within the code.在头文件中为位模式提供符号常量会更明智,而不是在代码中计算二进制。 Converting binary to hexadecimal or octal is trivial, remembering what happens when you write 01000110 to an IO register is not, particularly if you don't have the datasheet or circuit diagram handy.将二进制转换为十六进制或八进制是微不足道的,记住将 01000110 写入 IO 寄存器时会发生什么,尤其是当您手头没有数据表或电路图时。

You will then not only save those 10 seconds trying to work out the binary code, but maybe the somewhat longer time trying to work out what it does!然后,您不仅可以节省 10 秒尝试计算二进制代码的时间,而且可能会节省一些更长的时间来尝试计算它的作用!

I recommend C macros in C for this to avoid compiler warnings or other problems.为此,我建议使用 C 中的 C 宏以避免编译器警告或其他问题。 Instead of 0x I use Ox (like in "Ohio").我使用 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");
    }
}

My approach was:我的方法是:

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

Which preprocesses to...哪些预处理...

$  cpp binmacro.h
0xD4

0xD2C5

Binary is most useful when setting specific outputs on a controller.在控制器上设置特定输出时,二进制最有用。 I use a hack which is technically illegal but nonetheless always works.我使用了一种技术上非法但仍然有效的黑客。 If you just need to turn an LED on it offends every sensibility to use a whole int, or even a char for the job.如果您只需要打开一个 LED,它就会冒犯使用整个 int 甚至是 char 来完成工作的所有敏感性。 Don't forget we're probably not talking about the ultimate in compilation sophistication for these things.不要忘记,我们可能不是在谈论这些东西的终极编译复杂度。 So, for individual intelligibility combined with group control I use bitfields :-因此,对于结合组控制的个人可理解性,我使用位域:-

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;

They're easily addressed when used singly, or for more thorough control they can be treated as an unsigned int in flagrant disregard of K&R:-它们在单独使用时很容易解决,或者为了更彻底的控制,它们可以被视为 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

It's always worked for me, it's probably not portable, but then who actually ports something like this anyhow?它总是对我有用,它可能不便携,但是无论如何谁实际上移植了这样的东西? Give it a go.搏一搏。

The following is limited to 8 bits, although it should be straightforward to extend.以下内容仅限于 8 位,但扩展起来应该很简单。 While it does not result in a C literal, it does result in a compile time constant.虽然它不会产生 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))

The following function is compiled into code that returns the constant 18 .以下函数被编译成返回常量18代码。

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

Try it online! 在线试试吧!

If performance is not an issue, you can do something simpler:如果性能不是问题,您可以做一些更简单的事情:

#define B_(x) strtoull(#x, 0, 2)

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

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