在C语言代码中编写二进制数系统

54
作为十六进制数我们使用前缀0x,而对于八进制数,我们使用前缀0,那么对于二进制数有没有什么方法呢?
我尝试使用后缀b,但GCC不允许。

错误: 整数常量的后缀无效“b”

这是否可能?

4
C语言中不存在二进制文字。最接近的是十六进制,因为它们紧密遵循二进制位模式。 - Some programmer dude
2
十六进制转二进制非常容易。 - user1944441
3个回答

91

标准C并没有定义二进制常量。不过,GNU C提供了一个扩展(在流行的编译器中,clang也支持它):使用0b0B前缀:

int foo = 0b1010;

如果您想坚持使用标准C,那么有一个选项:您可以结合宏和函数创建一个几乎可读的“二进制常量”功能。
#define B(x) S_to_binary_(#x)

static inline unsigned long long S_to_binary_(const char *s)
{
        unsigned long long i = 0;
        while (*s) {
                i <<= 1;
                i += *s++ - '0';
        }
        return i;
}

然后你可以像这样使用它:

int foo = B(1010);

如果您开启了强大的编译器优化,编译器很可能会完全消除函数调用(常量折叠),或者至少将其内联,因此这甚至不会成为性能问题。 证明: 以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>


#define B(x) S_to_binary_(#x)

static inline unsigned long long S_to_binary_(const char *s)
{
    unsigned long long i = 0;
    while (*s) {
        i <<= 1;
        i += *s++ - '0';
    }
    return i;
}

int main()
{
    int foo = B(001100101);

    printf("%d\n", foo);

    return 0;
}

使用clang -o baz.S baz.c -Wall -O3 -S编译后,生成以下汇编代码:

    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp4:
    .cfi_def_cfa_register %rbp
    leaq    L_.str1(%rip), %rdi
    movl    $101, %esi               ## <= This line!
    xorb    %al, %al
    callq   _printf
    xorl    %eax, %eax
    popq    %rbp
    ret
    .cfi_endproc

    .section    __TEXT,__cstring,cstring_literals
L_.str1:                                ## @.str1
    .asciz   "%d\n"


.subsections_via_symbols

所以clang完全消除了对函数的调用,并将其返回值替换为101。很整洁,是吧?

11
@Lundin 嗯?重点在于可读性以及使用二进制字面量 - user529758
13
@Lundin 我有反对吗?OP问“这怎么可能”,我已经向他展示了如何做到。现在是时候让我一个人静一静了。 - user529758
2
@Lundin,你没有理解重点。有人认为八进制和十六进制字面量已经足够了,虽然它们足够用,但如果想要一个位模式,读取11001001101比读取0xc02更容易。我不知道这对你有什么影响,但对我来说,十六进制字面量的二进制表示并不是一眼就能看出来的。也许对你来说很明显。 - user529758
11
@Lundin 在许多情况下,许多人会发现二进制数字更易读。实际上,当定义具有大量标志的枚举时,许多程序员将在注释中注明每个常量的二进制值。事实上,现在C++提供了用户定义的字面量,许多人实现的第一个扩展就是用于二进制数字字面量的。 - Konrad Rudolph
4
作为一个曾经与大量嵌入式硬件打交道的人,我告诉你:我们常常需要处理二进制值,这些值在 CPU 的数据总线上并不从最低位开始。当然,在这种情况下,采用宏命令来获取数值并进行移位可以很方便地解决可读性的问题,但有时候直接指定二进制字面量也是非常方便的。我发现随着对嵌入式编程越来越熟悉,我对二进制字面量的“需求”几乎降至零,但在刚开始时,它们似乎会很有用。 - phonetagger
显示剩余9条评论

16

将您的字面值前缀加上0b,就像这样

int i = 0b11111111;

这里


13
这是一个扩展功能,可能需要gcc的特殊标志才能理解。当然,在不支持该扩展功能的其他编译器上无法移植。 - Some programmer dude

7

使用BOOST_BINARY库(是的,你可以在C语言中使用它)。

#include <boost/utility/binary.hpp>
...
int bin = BOOST_BINARY(110101);

在预处理期间,此宏将扩展为八进制字面量。


当我使用BOOST_BINARY和一个变量时,会出现错误:#include <stdio.h> #include <boost/utility/binary.hpp> int main() { int bin; printf("输入二进制数:"); scanf("%i", &bin); printf("十进制值为 %d", BOOST_BINARY(bin)); return 0; }错误提示为error: macro "BOOST_DETAIL_SPLIT_AND_SWAP_PARAMS" requires 2 arguments, but only 1 given。 - john
2
在大多数程序中混合使用像boost这样的大型库并不是一个好主意,特别是如果它们被设计成小而快速编译。由于C已经通过扩展支持了它,我建议任何人都围绕它构建,并使用版本宏进行切换。 - user2262111

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接