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

lbsnaicq  于 2023-01-25  发布在  其他
关注(0)|答案(9)|浏览(262)

我经常希望我能在c中做这样的事情:

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

拥有这种语法似乎是对C语言的一个非常有用的补充,我想不出它有什么缺点,而且对于一种低级语言来说,这似乎是一件很自然的事情,因为在这种语言中,位操作是相当常见的。
编辑:我看到了其他一些很好的替代方法,但当掩码更复杂时,它们都崩溃了。例如,如果reg是控制微控制器上I/O引脚的寄存器,我想将引脚2、3我可以写reg = 0x46;,但是我必须花10秒钟来思考它(在一两天不看这些代码之后,我每次阅读这些代码时可能都要花10秒钟)或者我可以写reg = (1 << 1) | (1 << 2) | (1 << 6);,但我个人认为这比写'reg = 0b01000110要清楚得多;我同意它不能扩展到超过8位或者16位架构,但我从来没有需要做一个32位的掩码。

bvjxkvbb

bvjxkvbb1#

根据Rationale for International Standard - Programming Languages C §6.4.4.1 * 整型常量 *
由于缺乏先例和实用性不足,添加二进制常量的建议被拒绝。
它不在标准C中,但GCC支持它作为扩展,前缀为0b0B

i = 0b101010;

详情请参见此处。

hk8txs48

hk8txs482#

这就是为什么hexadecimal是...十六进制的原因。“... * 十六进制记数法的主要用途是在计算和数字电子产品中方便人们使用的二进制编码值的表示法... *"。它将如下所示:

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

十六进制:

1.一个十六进制数字可以代表一个半字节(4位或半个八进制)。
1.两个十六进制数字可以表示一个字节(8位)。
1.缩放到较大遮罩时,十六进制要紧凑得多。
经过一些练习,十六进制和二进制之间的转换会变得自然得多。试着用手写出你的转换结果,而不是使用在线的bin/hex符号转换器--那么几天后它就会变得自然(结果也会更快)。

**旁白:**尽管二进制常量不是C标准,但如果你用GCC编译,就可以使用二进制常量,它们应该以'0 b'或'0 B'为前缀。更多信息请参见官方文档here。例如:

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

eivgtgni3#

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

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

(正如《自然》杂志所希望的那样,我冒昧地将评论修改为从零开始计数。)
你的编译器会折叠这些常量,所以这样写它们不会有性能损失,而且它们比0b...版本更容易阅读。

mftmpeh8

mftmpeh84#

我认为可读性是最重要的,虽然是低级的,但是阅读和维护代码的是人,而不是机器。
您是否很容易发现您错误地键入了0b1000000000000000000000000000000(0x40000000),而您真正的意思是0b10000000000000000000000000000000(0x80000000)

ygya80vv

ygya80vv5#

例如,如果reg是控制微控制器上I/O引脚的寄存器
我不禁认为这是一个糟糕的例子,控制寄存器中的位有特定的功能(任何连接到单个IO位的设备也是如此)。
在头文件中为位模式提供符号常量比在代码中计算二进制要明智得多。将二进制转换为十六进制或八进制很简单,记住将01000110写入IO寄存器时会发生什么就不简单了,特别是如果您手边没有数据手册或电路图。
这样,您不仅可以保存10秒钟的时间来计算二进制代码,而且还可以节省更长的时间来计算它的功能!

z3yyvxxp

z3yyvxxp6#

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

#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");
    }
}
vi4fp9gy

vi4fp9gy7#

我的方法是:

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

wlp8pajw8#

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

它一直为我工作,它可能不是便携式的,但谁实际上端口这样的东西无论如何?给予它一个尝试。

j7dteeu8

j7dteeu89#

下面的代码被限制在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)

相关问题