ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我正在运行 gcc。

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
print("%b\n", 10); // prints "%b\n"
据我所知,您不能使用 printf 执行此操作。显然,您可以编写一个辅助方法来完成此操作,但这听起来不像您想要的方向。
没有为此预定义的格式。您需要自己将其转换为字符串,然后打印该字符串。
快速的 Google 搜索生成了此页面,其中包含一些可能有用的信息:forums.macrumors.com/archive/index.php/t-165959.html
不是 ANSI 标准 C 库的一部分——如果您正在编写可移植代码,最安全的方法是自己编写。
在 C++ 上转换为二进制字符串的一种语句标准和通用(适用于任何长度的任何 Integral 类型)解决方案:stackoverflow.com/a/31660310/1814353

W
William Whyte

哈克但对我有用:

#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 的多次调用。


并且具有在 printf 中可以多次调用的优点,而具有 static 缓冲区的那些则不能。
我冒昧地将 %d 更改为 %c,因为它应该更快(%d 必须执行 digit->char 转换,而 %c 只是输出参数
发布了此宏的扩展版本,支持 16、32、64 位 int:stackoverflow.com/a/25108449/432509
请注意,这种方法对堆栈不友好。假设 int 在系统上是 32 位,打印单个 32 位值将需要 32 * 4 字节值的空间;共 128 个字节。这取决于堆栈大小,可能是也可能不是问题。
在宏中的字节周围添加括号很重要,否则在发送操作 BYTE_TO_BINARY(a | b) -> a | 时可能会遇到问题。 b & 0x01 != (a | b) & 0x01
i
ib.

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

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

建议 size_t i; for (i=size; i-- > 0; ) 以避免 size_tint 不匹配。
有人可以详细说明这段代码背后的逻辑吗?
ptr 中的每个字节(外循环);然后对于当前字节(内部循环)的每个位,用当前位(1 << j)屏蔽字节。右移得到一个包含 0 (0000 0000b) 或 1 (0000 0001b) 的字节。使用格式 %u 打印结果字节 printf。 HTH。
@ZX9 请注意,suggested code 使用 >size_t 而不是您的评论的 >= 来确定何时终止循环。
@ZX9 仍然是您的有用原始评论,因为编码人员确实需要小心考虑使用无符号类型的 >>= 的边缘情况。 0 是一个无符号的边缘情况并且经常发生,这与不太常见的 INT_MAX/INT_MIN 的有符号数学不同。
E
EvilTeach

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

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

这肯定比为 printf 编写转义重载更“奇怪”。对于刚接触代码的开发人员来说,这也很容易理解。
一些更改:strcat 是在每次循环时向字符串添加单个字符的低效方法。相反,添加一个 char *p = b; 并将内部循环替换为 *p++ = (x & z) ? '1' : '0'z 应该从 128 (2^7) 而不是 256 (2^8) 开始。考虑更新以获取要使用的缓冲区的指针(为了线程安全),类似于 inet_ntoa()
@EvilTeach:您自己使用三元运算符作为 strcat() 的参数!我同意 strcat 可能比为赋值后递增取消引用的指针更容易理解,但即使是初学者也需要知道如何正确使用标准库。也许使用索引数组进行赋值会是一个很好的演示(并且实际上会起作用,因为每次调用函数时 b 都不会重置为全零)。
随机:二进制缓冲区 char 是静态的,并且在分配中被清零。这只会在第一次运行时清除它,之后它不会清除,而是使用最后一个值。
此外,这应该证明之前的结果在再次调用该函数后将无效,因此调用者不应尝试像这样使用它:printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4))
D
DGentry

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

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

这是关于如何在 glibc 中实现自定义 printf 格式的example


warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations] 不过有一个新函数可以做同样的事情:register_printf_specifier()。可在此处找到新用法示例:codereview.stackexchange.com/q/219994/200418
P
Peter Mortensen

您可以使用一张小桌子来提高速度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 我主要指的是优化器不那么激进且速度差异明显的嵌入式应用程序。


有用!但是用于定义 bit_rep 的语法是什么?
这段代码看起来很棒。但是您将如何更新此代码以处理 uint16_t、uint32_t 和 uint64_t?
@Robk,4、8 和 16 个 %s 以及相同数量的 bit_rep[word >> 4K & 0xF..F] 参数应该可以。尽管我认为 64 位数字的 16 个字符串打印可能不会比循环 64 次并输出 0/1 快。
i
isrnick

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

#include <stdio.h>

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

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

Online demo


您还应该使用 unsigned int 数字,因为当给定数字为负数时,该函数进入一个永无止境的递归调用。
更有效的方法,因为在 ASCII 中,'0'+1='1': putc('0'+(number&1), stdout);
我已将函数更改为也可以使用等于或小于 0 的 int 值。
将以下值 0x80 传递给您的函数,结果与预期不符。
i
ideasman42

根据@William Whyte 的回答,这是一个提供 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

这是极好的。打印以最低有效位开头的位是否有特殊原因?
您如何建议添加逗号?
将添加 PRINTF_BYTE_TO_BINARY_INT# 定义的分组版本以供选择使用。
i
ib.

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

#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 是任何整数表达式。


是的,你可以做到这一点。但这确实是糟糕的设计。即使您没有线程或重入,调用者也必须知道静态缓冲区正在被重用,并且 char *a = binary_fmt(x), *b = binary_fmt(y); 之类的东西不会按预期工作。强制调用者传递一个缓冲区使得存储需求明确;如果确实需要,调用者当然可以自由使用静态缓冲区,然后对同一缓冲区的重用变得明确。另请注意,在现代 PIC ABI 上,静态缓冲区通常比堆栈上的缓冲区花费更多的代码来访问。
这仍然是一个糟糕的设计。在这些情况下,它需要一个额外的复制步骤,而且即使在不需要复制的情况下,它也不比让调用者提供缓冲区便宜。使用静态存储只是一个坏习惯。
必须使用不必要的额外名称污染预处理器或变量符号表的名称空间,该名称必须用于正确调整每个调用者必须分配的存储空间,并强制每个调用者知道该值并分配必要数量的当更简单的函数本地存储解决方案足以满足大多数意图和目的,并且当一个简单的 strdup() 调用涵盖其余 99% 的用途时,存储是糟糕的设计。
在这里,我们将不得不不同意。我看不出添加一个不显眼的预处理器符号是如何接近严重限制使用案例的有害性,使接口容易出错,在程序期间为临时值保留永久存储,并在大多数情况下生成更糟糕的代码现代平台。
我不提倡无缘无故地进行微优化(即测量)。但我确实认为性能,即使它是在微增益范围内,当它作为一种奖励以及从根本上优越的设计时,也值得一提。
R
Robotbugs

快速简便的解决方案:

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

我正在对此进行测试,看起来这两种 *int_to_bitstring_ 方法都无法正确计算结果,还是我遗漏了什么? printbits 工作正常。此外,对于大于 32 的小数,静态和分配方法的结果开始不同。在 C 和使用位方面没有太多经验。
i
ib.
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;
}

很好的解决方案。不过我会改变一些东西。即在字符串中向后移动,以便可以正确处理任何大小的输入。
所有这些 8 都应替换为 CHAR_BIT
我喜欢那个 id 不使用任何类型的字符串库,因此可以很容易地在嵌入设置中使用
使用静态变量对于这个函数来说真的很糟糕。想象一下 printf(byte_to_binary(1), byte_to_binary(5)),其中一个调用会覆盖来自另一个调用的字符串
c
chux - Reinstate Monica

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

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

在任何底座上打印 [2-36]

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

使用静态内存作为返回缓冲区。这限制了函数可以用作 printf() 的参数的次数。分配需要调用代码以释放指针的内存。要求调用代码显式提供合适的缓冲区。直接调用 printf()。这为 fprintf()、sprintf()、vsprintf() 等提供了一个新函数。使用缩小的整数范围。

以下没有上述限制。它确实需要 C99 或更高版本并使用 "%s"。它使用 compound literal 来提供缓冲区空间。 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

这非常有用。你知道如何在 C++ 中使用它吗?当我编译时,它会生成一个错误“严重代码描述项目文件行抑制状态错误 C4576 带括号的类型后跟初始化列表是非标准显式类型转换语法 hello C:\my_projects\hello\hello\main.cpp 39 "
@Justalearner 这会生成一个 C++,因为 if 使用不属于 C++ 的 C 功能复合文字。也许发布你的 C++ 实现,尝试做同样的事情——即使不完整,我相信你会得到帮助——只要你先展示你的尝试。
K
Kalcifer

自 2022 年 2 月 3 日起,GNU C 库已更新为 version 2.35。因此,现在支持 %b 以二进制格式输出。

printf 系列函数现在支持以 %b 格式输出二进制整数,如 ISO C2X 草案中指定的那样,以及 ISO C2X 草案推荐的该格式的 %B 变体。


i
ib.

之前发布的答案都不是我想要的,所以我写了一个。将 %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;
}

这会溢出超过 50 位吗?
好电话,是的,它会...有人告诉我我需要使用 malloc,曾经这样做吗?
当然是。超级简单:char* buffer = (char*) malloc(sizeof(char) * 50);
@JanusTroelsen,或者更干净、更小、可维护:char *buffer = malloc(sizeof(*buffer) * 50);
为什么“%B”在这方面与“%b”有任何不同?以前的答案说“C 标准库中没有格式化函数可以像这样输出二进制文件”。和“某些运行时支持“%b”,尽管这不是标准。”。
i
ib.

此代码应能满足您最多 64 位的需求。我创建了两个函数:pBinpBinFill。两者都做同样的事情,但 pBinFill 用其最后一个参数提供的填充字符填充前导空格。 test 函数生成一些测试数据,然后使用 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

J
John Millikin

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

另请参阅此处进行有趣的讨论:

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

高温高压


这实际上是 C 运行时库的属性,而不是编译器。
q
quinmars

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

例如

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

在这方面还有其他一些选项... ruby -e 'printf("%b\n", 0xabc)'dc 后跟 2o 后跟 0x123p,依此类推。
P
Peter Mortensen

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


P
Peter Mortensen

以下递归函数可能有用:

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

小心,这不适用于负整数。
p
paniq

我优化了大小和 C++ 的顶级解决方案,并得到了这个解决方案:

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

如果要使用动态内存(通过 std::string),不妨去掉 static 数组。最简单的方法是删除 static 限定符并将 b 设为函数的本地。
((x>>z) & 0x01) + '0' 就足够了。
P
Peter Mortensen

利用:

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

如需更多参考,请参阅 How to print binary number via printf


之前的回答说“某些实现提供了 itoa(),但它不会在大多数情况下出现”?
м
малин чекуров
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' 的字符值;)不需要三元。
G
Geyslan G. Bem

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

这种方法具有以下属性:

适用于变量和文字。

不需要时不迭代所有位。

仅在完成一个字节时才调用 printf(并非对所有位都不必要)。

适用于任何类型。

适用于小端和大端(使用 GCC #defines 进行检查)。

可以与 char 不是字节(八位)的硬件一起使用。 (感谢@supercat)

使用不是 C 标准但在很大程度上已定义的 typeof()。

#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 

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


在使用硬件供应商的库时,我实际上在我最喜欢的嵌入式编译器上遇到了 VLA 崩溃的问题。有些人会争辩说我应该只使用 gcc 或 clang,但除了 -O0 之外没有提供任何设置,这将避免进行不合理的优化(例如假设如果不需要编译器来适应 p1 被用于在某些上下文中访问某些存储,编译器可以显示 p1 和 p2 将相等,它可能会忽略 p2 被用于访问该存储的可能性)。
M
Martijn Courteaux
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);
        }
}

应该工作 - 未经测试。


B
Bo Persson

我喜欢 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;
}

一旦 count 达到 MAXCNT - 1count 的下一个增量将使其变为 MAXCNT 而不是零,这将导致访问超出数组的边界。您应该完成 count = (count + 1) % MAXCNT
顺便说一句,对于在单个 printf 中使用 MAXCNT + 1 调用此函数的开发人员来说,这会令他们感到惊讶。一般来说,如果您想为超过 1 件事提供选项,请将其设为无限。诸如 4 之类的数字只会引起问题。
t
the Tin Man

这是 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

这不是 C,它使用 C++ 的糟糕 OOP
w
wnoise

没有标准和便携的方式。

一些实现提供了 itoa(),但它不会出现在大多数情况下,而且它的接口有点糟糕。但是代码在链接后面,应该可以让您很容易地实现自己的格式化程序。


M
Marko

我只想发布我的解决方案。它用于获取一个字节的 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);
}

a
andre.barata

这是我为 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 );
}

刚刚注意到这与@Marko 解决方案非常相似
有没有办法限制输出的位大小?
@Remian8985 是的, s 变量保存将输出的位数。 "(sizeof(v)<<3)" 基本上是以字节为单位的输入变量的大小(在 int 的情况下为 4)然后 "<<3" 与乘以 8 相同,以获得要打印的位数
l
luart

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

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

Or just: std::cout << std::bitset<sizeof(num) * 8>(num);


这是 C++ 的惯用解决方案,但他要求使用 C。
S
SarahGaidi

我的解决方案:

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