¿Hay un convertidor printf para imprimir en formato binario?


435

Puedo imprimir con printf como un número hexadecimal u octal. ¿Hay una etiqueta de formato para imprimir como base binaria o arbitraria?

Estoy ejecutando gcc.

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

No puedes hacer esto, hasta donde yo sé, usando printf. Obviamente, podrías escribir un método de ayuda para lograr esto, pero eso no suena como la dirección que quieres seguir.
Ian P

No hay un formato predefinido para eso. Necesita transformarlo usted mismo en una cadena y luego imprimir la cadena.
rslite

Una búsqueda rápida en Google produjo esta página con información que puede ser útil: forums.macrumors.com/archive/index.php/t-165959.html
Ian P

12
No como parte de la Biblioteca ANSI Standard C: si está escribiendo código portátil, el método más seguro es rodar el suyo.
tomlogic

Una solución estándar y genérica (para cualquier tipo integral de cualquier longitud) de la conversión a cadena binaria en C ++: stackoverflow.com/a/31660310/1814353
luart

Respuestas:


267

Hacky pero funciona para mí:

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

Para tipos de múltiples bytes

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

Necesitas todas las citas extra desafortunadamente. Este enfoque tiene los riesgos de eficiencia de las macros (no pase una función como argumento BYTE_TO_BINARY) pero evita los problemas de memoria y las múltiples invocaciones de strcat en algunas de las otras propuestas aquí.


13
Y tiene la ventaja de ser invocable varias veces en una printfque los que tienen staticbuffers no pueden.
Patrick Schlüter

44
Me he tomado la libertad de cambiar el %da %c, porque debería ser aún más rápido ( %dtiene que realizar la conversión digit-> char, mientras que %csimplemente genera el argumento

3
Publicó una versión ampliada de esta macro con 16, 32, 64 bit int support: stackoverflow.com/a/25108449/432509
ideasman42

2
Tenga en cuenta que este enfoque no es amistoso con la pila. Suponiendo que el sistema inttenga 32 bits, la impresión de un solo valor de 32 bits requerirá espacio para los valores de 32 * 4 bytes; total de 128 bytes. Lo cual, dependiendo del tamaño de la pila, puede o no ser un problema.
user694733

1
es importante agregar paréntesis alrededor del byte en la macro o podría tener problemas al enviar una operación BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) & 0x01
Ivan Hoffmann

203

Imprimir binario para cualquier tipo de datos

//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("");
}

prueba

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
Sugiera size_t i; for (i=size; i-- > 0; )evitar size_tvs. intno coincidencia.
chux - Restablece a Mónica el

1
¿Podría alguien explicar la lógica detrás de este código?
jII

2
Tome cada byte en ptr(bucle externo); entonces para cada bit el byte actual (bucle interno), enmascare el byte por el bit actual ( 1 << j). Desplace esa derecha dando como resultado un byte que contenga 0 ( 0000 0000b) o 1 ( 0000 0001b). Imprima el byte resultante printf con formato %u. HTH.
nielsbot

1
@ ZX9 Observe que el código sugerido se utiliza >con size_ty no el >=de su comentario para determinar cuándo finalizar el ciclo.
chux

3
@ ZX9 Sigue siendo un comentario original útil suyo, ya que los codificadores deben tener cuidado al considerar el uso de mayúsculas >y minúsculas y >=con tipos sin signo. 0es un caso de borde sin signo y ocurre comúnmente, a diferencia de las matemáticas con signo menos comunes INT_MAX/INT_MIN.
chux

151

Aquí hay un truco rápido para demostrar técnicas para hacer lo que quieres.

#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
Esto es ciertamente menos "extraño" que escribir de forma personalizada una sobrecarga de escape para printf. También es fácil de entender para un desarrollador nuevo en el código.
Furious Coder

43
Algunos cambios: strcates un método ineficiente de agregar un único carácter a la cadena en cada pasada del bucle. En su lugar, agregue char *p = b;ay reemplace el bucle interno con *p++ = (x & z) ? '1' : '0'. zdebería comenzar en 128 (2 ^ 7) en lugar de 256 (2 ^ 8). Considere actualizar para llevar un puntero al búfer para usar (por seguridad de subprocesos), similar a inet_ntoa().
tomlogic

3
@EvilTeach: ¡Usted mismo está utilizando un operador ternario como parámetro para strcat()! Estoy de acuerdo en que strcatprobablemente sea más fácil de entender que el incremento posterior de un puntero desreferenciado para la tarea, pero incluso los principiantes necesitan saber cómo usar correctamente la biblioteca estándar. Tal vez usar una matriz indexada para la asignación hubiera sido una buena demostración (y realmente funcionará, ya bque no se restablece a todos ceros cada vez que se llama a la función).
tomlogic

3
Aleatorio: el carácter binario del búfer es estático y se borra a todos los ceros en la asignación. Esto solo lo borrará la primera vez que se ejecute, y después de eso no se borrará, sino que usará el último valor.
markwatson

8
Además, este debe documentar que el resultado anterior no será válido después de llamar a la función de nuevo, por lo que las personas que llaman no debe tratar de utilizar de esta manera: printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann

84

No hay un especificador de conversión binaria en glibc normalmente.

Es posible agregar tipos de conversión personalizados a la familia de funciones printf () en glibc. Ver register_printf_function para más detalles. Puede agregar una conversión% b personalizada para su propio uso, si simplifica el código de la aplicación para tenerla disponible.

Aquí hay un ejemplo de cómo implementar formatos de impresión personalizados en glibc.


Siempre he escrito mi propio v [snf] printf () para los casos limitados en los que quería diferentes radixs: me alegro de haber examinado esto.
Jamie

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Hay una nueva función para hacer lo mismo, sin embargo: register_printf_specifier(). Puede encontrar un ejemplo del nuevo uso aquí: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

Puede usar una mesa pequeña para mejorar la velocidad 1 . Técnicas similares son útiles en el mundo incrustado, por ejemplo, para invertir un byte:

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 Me refiero principalmente a aplicaciones integradas donde los optimizadores no son tan agresivos y la diferencia de velocidad es visible.


27

Imprima el bit menos significativo y muévalo a la derecha. Hacer esto hasta que el entero se convierta en cero imprime la representación binaria sin ceros a la izquierda pero en orden inverso. Usando la recursividad, el orden se puede corregir con bastante facilidad.

#include <stdio.h>

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

Para mí, esta es una de las soluciones más limpias para el problema. Si le gusta el 0bprefijo y un nuevo carácter de línea final, sugiero ajustar la función.

Demostración en línea


error: muy pocos argumentos para llamar a la función, esperado 2, tienen 1 putc ((número y 1)? '1': '0');
Koray Tugay

@KorayTugay Gracias por señalar eso. Corregí la llamada a la función y agregué una demostración.
danijar

66
también debe usar un número int sin signo, porque cuando el número dado es negativo, la función ingresa en una llamada recursiva interminable.
Puffy

Enfoque más eficiente, ya que en ASCII, '0' + 1 = '1':putc('0'+(number&1), stdout);
Roger Dueck

22

Sobre la base de la respuesta de @William Whyte, este es un macro que proporciona int8, 16, 32y 64versiones, la reutilización de la INT8macro a la repetición evitar.

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

Esto produce:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Para facilitar la lectura, puede agregar un separador, por ejemplo:

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

Esto es excelente. ¿Hay alguna razón particular para imprimir los bits que comienzan con Bits menos significativos?
gaganso

2
¿Cómo recomendarías agregar la coma?
nmz787

Agregaría una versión agrupada de PRINTF_BYTE_TO_BINARY_INT#define para usar opcionalmente.
ideasman42

16

Aquí hay una versión de la función que no sufre problemas de reentrada o límites en el tamaño / tipo del argumento:

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

Tenga en cuenta que este código funcionaría igual de bien para cualquier base entre 2 y 10 si solo reemplaza los 2 por la base deseada. El uso es:

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

¿Dónde xestá cualquier expresión integral?


77
Sí, se puede hacer eso. Pero es realmente un mal diseño. Incluso si no tiene subprocesos o reentrada, la persona que llama debe ser consciente de que el búfer estático se está reutilizando y que cosas como las char *a = binary_fmt(x), *b = binary_fmt(y);que no funcionan como se esperaba. Obligar a la persona que llama a pasar un búfer hace explícito el requisito de almacenamiento; la persona que llama es, por supuesto, libre de usar un búfer estático si realmente lo desea, y luego la reutilización del mismo búfer se vuelve explícita. También tenga en cuenta que, en las ABI modernas de PIC, los buffers estáticos generalmente cuestan más código para acceder que los buffers en la pila.
R .. GitHub DEJA DE AYUDAR AL HIELO

8
Eso sigue siendo un mal diseño. Requiere un paso de copia adicional en esos casos, y no es menos costoso que hacer que la persona que llama proporcione el búfer incluso en los casos en que no se requiera la copia. Usar almacenamiento estático es solo una mala expresión.
R .. GitHub DEJA DE AYUDAR AL HIELO

3
Tener que contaminar el espacio de nombres de la tabla de símbolos de preprocesador o variable con un nombre adicional innecesario que debe usarse para dimensionar adecuadamente el almacenamiento que debe asignar cada persona que llama, y ​​obliga a cada persona que llama a conocer este valor y asignar la cantidad necesaria de almacenamiento, es un mal diseño cuando la solución de almacenamiento local de función más simple será suficiente para la mayoría de los intentos y propósitos, y cuando una simple llamada strdup () cubre el 99% del resto de los usos.
Greg A. Woods,

55
Aquí vamos a tener que estar en desacuerdo. No puedo ver cómo agregar un símbolo de preprocesador discreto se acerca a la nocividad de limitar severamente los casos de uso, haciendo que la interfaz sea propensa a errores, reservando almacenamiento permanente durante la duración del programa por un valor temporal y generando peor código en la mayoría plataformas modernas
R .. GitHub DEJA DE AYUDAR A HIELO

55
No abogo por la micro-optimización sin razón (es decir, mediciones). Pero sí creo que vale la pena mencionar el rendimiento, incluso si está en la escala de micro ganancia, cuando se trata de una bonificación junto con un diseño fundamentalmente superior.
R .. GitHub DEJA DE AYUDAR A HIELO

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

66
Más claro si usa '1'y en '0'lugar de 49y 48en su ternario. Además, bdebe tener 9 caracteres de longitud para que el último carácter pueda seguir siendo un terminador nulo.
tomlogic

Además, B debe inicializarse cada vez.
EvilTeach

2
No si cambia alguna: 1. Agregar espacio para un cero final: static char b[9] = {0}2. Declaración movimiento fuera del bucle: int z,y;3. Añadir el cero final: b[y] = 0. De esta manera no se necesita reinitalización.
Kobor42

1
Buena solución Aunque cambiaría algunas cosas. Es decir, retroceder en la cadena para que la entrada de cualquier tamaño se pueda manejar correctamente.
Kobor42

Todos esos 8s deben ser reemplazados por CHAR_BIT.
alk

12

Solución rápida y fácil:

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

Funciona para cualquier tipo de tamaño y para entradas firmadas y sin firmar. El '& 1' es necesario para manejar entradas firmadas ya que el cambio puede hacer la extensión de la señal.

Hay muchas formas de hacer esto. Aquí hay uno súper simple para imprimir 32 bits o n bits desde un tipo de 32 bits con signo o sin signo (sin poner un negativo si está firmado, solo imprime los bits reales) y sin retorno de carro. Tenga en cuenta que i disminuye antes del cambio de bit:

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

¿Qué hay de devolver una cadena con los bits para almacenar o imprimir más tarde? Puede asignar la memoria y devolverla, y el usuario tiene que liberarla, o bien puede devolver una cadena estática, pero se bloqueará si se vuelve a llamar o por otro hilo. Ambos métodos mostrados:

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

Llamar con:

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

Ninguna de las respuestas publicadas anteriormente es exactamente lo que estaba buscando, así que escribí una. ¡Es súper simple usar% B con el 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
¿se desbordará con más de 50 bits?
Janus Troelsen

Buena decisión, sí lo hará ... Me dijeron que necesitaba usar Malloc, ¿alguna vez te has puesto eso?
TechplexEngineer

sí, por supuesto. súper fácil:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen

@JanusTroelsen, o mucho más limpio, más pequeño, mantenible:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

¿Por qué "% B" sería diferente a "% b" a este respecto? Las respuestas anteriores decían cosas como "No hay una función de formato en la biblioteca estándar C para generar binarios como ese". y " Algunos tiempos de ejecución admiten"% b "aunque eso no es un estándar". .
Peter Mortensen


7

Este código debe manejar sus necesidades de hasta 64 bits. Creé 2 funciones pBin y pBinFill. Ambos hacen lo mismo, pero pBinFill llena los espacios iniciales con el fillChar. La función de prueba genera algunos datos de prueba, luego los imprime utilizando la función.



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" entra en conflicto con stream.h desde log4cxx. Por favor, use nombres de definición aleatorios convencionales :)
kagali-san

55
@mhambra: ¡deberías decirle a log4cxx que no use un nombre tan genérico como en su widthlugar!
u0b34a0f6ae

7

¿Hay un convertidor printf para imprimir en formato binario?

La printf()familia solo puede imprimir en base 8, 10 y 16 utilizando directamente los especificadores estándar. Sugiero crear una función que convierta el número en una cadena según las necesidades particulares del código.


Para imprimir en cualquier base [2-36]

Todas las demás respuestas hasta ahora tienen al menos una de estas limitaciones.

  1. Use memoria estática para el búfer de retorno. Esto limita el número de veces que la función puede usarse como argumento para printf().

  2. Asigne memoria que requiere el código de llamada para liberar punteros.

  3. Requerir que el código de llamada proporcione explícitamente un búfer adecuado.

  4. Llamar printf()directamente Esto obliga a una nueva función para la que fprintf(), sprintf(), vsprintf(), etc.

  5. Use un rango entero reducido.

Lo siguiente no tiene ninguna de las limitaciones anteriores . Requiere C99 o posterior y uso de "%s". Utiliza un literal compuesto para proporcionar el espacio del búfer. No tiene problemas con múltiples llamadas en a 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;
}

Salida

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

Esto es muy útil ¿Sabes cómo usarlo en C ++? Cuando compilo, genera un error "Código de gravedad Descripción Archivo de proyecto Error de estado de supresión de línea C4576 un tipo entre paréntesis seguido de una lista de inicialización es una sintaxis de conversión de tipo explícito no estándar hola C: \ my_projects \ hello \ hello \ main.cpp 39 "
Solo aprendiz el

1
@Justalearner Esto genera un C ++ porque si usa un literal compuesto de característica C que no es parte de C ++. Quizás publique su implementación de C ++ que intente hacer lo mismo, incluso si está incompleta, estoy seguro de que obtendrá ayuda, siempre que muestre su intento primero.
chux - Restablecer Monica

6

Tal vez un poco OT, pero si necesita esto solo para depurar para comprender o volver sobre algunas operaciones binarias que está haciendo, puede echar un vistazo a wcalc (una calculadora de consola simple). Con las opciones -b obtienes salida binaria.

p.ej

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

1
También hay algunas otras opciones en este frente ... ruby -e 'printf("%b\n", 0xabc)', dcseguidas de 2oseguidas de 0x123p, y así sucesivamente.
lindes

6

No hay una función de formateo en la biblioteca estándar de C para generar binarios como ese. Todas las operaciones de formato que admite la familia printf son para texto legible por humanos.


5

La siguiente función recursiva puede ser útil:

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

77
Tenga cuidado, esto no funciona con enteros negativos.
Anderson Freitas

4

Optimicé la mejor solución para tamaño y C ++, y llegué a esta solución:

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
Si desea utilizar la memoria dinámica (a través std::string), también podría deshacerse de la staticmatriz. La forma más sencilla sería simplemente soltar el staticcalificador y hacer bque la función sea local.
Shahbaz

((x>>z) & 0x01) + '0'es suficiente.
Jason C

4

Imprima bits de cualquier tipo usando menos código y recursos

Este enfoque tiene como atributos:

  • Funciona con variables y literales.
  • No itera todos los bits cuando no es necesario.
  • Llame a printf solo cuando complete un byte (no innecesariamente para todos los bits).
  • Funciona para cualquier tipo.
  • Funciona con poca y gran endianness (usa GCC #defines para verificar).
  • Utiliza typeof () que no es estándar C pero está ampliamente definido.
#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;
}

Salida

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

He usado otro enfoque ( bitprint.h ) para llenar una tabla con todos los bytes (como cadenas de bits) e imprimirlos en función del byte de entrada / índice. Vale la pena echarle un vistazo.


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

}

O agregue 0 o 1 al valor de carácter de '0';) No se necesita ternario.
Búho

3

Me gustó el código de paniq, el buffer estático es una buena idea. Sin embargo, falla si desea múltiples formatos binarios en un solo printf () porque siempre devuelve el mismo puntero y sobrescribe la matriz.

Aquí hay un menú desplegable de estilo C que gira el puntero en un búfer dividido.

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
Una vez que countalcanza MAXCNT - 1, el siguiente incremento de countlo haría en MAXCNTlugar de cero, lo que provocará un acceso fuera de los límites de la matriz. Deberías haberlo hecho count = (count + 1) % MAXCNT.
Shahbaz

1
Por cierto, esto sería una sorpresa más tarde para un desarrollador que usa MAXCNT + 1llamadas a esta función de una sola vez printf. En general, si desea dar la opción para más de 1 cosa, hágalo infinito. Números como 4 solo pueden causar problemas.
Shahbaz

3

No hay forma estándar y portátil.

Algunas implementaciones proporcionan itoa () , pero no va a estar en la mayoría, y tiene una interfaz algo mala. Pero el código está detrás del enlace y debería permitirle implementar su propio formateador con bastante facilidad.


3

Conversión genérica de una declaración de cualquier tipo integral en la representación de cadena binaria usando la biblioteca estándar :

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

O solo: std::cout << std::bitset<sizeof(num) * 8>(num);


1
Esa es una solución idiomática para C ++ pero estaba preguntando por C.
danijar

3

Mi solución:

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

Sobre la base de la sugerencia de @ ideasman42 en su respuesta, esta es una macro que proporciona int8, 16, 32y 64versiones, la reutilización de la INT8macro para evitar la repetición.

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

Esto produce:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Para facilitar la lectura, puede cambiar: #define PRINTF_BINARY_SEPARATORa#define PRINTF_BINARY_SEPARATOR "," o#define PRINTF_BINARY_SEPARATOR " "

Esto generará:

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

o

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

gracias por copiar este código, el primero en copiar en este proyecto en el que estoy trabajando, escribir esto se sintió como una tarea tediosa :)
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);
        }
}

debería funcionar - no probado.


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

44
Pagar el costo de una mala ubicación perjudicará el rendimiento. Pasar la responsabilidad por la destrucción del búfer a la persona que llama no es amable.
EvilTeach

2

A continuación le mostrará el diseño de la memoria:

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

¿Qué quiere decir con "Siguiente le mostrará el diseño de memoria" ?
Peter Mortensen

2

Aquí hay una pequeña variación de la solución de paniq que usa plantillas para permitir la impresión de enteros de 32 y 64 bits:

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

Y se puede usar como:

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

Aquí está el resultado:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.