是否有printf转换器可以二进制格式打印?


434

我可以用printf作为十六进制或八进制数字进行打印。是否有格式标签可以打印为二进制或任意基数?

我正在运行gcc。

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
print("%b\n", 10); // prints "%b\n"

据我所知,您无法使用printf执行此操作。显然,您可以编写一个辅助方法来完成此操作,但这听起来并不像您想要的方向。
伊恩·P

没有为此预定义的格式。您需要自己将其转换为字符串,然后打印该字符串。
rslite

快速的Google搜索在此页面上显示了一些可能有用的信息:forums.macrumors.com/archive/index.php/t-165959.html
Ian P

12
不属于ANSI标准C库的一部分-如果您正在编写可移植的代码,最安全的方法是自行滚动。
tomlogic 2010年

在C ++上转换为二进制字符串的一种语句标准和通用(对于任何长度的任何整数类型)的解决方案:stackoverflow.com/a/31660310/1814353
luart

Answers:


266

哈基,但对我有用:

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)  \
  (byte & 0x80 ? '1' : '0'), \
  (byte & 0x40 ? '1' : '0'), \
  (byte & 0x20 ? '1' : '0'), \
  (byte & 0x10 ? '1' : '0'), \
  (byte & 0x08 ? '1' : '0'), \
  (byte & 0x04 ? '1' : '0'), \
  (byte & 0x02 ? '1' : '0'), \
  (byte & 0x01 ? '1' : '0') 
printf("Leading text "BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(byte));

对于多字节类型

printf("m: "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN"\n",
  BYTE_TO_BINARY(m>>8), BYTE_TO_BINARY(m));

不幸的是,您需要所有额外的报价。这种方法具有使用宏的效率风险(不要将函数作为参数传递给BYTE_TO_BINARY),但可以避免内存问题以及在此处其他一些建议中对strcat的多次调用。


13
并具有多次调用的优势printf,而带有static缓冲区的调用则不能多次调用。
PatrickSchlüter10年

4
我已经自由地将to更改%d%c,因为它应该更快(%d必须执行digit-> char转换,而%c只是输出参数

3
发布了具有
16、32、64

2
请注意,此方法不是堆栈友好的。假设系统上int为32位,则打印单个32位值将需要32 * 4字节值的空间;总共128个字节。取决于堆栈大小,这可能是问题,也可能不是问题。
user694733 '17

1
在宏中的字节周围加上括号很重要,否则在发送操作BYTE_TO_BINARY(a | b)-> a |时可能会遇到问题。b&0x01!=(a | b)&0x01
伊万·霍夫曼

203

打印任何数据类型的二进制文件

//assumes little endian
void printBits(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char*) ptr;
    unsigned char byte;
    int i, j;

    for (i=size-1;i>=0;i--)
    {
        for (j=7;j>=0;j--)
        {
            byte = (b[i] >> j) & 1;
            printf("%u", byte);
        }
    }
    puts("");
}

测试

int main(int argv, char* argc[])
{
        int i = 23;
        uint ui = UINT_MAX;
        float f = 23.45f;
        printBits(sizeof(i), &i);
        printBits(sizeof(ui), &ui);
        printBits(sizeof(f), &f);
        return 0;
}

8
建议size_t i; for (i=size; i-- > 0; )避免size_tint不匹配。
chux-恢复莫妮卡

1
有人可以详细说明此代码背后的逻辑吗?
2014年

2
接收每个字节ptr(外循环);然后针对当前位的每个位(内部循环),用当前位(1 << j)屏蔽该字节。向右移,得到一个包含0(0000 0000b)或1(0000 0001b)的字节。用format输出结果字节printf %u。HTH。
nielsbot '16

1
@ ZX9注意,建议的代码>与注释的size_t和一起使用,而不是>=确定终止循环的时间。
chux-恢复莫妮卡

3
@ ZX9还是您的一个有用的原始注释,因为编码人员确实需要考虑考虑>>=与unsigned类型一起使用的情况 。 0是无符号边缘情况,通常会发生,这与不常见的带符号数学不同INT_MAX/INT_MIN
chux-恢复莫妮卡

151

这是一个快速技巧,可以演示实现所需功能的技术。

#include <stdio.h>      /* printf */
#include <string.h>     /* strcat */
#include <stdlib.h>     /* strtol */

const char *byte_to_binary
(
    int x
)
{
    static char b[9];
    b[0] = '\0';

    int z;
    for (z = 128; z > 0; z >>= 1)
    {
        strcat(b, ((x & z) == z) ? "1" : "0");
    }

    return b;
}

int main
(
    void
)
{
    {
        /* binary string to int */

        char *tmp;
        char *b = "0101";

        printf("%d\n", strtol(b, &tmp, 2));
    }

    {
        /* byte to binary string */

        printf("%s\n", byte_to_binary(5));
    }

    return 0;
}

2
这肯定比自定义为printf编写转义过载的“怪异”要少。对于刚接触该代码的开发人员来说,也很容易理解。
Furious Coder

43
进行一些更改:这strcat是在循环的每次通过中向字符串添加单个char的低效方法。而是添加a char *p = b;并将内部循环替换为*p++ = (x & z) ? '1' : '0'z应该从128(2 ^ 7)开始而不是256(2 ^ 8)。考虑更新以获取指向要使用的缓冲区的指针(出于线程安全),类似于inet_ntoa()
tomlogic 2010年

3
@EvilTeach:您自己正在使用三元运算符作为strcat()!的参数。我同意,这strcat可能比后增加为作业分配的已取消引用的指针更容易理解,但即使是初学者也需要知道如何正确使用标准库。也许使用索引数组进行赋值将是一个很好的演示(并且实际上将起作用,因为b每次调用该函数都不会重置为全零)。
tomlogic

3
随机:二进制缓冲区char是静态的,并且在赋值中被清除为全零。这只会在第一次运行时清除它,此后它不会清除,而是使用最后一个值。
markwatson

8
另外,这应该证明在再次调用该函数后,先前的结果将无效,因此调用者不应尝试像这样使用它:printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4))
圣保罗Ebermann

84

通常,glibc中没有二进制转换说明符。

可以将自定义转换类型添加到glibc中的printf()系列函数中。有关详细信息,请参见register_printf_function。如果可以简化应用程序代码的使用,则可以添加自定义%b转换供您自己使用。

这是一个如何在glibc中实现自定义printf格式的示例


我总是为需要不同基数的有限情况编写自己的v [snf] printf():很高兴我浏览了此内容。
杰米

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]不过,有一个新功能可以做到这一点register_printf_specifier()。可以在此处找到新用法的示例:codereview.stackexchange.com/q/219994/200418
Cacahuete Frito,

47

您可以使用一张小桌子来提高速度1。类似的技术在嵌入式世界中很有用,例如,反转一个字节:

const char *bit_rep[16] = {
    [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
    [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
    [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
    [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111",
};

void print_byte(uint8_t byte)
{
    printf("%s%s", bit_rep[byte >> 4], bit_rep[byte & 0x0F]);
}

1我主要是指嵌入式应用程序,其中的优化器并不那么积极,并且速度差异可见。


27

打印最低有效位,然后向右移。这样做直到整数变为零,才打印二进制表示形式,但不带前导零,但顺序相反。使用递归,可以很容易地纠正顺序。

#include <stdio.h>

void print_binary(int number)
{
    if (number) {
        print_binary(number >> 1);
        putc((number & 1) ? '1' : '0', stdout);
    }
}

对我来说,这是解决问题的最干净的方法之一。如果您喜欢0b前缀和尾随换行符,建议您包装该函数。

在线演示


错误:函数调用的参数太少,预期为2,具有1个putc((number&1)?'1':'0');
Koray Tugay 2015年

@KorayTugay感谢您指出这一点。我更正了函数调用并添加了一个演示。
danijar

6
您还应该使用unsigned int数字,因为当给定数字为负数时,该函数将输入永无休止的递归调用。
浮肿

更有效的方法,因为在ASCII中为'0'+ 1 ='1':putc('0'+(number&1), stdout);
Roger Dueck

22

基于@William怀特的回答,这是提供了一个宏int8163264版本,再使用INT8宏来避免重复。

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16             PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32             PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#include <stdio.h>
int main() {
    long long int flag = 1648646756487983144ll;
    printf("My Flag "
           PRINTF_BINARY_PATTERN_INT64 "\n",
           PRINTF_BYTE_TO_BINARY_INT64(flag));
    return 0;
}

输出:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

为了提高可读性,您可能需要添加一个分隔符,例如:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000

太好了 打印从最低有效位开始的位是否有特定原因?
gaganso

2
您如何建议添加逗号?
nmz787

将添加PRINTF_BYTE_TO_BINARY_INT#定义的分组版本以供选择使用。
ideaman42 '17

16

这是该函数的一个版本,它没有重新输入问题或参数大小/类型的限制:

#define FMT_BUF_SIZE (CHAR_BIT*sizeof(uintmax_t)+1)
char *binary_fmt(uintmax_t x, char buf[static FMT_BUF_SIZE])
{
    char *s = buf + FMT_BUF_SIZE;
    *--s = 0;
    if (!x) *--s = '0';
    for(; x; x/=2) *--s = '0' + x%2;
    return s;
}

请注意,如果仅将2替换为所需的基数,则此代码对于2到10之间的任何基数同样适用。用法是:

char tmp[FMT_BUF_SIZE];
printf("%s\n", binary_fmt(x, tmp));

哪里x是整数表达式。


7
是的,你可以做到。但这确实是糟糕的设计。即使您没有线程或可重入,调用者也必须知道静态缓冲区正在被重用,并且类似的东西char *a = binary_fmt(x), *b = binary_fmt(y);将无法按预期工作。强制调用者传递缓冲区会使存储要求显式;当然,如果确实需要,调用者可以自由使用静态缓冲区,然后对同一缓冲区的重用就变得很明确。还应注意,在现代PIC ABI上,静态缓冲区的访问代码通常比堆栈上的缓冲区花费更多的代码。
R .. GitHub停止帮助ICE

8
那仍然是一个糟糕的设计。在这种情况下,它需要一个额外的复制步骤,并且即使在不需要复制的情况下,它比让调用者提供缓冲区便宜。使用静态存储只是一个坏习惯。
R .. GitHub STOP HELPING ICE

3
必须用不必要的额外名称来污染预处理器或变量符号表的名称空间,必须使用该额外名称来适当调整每个调用方必须分配的存储空间,并迫使每个调用方知道此值并分配必要的数量当更简单的本地函数存储解决方案满足大多数意图和目的,并且简单的strdup()调用涵盖了其余99%的使用时,存储就不是一个好设计。
格雷格·伍兹

5
在这里,我们将不得不不同意。我看不到如何在严重限制使用情况,使接口易于出错,在程序执行期间为永久值保留永久存储以及在大多数情况下生成更糟糕的代码的危害附近添加一个不引人注目的预处理器符号现代平台。
R .. GitHub STOP HELPING ICE

5
我不提倡无缘无故进行微优化(即测量)。但是我确实认为,即使是微增益级的性能,当它作为一项奖金以及基本的卓越设计时,也值得一提。
R .. GitHub停止帮助ICE

13
const char* byte_to_binary( int x )
{
    static char b[sizeof(int)*8+1] = {0};
    int y;
    long long z;
    for (z=1LL<<sizeof(int)*8-1,y=0; z>0; z>>=1,y++)
    {
        b[y] = ( ((x & z) == z) ? '1' : '0');
    }

    b[y] = 0;

    return b;
}

6
如果在三元数中使用'1''0'而不是4948,则更清楚。另外,b长度应为9个字符,以便最后一个字符可以保留为空终止符。
tomlogic

另外,B每次都需要初始化。
EvilTeach 2012年

2
如果您更改某些值,则不会:1.为最后的零添加空间:static char b[9] = {0}2.将声明移出循环:int z,y;3.添加最后的零:b[y] = 0。这样就不需要重新安装。
Kobor42 2012年

1
不错的解决方案。我会改变一些东西。即在字符串中向后移动,以便可以正确处理任何大小的输入。
Kobor42 2012年

所有这些8都应替换为CHAR_BIT
2016年

12

快速简便的解决方案:

void printbits(my_integer_type x)
{
    for(int i=sizeof(x)<<3; i; i--)
        putchar('0'+((x>>(i-1))&1));
}

适用于任何大小类型以及有符号和无符号整数。需要使用“&1”来处理带符号的整数,因为移位可能会进行符号扩展。

有很多方法可以做到这一点。这是一种超级简单的打印机,它可以打印32位或32位有符号或无符号类型的n位(如果带符号则不放负号,只打印实际位)并且不回车。请注意,在移位之前,i会递减:

#define printbits_n(x,n) for (int i=n;i;i--,putchar('0'|(x>>i)&1))
#define printbits_32(x) printbits_n(x,32)

返回带有要存储或以后打印的位的字符串呢?您可以分配内存并返回它,而用户必须释放它,否则,您返回一个静态字符串,但是如果再次调用它,或者被另一个线程调用,它将被破坏。两种方法都显示:

char *int_to_bitstring_alloc(int x, int count)
{
    count = count<1 ? sizeof(x)*8 : count;
    char *pstr = malloc(count+1);
    for(int i = 0; i<count; i++)
        pstr[i] = '0' | ((x>>(count-1-i))&1);
    pstr[count]=0;
    return pstr;
}

#define BITSIZEOF(x)    (sizeof(x)*8)

char *int_to_bitstring_static(int x, int count)
{
    static char bitbuf[BITSIZEOF(x)+1];
    count = (count<1 || count>BITSIZEOF(x)) ? BITSIZEOF(x) : count;
    for(int i = 0; i<count; i++)
        bitbuf[i] = '0' | ((x>>(count-1-i))&1);
    bitbuf[count]=0;
    return bitbuf;
}

致电:

// memory allocated string returned which needs to be freed
char *pstr = int_to_bitstring_alloc(0x97e50ae6, 17);
printf("bits = 0b%s\n", pstr);
free(pstr);

// no free needed but you need to copy the string to save it somewhere else
char *pstr2 = int_to_bitstring_static(0x97e50ae6, 17);
printf("bits = 0b%s\n", pstr2);

10

以前发布的答案都不是我要找的答案,所以我写了一个。将%B与printf!一起使用非常简单。

    /*
     * File:   main.c
     * Author: Techplex.Engineer
     *
     * Created on February 14, 2012, 9:16 PM
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <printf.h>
    #include <math.h>
    #include <string.h>


    static int printf_arginfo_M(const struct printf_info *info, size_t n, int *argtypes) {
        /* "%M" always takes one argument, a pointer to uint8_t[6]. */
        if (n > 0) {
            argtypes[0] = PA_POINTER;
        }
        return 1;
    } /* printf_arginfo_M */

    static int printf_output_M(FILE *stream, const struct printf_info *info, const void *const *args) {
        int value = 0;
        int len;

        value = *(int **) (args[0]);

        //Beginning of my code ------------------------------------------------------------
        char buffer [50] = ""; //Is this bad?
        char buffer2 [50] = ""; //Is this bad?
        int bits = info->width;
        if (bits <= 0)
            bits = 8; // Default to 8 bits

        int mask = pow(2, bits - 1);
        while (mask > 0) {
            sprintf(buffer, "%s", (((value & mask) > 0) ? "1" : "0"));
            strcat(buffer2, buffer);
            mask >>= 1;
        }
        strcat(buffer2, "\n");
        // End of my code --------------------------------------------------------------
        len = fprintf(stream, "%s", buffer2);
        return len;
    } /* printf_output_M */

    int main(int argc, char** argv) {

        register_printf_specifier('B', printf_output_M, printf_arginfo_M);

        printf("%4B\n", 65);

        return (EXIT_SUCCESS);
    }

1
会有超过50位溢出吗?
Janus Troelsen

好话,是的...我被告知我需要使用malloc,曾经这么做吗?
TechplexEngineer 2012年

当然是。超级简单:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen'3

@JanusTroelsen,或者说更清洁,更小,更易于维护:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

为什么在这方面“%B”与“%b”有什么不同?以前的答案说的诸如“ C标准库中没有格式化功能来输出这样的二进制文件”。和“ 某些运行时支持“%b”,尽管这不是标准。”
彼得·莫滕森


7

此代码最多可以处理您的64位。我创建了2个函数pBin和pBinFill。两者都做同样的事情,但是pBinFill用fillChar填充前导空格。测试功能会生成一些测试数据,然后使用该功能将其打印出来。



char* pBinFill(long int x,char *so, char fillChar); // version with fill
char* pBin(long int x, char *so);                   // version without fill
#define kDisplayWidth 64

char* pBin(long int x,char *so)
{
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';  // determine bit
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 i++;   // point to last valid character
 sprintf(so,"%s",s+i); // stick it in the temp string string
 return so;
}

char* pBinFill(long int x,char *so, char fillChar)
{ // fill in array from right to left
 char s[kDisplayWidth+1];
 int  i=kDisplayWidth;
 s[i--]=0x00;   // terminate string
 do
 { // fill in array from right to left
  s[i--]=(x & 1) ? '1':'0';
  x>>=1;  // shift right 1 bit
 } while( x > 0);
 while(i>=0) s[i--]=fillChar;    // fill with fillChar 
 sprintf(so,"%s",s);
 return so;
}

void test()
{
 char so[kDisplayWidth+1]; // working buffer for pBin
 long int val=1;
 do
 {
   printf("%ld =\t\t%#lx =\t\t0b%s\n",val,val,pBinFill(val,so,'0'));
   val*=11; // generate test data
 } while (val < 100000000);
}

Output:
00000001 =  0x000001 =  0b00000000000000000000000000000001
00000011 =  0x00000b =  0b00000000000000000000000000001011
00000121 =  0x000079 =  0b00000000000000000000000001111001
00001331 =  0x000533 =  0b00000000000000000000010100110011
00014641 =  0x003931 =  0b00000000000000000011100100110001
00161051 =  0x02751b =  0b00000000000000100111010100011011
01771561 =  0x1b0829 =  0b00000000000110110000100000101001
19487171 = 0x12959c3 =  0b00000001001010010101100111000011

1
“ #define width 64”与log4cxx中的stream.h冲突。请使用传统的随机定义名称:)
kagali-san 2011年

5
@mhambra:您应该告诉log4cxx width不要使用这样的通用名称!
u0b34a0f6ae 2011年

7

是否有printf转换器可以二进制格式打印?

printf()系列只能直接使用标准说明符以8、10和16为基数进行打印。我建议创建一个函数,以根据代码的特殊需要将数字转换为字符串。


以任何基础打印[2-36]

到目前为止,所有其他答案至少具有这些限制之一。

  1. 将静态内存用作返回缓冲区。这限制了该函数可以用作的参数的次数printf()

  2. 分配需要调用代码的内存以释放指针。

  3. 要求调用代码显式提供合适的缓冲区。

  4. printf()直接致电。这迫使了一个新的功能fprintf()sprintf()vsprintf(),等。

  5. 使用缩小的整数范围。

以下内容没有上述限制。它确实需要C99或更高版本并使用"%s"。它使用复合文字来提供缓冲区空间。在中有多个调用没有问题printf()

#include <assert.h>
#include <limits.h>
#define TO_BASE_N (sizeof(unsigned)*CHAR_BIT + 1)

//                               v. compound literal .v
#define TO_BASE(x, b) my_to_base((char [TO_BASE_N]){""}, (x), (b))

// Tailor the details of the conversion function as needed
// This one does not display unneeded leading zeros
// Use return value, not `buf`
char *my_to_base(char *buf, unsigned i, int base) {
  assert(base >= 2 && base <= 36);
  char *s = &buf[TO_BASE_N - 1];
  *s = '\0';
  do {
    s--;
    *s = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % base];
    i /= base;
  } while (i);

  // Could employ memmove here to move the used buffer to the beginning

  return s;
}

#include <stdio.h>
int main(void) {
  int ip1 = 0x01020304;
  int ip2 = 0x05060708;
  printf("%s %s\n", TO_BASE(ip1, 16), TO_BASE(ip2, 16));
  printf("%s %s\n", TO_BASE(ip1, 2), TO_BASE(ip2, 2));
  puts(TO_BASE(ip1, 8));
  puts(TO_BASE(ip1, 36));
  return 0;
}

输出量

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

这是非常有用的。您知道如何在C ++中使用它吗?当我编译时,它会生成一个错误“严重性代码描述项目文件行抑制状态错误C4576,带括号的类型后跟一个初始化程序列表是一种非标准的显式类型转换语法,你好C:\ my_projects \ hello \ hello \ main.cpp 39 “
只是一个学习者,

1
@Justalearner生成一个C ++,因为如果使用不属于C ++ 的C功能复合文字。也许发布您尝试执行相同操作的C ++实现-即使不完整,我相信您也会得到帮助-只要您首先展示您的尝试即可。
chux-恢复莫妮卡

6

也许有点OT,但是如果您仅需要进行调试以了解或追溯正在执行的某些二进制操作,则可以看看wcalc(一个简单的控制台计算器)。使用-b选项,您将获得二进制输出。

例如

$ wcalc -b“(256 | 3)&0xff”
 = 0b11

1
有这方面的一些其他选择,太多...... ruby -e 'printf("%b\n", 0xabc)'dc其次是2o其次0x123p,等等。
lindes


5

以下递归函数可能有用:

void bin(int n)
{
    /* Step 1 */
    if (n > 1)
        bin(n/2);
    /* Step 2 */
    printf("%d", n % 2);
}

7
请注意,这不适用于负整数。
安德森·弗雷塔斯

4

我针对大小和C ++-ness优化了最佳解决方案,并获得了以下解决方案:

inline std::string format_binary(unsigned int x)
{
    static char b[33];
    b[32] = '\0';

    for (int z = 0; z < 32; z++) {
        b[31-z] = ((x>>z) & 0x1) ? '1' : '0';
    }

    return b;
}

3
如果要使用动态内存(通过std::string),则最好不要使用该static数组。最简单的方法是仅删除static限定符并使b该函数局部化。
Shahbaz 2013年

((x>>z) & 0x01) + '0'足够了。
杰森C

4

使用更少的代码和资源即可打印任何类型的位

此方法具有以下属性:

  • 使用变量和文字。
  • 不需要时不迭代所有位。
  • 仅当完成一个字节(并非所有位不必要)时才调用printf。
  • 适用于任何类型。
  • 字节序大小不一(使用GCC#定义进行检查)。
  • 使用不是C标准但已很大程度上定义的typeof()。
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define for_endian(size) for (int i = 0; i < size; ++i)
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define for_endian(size) for (int i = size - 1; i >= 0; --i)
#else
#error "Endianness not detected"
#endif

#define printb(value)                                   \
({                                                      \
        typeof(value) _v = value;                       \
        __printb((typeof(_v) *) &_v, sizeof(_v));       \
})

void __printb(void *value, size_t size)
{
        uint8_t byte;
        size_t blen = sizeof(byte) * 8;
        uint8_t bits[blen + 1];

        bits[blen] = '\0';
        for_endian(size) {
                byte = ((uint8_t *) value)[i];
                memset(bits, '0', blen);
                for (int j = 0; byte && j < blen; ++j) {
                        if (byte & 0x80)
                                bits[j] = '1';
                        byte <<= 1;
                }
                printf("%s ", bits);
        }
        printf("\n");
}

int main(void)
{
        uint8_t c1 = 0xff, c2 = 0x44;
        uint8_t c3 = c1 + c2;

        printb(c1);
        printb((char) 0xff);
        printb((short) 0xff);
        printb(0xff);
        printb(c2);
        printb(0x44);
        printb(0x4411ff01);
        printb((uint16_t) c3);
        printf("\n");

        return 0;
}

输出量

$ ./printb 
11111111 
11111111 
00000000 11111111 
00000000 00000000 00000000 11111111 
01000100 
00000000 00000000 00000000 01000100 
01000100 00010001 11111111 00000001 
00000000 01000011 

我使用了另一种方法(bitprint.h)用所有字节(作为位字符串)填充表,并根据输入/索引字节打印它们。值得一看。


4
void
print_binary(unsigned int n)
{
    unsigned int mask = 0;
    /* this grotesque hack creates a bit pattern 1000... */
    /* regardless of the size of an unsigned int */
    mask = ~mask ^ (~mask >> 1);

    for(; mask != 0; mask >>= 1) {
        putchar((n & mask) ? '1' : '0');
    }

}

或将0或1添加到字符值“ 0”;)不需要三进制。
猫头鹰

3

我喜欢paniq的代码,静态缓冲区是个好主意。但是,如果您希望在单个printf()中使用多种二进制格式,它将失败,因为它总是返回相同的指针并覆盖数组。

这是一个C样式的插入式控件,它在拆分缓冲区上旋转指针。

char *
format_binary(unsigned int x)
{
    #define MAXLEN 8 // width of output format
    #define MAXCNT 4 // count per printf statement
    static char fmtbuf[(MAXLEN+1)*MAXCNT];
    static int count = 0;
    char *b;
    count = count % MAXCNT + 1;
    b = &fmtbuf[(MAXLEN+1)*count];
    b[MAXLEN] = '\0';
    for (int z = 0; z < MAXLEN; z++) { b[MAXLEN-1-z] = ((x>>z) & 0x1) ? '1' : '0'; }
    return b;
}

1
一旦count到达MAXCNT - 1,下一个增量count将使其MAXCNT变为零,而不是零,这将导致对数组边界的访问。你应该做的count = (count + 1) % MAXCNT
Shahbaz 2013年

1
顺便说一句,这对于后来MAXCNT + 1在single中使用对该函数的调用的开发人员来说是一个惊喜printf。通常,如果您要为一件以上的事情提供选项,请使其无限。诸如4之类的数字只会引起问题。
Shahbaz 2013年

3

没有标准的可移植方式。

一些实现提供了itoa(),但是大多数情况下都不会用到,并且它的接口有些拙劣。但是该代码位于链接的后面,应该可以使您轻松实现自己的格式化程序。


3

使用标准库将所有整数类型转换为二进制字符串表示形式的一条语句通用转换:

#include <bitset>
MyIntegralType  num = 10;
print("%s\n",
    std::bitset<sizeof(num) * 8>(num).to_string().insert(0, "0b").c_str()
); // prints "0b1010\n"

要不就: std::cout << std::bitset<sizeof(num) * 8>(num);


1
这是对C惯用的解决方案+ +,但他被要求C.
danijar

3

我的解决方案:

long unsigned int i;
for(i = 0u; i < sizeof(integer) * CHAR_BIT; i++) {
    if(integer & LONG_MIN)
        printf("1");
    else
        printf("0");
    integer <<= 1;
}
printf("\n");

3

根据@ ideasman42在他回答的建议,这是提供了一个宏int8163264版本,再使用INT8宏来避免重复。

/* --- PRINTF_BYTE_TO_BINARY macro's --- */
#define PRINTF_BINARY_SEPARATOR
#define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c"
#define PRINTF_BYTE_TO_BINARY_INT8(i)    \
    (((i) & 0x80ll) ? '1' : '0'), \
    (((i) & 0x40ll) ? '1' : '0'), \
    (((i) & 0x20ll) ? '1' : '0'), \
    (((i) & 0x10ll) ? '1' : '0'), \
    (((i) & 0x08ll) ? '1' : '0'), \
    (((i) & 0x04ll) ? '1' : '0'), \
    (((i) & 0x02ll) ? '1' : '0'), \
    (((i) & 0x01ll) ? '1' : '0')

#define PRINTF_BINARY_PATTERN_INT16 \
    PRINTF_BINARY_PATTERN_INT8               PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
    PRINTF_BYTE_TO_BINARY_INT8((i) >> 8),   PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
    PRINTF_BINARY_PATTERN_INT16              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
    PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64    \
    PRINTF_BINARY_PATTERN_INT32              PRINTF_BINARY_SEPARATOR              PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
    PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
/* --- end macros --- */

#include <stdio.h>
int main() {
    long long int flag = 1648646756487983144ll;
    printf("My Flag "
           PRINTF_BINARY_PATTERN_INT64 "\n",
           PRINTF_BYTE_TO_BINARY_INT64(flag));
    return 0;
}

输出:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

为了提高可读性,您可以将:更改#define PRINTF_BINARY_SEPARATOR#define PRINTF_BINARY_SEPARATOR ","#define PRINTF_BINARY_SEPARATOR " "

这将输出:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000

要么

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

感谢复制此代码,第一个要复制到我正在从事的项目中,编写此任务感觉很乏味:)
DevZer0


2
void print_ulong_bin(const unsigned long * const var, int bits) {
        int i;

        #if defined(__LP64__) || defined(_LP64)
                if( (bits > 64) || (bits <= 0) )
        #else
                if( (bits > 32) || (bits <= 0) )
        #endif
                return;

        for(i = 0; i < bits; i++) { 
                printf("%lu", (*var >> (bits - 1 - i)) & 0x01);
        }
}

应该可以工作-未经测试。


2
/* Convert an int to it's binary representation */

char *int2bin(int num, int pad)
{
 char *str = malloc(sizeof(char) * (pad+1));
  if (str) {
   str[pad]='\0';
   while (--pad>=0) {
    str[pad] = num & 1 ? '1' : '0';
    num >>= 1;
   }
  } else {
   return "";
  }
 return str;
}

/* example usage */

printf("The number 5 in binary is %s", int2bin(5, 4));
/* "The number 5 in binary is 0101" */

4
支付分配不当的费用将损害性能。将破坏缓冲区的责任传递给调用者是不友好的。
EvilTeach

2

接下来将向您显示内存布局:

#include <limits>
#include <iostream>
#include <string>

using namespace std;

template<class T> string binary_text(T dec, string byte_separator = " ") {
    char* pch = (char*)&dec;
    string res;
    for (int i = 0; i < sizeof(T); i++) {
        for (int j = 1; j < 8; j++) {
            res.append(pch[i] & 1 ? "1" : "0");
            pch[i] /= 2;
        }
        res.append(byte_separator);
    }
    return res;
}

int main() {
    cout << binary_text(5) << endl;
    cout << binary_text(.1) << endl;

    return 0;
}

什么是你的意思“下一步将展现给你的内存布局”
Peter Mortensen

2

这是paniq解决方案的一小部分变化,解决方案使用模板允许打印32位和64位整数:

template<class T>
inline std::string format_binary(T x)
{
    char b[sizeof(T)*8+1] = {0};

    for (size_t z = 0; z < sizeof(T)*8; z++)
        b[sizeof(T)*8-1-z] = ((x>>z) & 0x1) ? '1' : '0';

    return std::string(b);
}

可以像这样使用:

unsigned int value32 = 0x1e127ad;
printf( "  0x%x: %s\n", value32, format_binary(value32).c_str() );

unsigned long long value64 = 0x2e0b04ce0;
printf( "0x%llx: %s\n", value64, format_binary(value64).c_str() );

结果如下:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.