是否有一个printf转换器打印二进制格式?

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

我正在运行gcc。

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
printf("%b\n", 10); // prints "%b\n"
1103797 次浏览

C标准库中没有这样输出二进制的格式化函数。printf家族支持的所有格式操作都是针对人类可读的文本。

一些运行时支持“%b”,尽管这不是标准。

也可以在这里看到一个有趣的讨论:

http://bytes.com/forum/thread591027.html

HTH

没有标准和便携的方式。

一些实现提供了itoa (),但大多数实现中都没有,而且它的接口有些糟糕。但是代码在链接后面,应该可以让您非常容易地实现自己的格式化程序。

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

在glibc中,可以向printf()函数家族添加自定义转换类型。详见register_printf_function。如果可以简化应用程序代码,您可以添加自定义%b转换供自己使用。

下面是如何在glibc中实现自定义printf格式的例子

这里有一个快速的技巧来演示如何做你想做的事情。

#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;
}

可能有点超时,但是如果你只是为了调试来理解或回溯你正在做的一些二进制操作而需要这个,你可以看看wcalc(一个简单的控制台计算器)。使用-b选项可以得到二进制输出。

如。

$ wcalc -b "(256 | 3) & 0xff"
= 0b11
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;
}

即使是支持%b的运行时库,它似乎也只适用于整数值。

如果您想打印二进制的浮点值,我写了一些代码,您可以在http://www.exploringbinary.com/converting-floating-point-numbers-to-binary-strings-in-c/中找到。

这段代码应该处理你的需求高达64位。 我创建了两个函数:pBinpBinFill。两者都做同样的事情,但是pBinFill用最后一个参数提供的fill字符填充前导空格。 测试函数生成一些测试数据,然后使用pBinFill函数将其打印出来

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

输出:

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

但对我来说很管用:

#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。

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);
}
}

应该工作-未经测试。

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

// 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;
}
void PrintBinary( int Value, int Places, char* TargetString)
{
int Mask;


Mask = 1 << Places;


while( Places--) {
Mask >>= 1; /* Preshift, because we did one too many above */
*TargetString++ = (Value & Mask)?'1':'0';
}
*TargetString = 0; /* Null terminator for C string */
}

调用函数“拥有”字符串…:

char BinaryString[17];
...
PrintBinary( Value, 16, BinaryString);
printf( "yadda yadda %s yadda...\n", BinaryString);

取决于您的CPU, PrintBinary中的大多数操作呈现给一个或很少几个机器指令。

#include <stdio.h>
#include <conio.h>


void main()
{
clrscr();
printf("Welcome\n\n\n");
unsigned char x='A';
char ch_array[8];
for(int i=0; x!=0; i++)
{
ch_array[i] = x & 1;
x = x >>1;
}
for(--i; i>=0; i--)
printf("%d", ch_array[i]);


getch();
}

下面是一个不受重入性问题或参数大小/类型限制的函数版本:

#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到10之间的任何底数,只要将2替换为所需的底数。用法是:

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

其中x为任意积分表达式。

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

我优化了顶部的解决方案的大小和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;
}

是否有一个printf转换器打印二进制格式?

没有标准的printf格式说明符来完成“二进制”输出。这是我在需要的时候设计的替代方案。

我的适用范围从2到36。它将数字扇出到递归调用的调用帧中,直到它到达一个比基数小的数字。然后它向后“遍历”,向前填充缓冲区,然后返回。返回值是使用的大小,如果缓冲区不够大,则返回-1。

int conv_rad (int num, int rad, char *s, int n) {
char *vec = "0123456789" "ABCDEFGHIJKLM" "NOPQRSTUVWXYZ";
int off;
if (n == 0) return 0;
if (num < rad) { *s = vec[num]; return 1; }
off = conv_rad(num/rad, rad, s, n);
if ((off == n) || (off == -1)) return -1;
s[off] = vec[num%rad];
return off+1;
}

这个函数是为“Pascal”风格的字符串设计的,它携带着它们的长度。因此,正如所写的,conv_rad并不以空终止缓冲区。对于更一般的C用法,它可能需要一个简单的包装器来执行空终止。或者打印时,只需将赋值改为putchar()s。

我喜欢的代码由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;
}

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

#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;
}

之前发布的答案都不是我想要的,所以我写了一个。这是超级简单的使用%Bprintf!

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


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;
}


int main(int argc, char** argv)
{
register_printf_specifier('B', printf_output_M, printf_arginfo_M);


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


return EXIT_SUCCESS;
}

使用:

char buffer [33];
itoa(value, buffer, 2);
printf("\nbinary: %s\n", buffer);

更多参考,见如何通过printf打印二进制数字

下面是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

这可能不是很有效,但很简单。试试这个:

tmp1 = 1;
while(inint/tmp1 > 1) {
tmp1 <<= 1;
}
do {
printf("%d", tmp2=inint/tmp1);
inint -= tmp1*tmp2;
} while((tmp1 >>= 1) > 0);
printf(" ");

这是一个非常简单的例子:

int print_char_to_binary(char ch)
{
int i;
for (i=7; i>=0; i--)
printf("%hd ", ((ch & (1<<i))>>i));
printf("\n");
return 0;
}

您可以使用一个小表来提高速度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我主要指的是嵌入式应用程序,其中优化器不是那么激进,速度差异是可见的。

还有另一种二进制打印方法:首先转换整数

要以二进制格式打印6,将6更改为110,然后打印"110"

绕过char buf[]问题 printf()格式说明符,标志,&像"%08lu""%*lX"这样的字段仍然可用 不仅是二进制(以2为基数),这个方法还可以扩展到其他以16为基数的方法 仅限于较小的整数值。< / p >

#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>


unsigned long char_to_bin10(char ch) {
unsigned char uch = ch;
unsigned long sum = 0;
unsigned long power = 1;
while (uch) {
if (uch & 1) {
sum += power;
}
power *= 10;
uch /= 2;
}
return sum;
}


uint64_t uint16_to_bin16(uint16_t u) {
uint64_t sum = 0;
uint64_t power = 1;
while (u) {
if (u & 1) {
sum += power;
}
power *= 16;
u /= 2;
}
return sum;
}


void test(void) {
printf("%lu\n", char_to_bin10(0xF1));
// 11110001
printf("%" PRIX64 "\n", uint16_to_bin16(0xF731));
// 1111011100110001
}

我只是想把我的解贴出来。它用于获取一个字节的0和1,但多次调用此函数可以用于更大的数据块。我将它用于128位或更大的结构。还可以修改它,使用size_t作为输入参数和指向想要打印的数据的指针,因此它可以与大小无关。但这对我很管用。

void print_binary(unsigned char c)
{
unsigned char i1 = (1 << (sizeof(c)*8-1));
for(; i1; i1 >>= 1)
printf("%d",(c&i1)!=0);
}


void get_binary(unsigned char c, unsigned char bin[])
{
unsigned char i1 = (1 << (sizeof(c)*8-1)), i2=0;
for(; i1; i1>>=1, i2++)
bin[i2] = ((c&i1)!=0);
}

根据@William Whyte的回答,这是一个宏,提供int81632 &64版本,重用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

为了可读性,你可能需要为eg添加分隔符:

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

下面是我对unsigned int的处理方法

void printb(unsigned int v) {
unsigned int i, s = 1<<((sizeof(v)<<3)-1); // s = only most significant bit at 1
for (i = s; i; i>>=1) printf("%d", v & i || 0 );
}
在C语言中一个小的实用函数,在解决位操作问题时做到这一点。这将遍历字符串,使用掩码(1<
void
printStringAsBinary(char * input)
{
char * temp = input;
int i = 7, j =0;;
int inputLen = strlen(input);


/* Go over the string, check first bit..bit by bit and print 1 or 0
**/


for (j = 0; j < inputLen; j++) {
printf("\n");
while (i>=0) {
if (*temp & (1 << i)) {
printf("1");
} else {
printf("0");
}
i--;
}
temp = temp+1;
i = 7;
printf("\n");
}
}
void binario(int num) {
for(int i=0;i<32;i++){
(num&(1<i))? printf("1"):
printf("0");
}
printf("\n");
}

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

#include <stdio.h>


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

对我来说,这是最干净的解决方案之一。如果您喜欢0b前缀和一个尾随的新行字符,我建议对函数进行包装。

在线演示 . b

下面的递归函数可能有用:

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

使用标准库将任何整数类型转换为二进制字符串表示:

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

# EYZ1 # EYZ0

是否有一个printf转换器打印二进制格式?

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

[Edit 2022]这预计将在实现"%b"的C的下一个版本中改变。

二进制常量,如0b10101010和printf()函数族C2x的%b转换说明符


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

到目前为止,所有其他答案都至少有一个这样的局限。

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

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

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

  4. 直接调用printf()。这迫使一个新函数为fprintf()sprintf()vsprintf()等。

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

下面是以上限制皆无。它确实需要C99或更高版本,并使用"%s"。它使用复合文字< em > < / em >来提供缓冲空间。在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[TO_BASE_N], 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
// size_t len = &buf[TO_BASE_N] - s;
// memmove(buf, s, len);


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

下面的函数返回给定无符号整数的二进制表示形式,使用不带前导零的指针算术:

const char* toBinaryString(unsigned long num)
{
static char buffer[CHAR_BIT*sizeof(num)+1];
char* pBuffer = &buffer[sizeof(buffer)-1];


do *--pBuffer = '0' + (num & 1);
while (num >>= 1);
return pBuffer;
}

注意,不需要显式设置NUL结束符,因为buffer表示一个带有静态存储持续时间的对象,该对象已经被全0填充。

通过简单地修改num形式参数的类型,可以很容易地适应unsigned long long(或另一个无符号整数)。

CHAR_BIT要求包含<limits.h>

下面是一个用法示例:

int main(void)
{
printf(">>>%20s<<<\n", toBinaryString(1));
printf(">>>%-20s<<<\n", toBinaryString(254));
return 0;
}

其期望输出为:

>>>                   1<<<
>>>11111110            <<<

我的解决方案:

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");

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

这种方法有以下属性:

  • 使用变量和字面量。
  • 没有必要时不迭代所有位。
  • 只在完成一个字节时调用printf(不必对所有位都调用)。
  • 适用于任何类型。
  • 使用大小字节序(使用GCC #定义进行检查)。
  • 可以与char不是字节(8位)的硬件一起工作。(谢谢大家@supercat)
  • 使用typeof(),它不是C标准的,但在很大程度上是定义的。
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <limits.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));       \
})


#define MSB_MASK 1 << (CHAR_BIT - 1)


void __printb(void *value, size_t size)
{
unsigned char uc;
unsigned char bits[CHAR_BIT + 1];


bits[CHAR_BIT] = '\0';
for_endian(size) {
uc = ((unsigned char *) value)[i];
memset(bits, '0', CHAR_BIT);
for (int j = 0; uc && j < CHAR_BIT; ++j) {
if (uc & MSB_MASK)
bits[j] = '1';
uc <<= 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);
printb('A');
printf("\n");


return 0;
}

输出

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

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

还有一种想法是将数字转换为十六进制格式,然后将每个十六进制密码解码为四个“位”(1和0)。sprintf可以为我们做位操作:

const char* binary(int n) {
static const char binnums[16][5] = { "0000","0001","0010","0011",
"0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111" };
static const char* hexnums = "0123456789abcdef";
static char inbuffer[16], outbuffer[4*16];
const char *i;
sprintf(inbuffer,"%x",n); // hexadecimal n -> inbuffer
for(i=inbuffer; *i!=0; ++i) { // for each hexadecimal cipher
int d = strchr(hexnums,*i) - hexnums; // store its decimal value to d
char* o = outbuffer+(i-inbuffer)*4; // shift four characters in outbuffer
sprintf(o,"%s",binnums[d]); // place binary value of d there
}
return strchr(outbuffer,'1'); // omit leading zeros
}


puts(binary(42)); // outputs 101010

也许有人会发现这个解决方案很有用:

void print_binary(int number, int num_digits) {
int digit;
for(digit = num_digits - 1; digit >= 0; digit--) {
printf("%c", number & (1 << digit) ? '1' : '0');
}
}

根据@ideasman42在他的回答中的建议,这是一个宏,提供int81632 &64版本,重用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

使用以下功能:

void conbin(int num){
if(num != 0)
{
conbin(num >> 1);
if (num & 1){
printf("1");
}
else{
printf("0");
}
}
}
void DisplayBinary(int n)
{
int arr[8];
int top =-1;
while (n)
{
if (n & 1)
arr[++top] = 1;
else
arr[++top] = 0;


n >>= 1;
}
for (int i = top ; i > -1;i--)
{
printf("%d",arr[i]);
}
printf("\n");
}
void DisplayBinary(unsigned int n)
{
int l = sizeof(n) * 8;
for (int i = l - 1 ; i >= 0; i--) {
printf("%x", (n & (1 << i)) >> i);
}
}
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');
}


}

做一个函数并调用它

display_binary(int n)
{
long int arr[32];
int arr_counter=0;
while(n>=1)
{
arr[arr_counter++]=n%2;
n/=2;
}
for(int i=arr_counter-1;i>=0;i--)
{
printf("%d",arr[i]);
}
}

快速简单的解决方法:

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

适用于任何大小类型以及有符号整型和无符号整型。'&1'需要处理有符号整型,因为shift可能会进行符号扩展。

有很多方法可以做到这一点。这里有一个超级简单的方法,用于从有符号或无符号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);

以下是我对这个问题的看法。

与大多数其他例子相比的优点:

  1. 使用putchar(),它比printf()甚至puts()(尽管没有那么多)更有效
  2. 分成两部分(预计有内联代码),如果需要,可以提高效率。
  3. 基于非常快速的RISC算术运算(包括不使用除法和乘法)

大多数例子的缺点:

  1. 代码不是很简单。
  2. print_binary_size()修改输入变量而不复制。

注意:此代码的最佳结果依赖于在gcc中使用-O1或更高的值或等效值。

代码如下:

    inline void print_binary_sized(unsigned int number, unsigned int digits) {
static char ZERO = '0';
int digitsLeft = digits;
        

do{
putchar(ZERO + ((number >> digitsLeft) & 1));
}while(digitsLeft--);
}


void print_binary(unsigned int number) {
int digitsLeft = sizeof(number) * 8;
        

while((~(number >> digitsLeft) & 1) && digitsLeft){
digitsLeft--;
}
print_binary_sized(number, digitsLeft);
}
// m specifies how many of the low bits are shown.
// Replace m with sizeof(n) below for all bits and
// remove it from the parameter list if you like.


void print_binary(unsigned long n, unsigned long m) {
static char show[3] = "01";
unsigned long mask = 1ULL << (m-1);
while(mask) {
putchar(show[!!(n&mask)]); mask >>= 1;
}
putchar('\n');
}

我的解决方案返回一个int,然后可以在printf中使用。它还可以以大端序或小端序返回位。

#include <stdio.h>
#include <stdint.h>


int binary(uint8_t i,int bigEndian)
{
int j=0,m = bigEndian ? 1 : 10000000;
while (i)
{
j+=m*(i%2);
if (bigEndian) m*=10; else m/=10;
i >>= 1;
}
return j;
}


int main()
{
char buf[]="ABCDEF";
printf("\nbig endian = ");
for (int i=0; i<5; i++) printf("%08d ",binary(buf[i],1));
printf("\nwee endian = ");
for (int i=0; i<5; i++) printf("%08d ",binary(buf[i],0));
getchar();
return 0;
}

输出

big endian = 01000001 01000010 01000011 01000100 01000101 01000110
wee endian = 10000010 01000010 11000010 00100010 10100010 01100010

这个答案末尾的函数+宏的组合可以帮助你。

像这样使用它:

float float_var = 9.4;
SHOW_BITS(float_var);

这将输出:Variable 'float_var': 01000001 00010110 01100110 01100110

注意,它是非常通用的,可以用于几乎任何类型。 例如:< / p >
struct {int a; float b; double c;} struct_var = {1,1.1,1.2};
SHOW_BITS(struct_var);

它将输出:

Variable `struct_var`: 00111111 11110011 00110011 00110011 00110011 00110011 00110011 00110011 00111111 10001100 11001100 11001101 00000000 00000000 00000000 00000001

代码如下:

#define SHOW_BITS(a) ({ \
printf("Variable `%s`: ", #a);\
show_bits(&a, sizeof(a));\
})


void show_uchar(unsigned char a)
{
for(int i = 7; i >= 0; i-= 1)
printf("%d", ((a >> i) & 1));
}


void show_bits(void* a, size_t s)
{
unsigned char* p = (unsigned char*) a;
for(int i = s-1; i >= 0 ; i -= 1) {
show_uchar(p[i]);
printf(" ");
}
printf("\n");
}
void print_bits (uintmax_t n)
{
for (size_t i = 8 * sizeof (int); i-- != 0;)
{
char c;
if ((n & (1UL << i)) != 0)
c = '1';
else
c = '0';


printf ("%c", c);


}
}

这不是一个覆盖所有地方的解决方案,但如果你想要一些快速、容易理解的东西,我很惊讶还没有人提出这个解决方案。

c

// Based on https://stackoverflow.com/a/112956/1438550


#include <stdio.h>
#include <stdint.h>


const char *int_to_binary_str(int x, int N_bits){
static char b[512];
char *p = b;
b[0] = '\0';


for(int i=(N_bits-1); i>=0; i--){
*p++ = (x & (1<<i)) ? '1' : '0';
if(!(i%4)) *p++ = ' ';
}
return b;
}


int main() {
for(int i=31; i>=0; i--){
printf("0x%08X %s \n", (1<<i), int_to_binary_str((1<<i), 32));
}
return 0;
}

期望的行为:

Run:
gcc -pthread -Wformat=0 -lm -o main main.c; ./main


Output:
0x80000000 1000 0000 0000 0000 0000 0000 0000 0000
0x40000000 0100 0000 0000 0000 0000 0000 0000 0000
0x20000000 0010 0000 0000 0000 0000 0000 0000 0000
0x10000000 0001 0000 0000 0000 0000 0000 0000 0000
0x08000000 0000 1000 0000 0000 0000 0000 0000 0000
0x04000000 0000 0100 0000 0000 0000 0000 0000 0000
0x02000000 0000 0010 0000 0000 0000 0000 0000 0000
0x01000000 0000 0001 0000 0000 0000 0000 0000 0000
0x00800000 0000 0000 1000 0000 0000 0000 0000 0000
0x00400000 0000 0000 0100 0000 0000 0000 0000 0000
0x00200000 0000 0000 0010 0000 0000 0000 0000 0000
0x00100000 0000 0000 0001 0000 0000 0000 0000 0000
0x00080000 0000 0000 0000 1000 0000 0000 0000 0000
0x00040000 0000 0000 0000 0100 0000 0000 0000 0000
0x00020000 0000 0000 0000 0010 0000 0000 0000 0000
0x00010000 0000 0000 0000 0001 0000 0000 0000 0000
0x00008000 0000 0000 0000 0000 1000 0000 0000 0000
0x00004000 0000 0000 0000 0000 0100 0000 0000 0000
0x00002000 0000 0000 0000 0000 0010 0000 0000 0000
0x00001000 0000 0000 0000 0000 0001 0000 0000 0000
0x00000800 0000 0000 0000 0000 0000 1000 0000 0000
0x00000400 0000 0000 0000 0000 0000 0100 0000 0000
0x00000200 0000 0000 0000 0000 0000 0010 0000 0000
0x00000100 0000 0000 0000 0000 0000 0001 0000 0000
0x00000080 0000 0000 0000 0000 0000 0000 1000 0000
0x00000040 0000 0000 0000 0000 0000 0000 0100 0000
0x00000020 0000 0000 0000 0000 0000 0000 0010 0000
0x00000010 0000 0000 0000 0000 0000 0000 0001 0000
0x00000008 0000 0000 0000 0000 0000 0000 0000 1000
0x00000004 0000 0000 0000 0000 0000 0000 0000 0100
0x00000002 0000 0000 0000 0000 0000 0000 0000 0010
0x00000001 0000 0000 0000 0000 0000 0000 0000 0001

简单,经过测试,适用于任何无符号整数类型。没有头痛。

#include <stdint.h>
#include <stdio.h>


// Prints the binary representation of any unsigned integer
// When running, pass 1 to first_call
void printf_binary(unsigned int number, int first_call)
{
if (first_call)
{
printf("The binary representation of %d is [", number);
}
if (number >> 1)
{
printf_binary(number >> 1, 0);
putc((number & 1) ? '1' : '0', stdout);
}
else
{
putc((number & 1) ? '1' : '0', stdout);
}
if (first_call)
{
printf("]\n");
}
}

截至2022年2月3日,GNU C库已更新到版本2.35。因此,%b现在支持以二进制格式输出。

printf-family函数现在支持%b格式的输出 二进制整数,如ISO C2X草案中指定的,以及%B变体

. ISO C2X草案建议的格式