Есть ли конвертер printf для печати в двоичном формате?

435

Я могу напечатать с printf как шестнадцатеричное или восьмеричное число. Есть ли тег формата для печати в двоичном или произвольном виде?

Я бегу GCC.

printf("%d %x %o\n", 10, 10, 10); //prints "10 A 12\n"
print("%b\n", 10); // prints "%b\n"
Брайан
источник
Вы не можете сделать это, насколько я знаю, используя printf. Очевидно, вы могли бы написать вспомогательный метод для достижения этой цели, но это не похоже на направление, в котором вы хотите идти.
Ян Р
Для этого не существует предопределенного формата. Вам необходимо преобразовать его в строку, а затем распечатать строку.
rslite
Быстрый поиск в Google дал эту страницу с некоторой информацией, которая может оказаться полезной: forums.macrumors.com/archive/index.php/t-165959.html
Ян П
12
Не входит в стандартную библиотеку ANSI C - если вы пишете переносимый код, самый безопасный способ - это создать свой собственный.
Tomlogic
Одно утверждение стандартное и универсальное (для любого типа Integral любой длины) решение преобразования в двоичную строку на C ++: stackoverflow.com/a/31660310/1814353
luart

Ответы:

267

Хаки но у меня работает

#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буферов.
Патрик Шлютер
4
Я взял на себя смелость изменить %dк %c, потому что оно должно быть еще быстрее ( %dдолжно выполнить digit-> обугленного преобразования, а %cпросто выводит аргумент
3
Опубликована расширенная версия этого макроса с поддержкой 16, 32, 64 битных int: stackoverflow.com/a/25108449/432509
ideasman42
2
Обратите внимание, что этот подход не является дружественным к стеку. Предполагая, intчто в системе 32-битное значение, для печати одного 32-битного значения потребуется пространство для 32 * 4-байтовых значений; всего 128 байтов. Что, в зависимости от размера стека, может быть или не быть проблемой.
user694733
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_tпротив intнесовпадения.
chux - Восстановить Монику
1
Может ли кто-нибудь рассказать о логике этого кода?
JII
2
Возьмите каждый байт ptr(внешний цикл); затем для каждого бита текущий байт (внутренний цикл) маскирует байт текущим битом ( 1 << j). Сдвиньте это вправо, получив в результате байт, содержащий 0 ( 0000 0000b) или 1 ( 0000 0001b). Распечатать полученный байт printf с форматом %u. НТН.
Nielsbot
1
@ ZX9 Обратите внимание, что предлагаемый код используется >с вашим комментарием, size_tа не с ним, >=чтобы определить, когда завершить цикл.
chux - Восстановить Монику
3
@ ZX9 Тем не менее полезный оригинальный комментарий ваших , как кодерам действительно нужно быть осторожными , принимая во внимание случая использования Края >и >=с беззнаковыми типами. 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;
}
EvilTeach
источник
2
Это, безусловно, менее «странно», чем обычная запись escape-перегрузки для printf. Это также легко понять для разработчика, не знакомого с кодом.
Furious Coder
43
Несколько изменений: strcatэто неэффективный метод добавления одного символа в строку на каждом проходе цикла. Вместо этого добавьте char *p = b;и замените внутренний цикл на *p++ = (x & z) ? '1' : '0'. zдолжен начинаться с 128 (2 ^ 7) вместо 256 (2 ^ 8). Рассмотрите возможность обновления, чтобы получить указатель на используемый буфер (для безопасности потока), аналогично inet_ntoa().
Tomlogic
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)).
Пауло Эберманн
84

Обычно в glibc нет спецификатора двоичного преобразования.

Можно добавить пользовательские типы преобразования в семейство функций printf () в glibc. Смотрите register_printf_function для подробностей. Вы можете добавить пользовательское преобразование% b для собственного использования, если оно упрощает код приложения, чтобы сделать его доступным.

Вот пример того, как реализовать пользовательские форматы printf в glibc.

DGentry
источник
Я всегда писал свой собственный 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 В основном я имею в виду встроенные приложения, в которых оптимизаторы не столь агрессивны и разница в скорости заметна.

Shahbaz
источник
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 ((число & 1)? '1': '0');
Корай Тугай
@KorayTugay Спасибо за указание на это. Я исправил вызов функции и добавил демо.
Данияр
6
Вы также должны использовать целое число без знака, потому что когда заданное число отрицательно, функция входит в бесконечный рекурсивный вызов.
Puffy
Более эффективный подход, поскольку в ASCII '0' + 1 = '1':putc('0'+(number&1), stdout);
Роджер Дуек
22

На основании ответа @William Уайта, это макрос , который обеспечивает int8, 16, 32& 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

Для удобства чтения вы можете добавить разделитель, например:

My Flag 00010110,11100001,00101011,01111101,01111000,10010000,11110000,00101000
ideasman42
источник
Это отлично. Есть ли особая причина для печати битов, начинающихся с наименее значимых бит?
Гагансо
2
Как бы вы порекомендовали добавить запятую?
nmz787
Добавил бы сгруппированную версию PRINTF_BYTE_TO_BINARY_INT#определений для необязательного использования.
ideasman42
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 до 10, если вы просто замените 2 на желаемую базу. Использование это:

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

Где xлюбое интегральное выражение.

R .. GitHub ОСТАНОВИТЬ, ПОМОГАЯ
источник
7
Да, вы можете сделать это. Но это действительно плохой дизайн. Даже если у вас нет потоков или повторного входа, вызывающая сторона должна знать, что статический буфер используется повторно, и такие вещи char *a = binary_fmt(x), *b = binary_fmt(y);не будут работать так, как ожидалось. Принуждение вызывающей стороны к пропуску буфера приводит к требованию хранения; вызывающий, конечно, может свободно использовать статический буфер, если это действительно необходимо, и тогда повторное использование этого же буфера становится явным. Также обратите внимание, что в современных PIC ABI статические буферы обычно требуют больше кода для доступа, чем буферы в стеке.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE
8
Это все еще плохой дизайн. Это требует дополнительного шага копирования в этих случаях, и это не менее дорого, чем предоставление вызывающей стороне буфера даже в тех случаях, когда копирование не требуется. Использование статического хранилища - просто плохая идиома.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE
3
Необходимость загрязнения пространства имен препроцессора или таблицы переменных символов ненужным дополнительным именем, которое необходимо использовать для правильного определения размера хранилища, которое должно быть выделено каждым вызывающим абонентом, и принуждение каждого вызывающего абонента знать это значение и выделять необходимое количество хранилище - плохой дизайн, когда более простое решение для локального хранения функций будет достаточным для большинства целей и задач, и когда простой вызов strdup () покрывает 99% остального использования.
Грег А. Вудс
5
Здесь мы собираемся не согласиться. Я не вижу, как добавление одного ненавязчивого символа препроцессора может привести к серьезному ограничению вариантов использования, созданию интерфейса с ошибками, резервированию постоянного хранилища на время программы для временного значения и созданию худшего кода для большинства современные платформы.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE
5
Я не защищаю микрооптимизацию без причины (например, измерения). Но я думаю, что производительность, даже если она находится в масштабе микро-усиления, стоит упомянуть, когда она идет в качестве бонуса наряду с принципиально превосходным дизайном.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
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;
}
DanSkeel
источник
6
Понятнее, если вы используете '1'и '0'вместо 49и 48в своей троичной. Кроме того, bдлина должна быть 9 символов, чтобы последний символ оставался нулевым терминатором.
Tomlogic
Также B необходимо каждый раз инициализировать.
EvilTeach
2
Нет , если вы изменить некоторые из них : 1. добавить пространства для окончательной нулевого: static char b[9] = {0}2. декларации выйти из цикла: int z,y;3. Добавьте окончательный ноль: b[y] = 0. Таким образом, реинитализация не требуется.
Kobor42
1
Хорошее решение. Я бы изменил кое-что. Т.е. возвращаясь в строку назад, чтобы ввод любого размера мог быть обработан правильно.
Kobor42
Все эти 8s должны быть заменены на CHAR_BIT.
алк
12

Быстрое и простое решение:

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

Работает для любого типа размера и для подписанных и беззнаковых целых. '& 1' необходим для обработки подписанных целых, поскольку сдвиг может расширять подпись.

Есть так много способов сделать это. Вот супер простой способ печати 32-битных или n-битных данных из 32-битного типа со знаком или без знака (не помещая отрицательный знак, если подписан, просто печатая фактические биты) и без возврата каретки. Обратите внимание, что я уменьшается до сдвига битов:

#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);
Robotbugs
источник
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);
    }
TechplexEngineer
источник
1
будет ли это переполнение более чем с 50 битами?
Янус Троелсен
Хороший вызов, да, это будет ... Мне сказали, что мне нужно использовать malloc, когда-нибудь, понимаешь?
TechplexEngineer
Ну конечно; естественно. супер просто:char* buffer = (char*) malloc(sizeof(char) * 50);
Янус Троелсен
@JanusTroelsen, или намного чище, меньше, ремонтопригоден:char *buffer = malloc(sizeof(*buffer) * 50);
Шахбаз
Почему "% B" будет отличаться от "% b" в этом отношении? В предыдущих ответах говорилось что-то вроде: «В стандартной библиотеке C нет функции форматирования для вывода подобного двоичного файла». и " Некоторые среды выполнения поддерживают"% b ", хотя это не стандарт." ,
Питер Мортенсен
8

Некоторые среды выполнения поддерживают «% b», хотя это не стандарт.

Также смотрите здесь для интересного обсуждения:

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

НТН

rlerallut
источник
1
На самом деле это свойство библиотеки времени выполнения C, а не компилятора.
Cjm
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
mrwes
источник
1
«#define width 64» конфликтует с stream.h из log4cxx. Пожалуйста, используйте условно случайные определения имен :)
kagali-san
5
@mhambra: вы должны отключить log4cxx за использование такого общего имени, как widthвместо этого!
u0b34a0f6ae
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
Chux - Восстановить Монику
источник
Это очень полезно. Вы знаете, как использовать его в C ++? При компиляции выдается ошибка «Код серьезности Описание Ошибка состояния строки файла проекта C4576, заключенный в скобки тип, за которым следует список инициализатора, представляет собой нестандартный синтаксис явного преобразования типа hello 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
quinmars
источник
1
на этом фронте тоже есть несколько других вариантов ruby -e 'printf("%b\n", 0xabc)', dcзатем 2oследуют 0x123pи так далее.
Линдес
6

В стандартной библиотеке C нет функции форматирования для вывода подобного двоичного файла. Все операции форматирования, поддерживаемые семейством printf, направлены на читабельный текст.

Флориан Бёш
источник
5

Следующая рекурсивная функция может быть полезна:

void bin(int n)
{
    /* Step 1 */
    if (n > 1)
        bin(n/2);
    /* Step 2 */
    printf("%d", n % 2);
}
Капил
источник
7
Будьте осторожны, это не работает с отрицательными целыми числами.
Андерсон Фрейтас
4

Я оптимизировал лучшее решение для размера и 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;
}
paniq
источник
3
Если вы хотите использовать динамическую память (сквозную std::string), вы можете также избавиться от staticмассива. Простейшим способом было бы просто удалить staticспецификатор и сделать его bлокальным для функции.
Шахбаз
((x>>z) & 0x01) + '0'достаточно.
Джейсон C
4

Печатайте биты любого типа, используя меньше кода и ресурсов

Этот подход имеет в качестве атрибутов:

  • Работает с переменными и литералами.
  • Не повторяет все биты, когда это не нужно.
  • Вызывайте printf только после завершения байта (не обязательно для всех битов).
  • Работает для любого типа.
  • Работает с небольшим и большим порядком байтов (для проверки использует GCC #defines).
  • Использует typeof (), который не является стандартом C, но в значительной степени определяется.
#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;
}
eMPee584
источник
1
При countдостижении MAXCNT - 1следующего приращения значение countбудет MAXCNTравно нулю, что приведет к выходу за пределы массива. Вы должны были сделать count = (count + 1) % MAXCNT.
Шахбаз
1
Кстати, это станет неожиданностью для разработчика, который использует MAXCNT + 1вызовы этой функции в отдельности printf. В общем, если вы хотите предоставить опцию для более чем 1 вещи, сделайте ее бесконечной. Числа, такие как 4, могут только вызвать проблему.
Шахбаз
3

Нет стандартного и портативного способа.

Некоторые реализации предоставляют itoa () , но в большинстве случаев этого не произойдет, и он имеет несколько вялый интерфейс. Но код находится за ссылкой и должен позволить вам довольно легко реализовать свой собственный форматтер.

wnoise
источник
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);

luart
источник
1
Это идиоматическое решение для C ++, но он просил C.
Данияр
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");
SarahGaidi
источник
3

На основе @ ideasman42 - х внушения в своем ответе, это макрос , который обеспечивает int8, 16, 32& 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
Et7f3XIV
источник
спасибо, что скопировал этот код, первым, кто скопировал в этом проекте, над которым я работаю, написание этого было утомительным занятием :)
DevZer0
3

Использование:

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

Для получения дополнительной информации см. Как напечатать двоичное число через printf .

kapilddit
источник
1
В предыдущем ответе говорилось: «Некоторые реализации предоставляют itoa (), но в большинстве случаев этого не будет» ?
Питер Мортенсен
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;
}
Йола
источник
Что вы подразумеваете под "Далее покажет вам макет памяти" ?
Питер Мортенсен
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
Лео
источник