Gibt es einen printf-Konverter zum Drucken im Binärformat?


433

Ich kann mit printf als Hex- oder Oktalzahl drucken. Gibt es ein Format-Tag, das als binäre oder beliebige Basis gedruckt werden soll?

Ich laufe gcc.

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

Soweit ich weiß, können Sie dies nicht mit printf tun. Sie könnten natürlich eine Hilfsmethode schreiben, um dies zu erreichen, aber das klingt nicht nach der Richtung, in die Sie gehen möchten.
Ian P

Dafür ist kein Format vordefiniert. Sie müssen es selbst in eine Zeichenfolge umwandeln und dann die Zeichenfolge drucken.
Rslite

Eine schnelle Google-Suche ergab
Ian P

12
Nicht als Teil der ANSI Standard C-Bibliothek - wenn Sie tragbaren Code schreiben, ist es am sichersten, Ihren eigenen zu rollen.
Tomlogic

Eine Anweisung Standard und generische (für jeden Integral-Typ beliebiger Länge) Lösung der Konvertierung in eine Binärzeichenfolge unter
luart

Antworten:


266

Hacky aber funktioniert bei mir:

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

Für Multi-Byte-Typen

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

Sie benötigen leider alle zusätzlichen Angebote. Dieser Ansatz BYTE_TO_BINARYbirgt die Effizienzrisiken von Makros (geben Sie keine Funktion als Argument an ), vermeidet jedoch die Speicherprobleme und Mehrfachaufrufe von strcat in einigen anderen Vorschlägen.


13
Und hat auch den Vorteil, mehrfach aufrufbar zu sein, printfwas diejenigen mit staticPuffern nicht können.
Patrick Schlüter

4
Ich habe mir die Freiheit genommen, um die Änderung %dzu %c, weil es noch schneller sein sollte ( %dhat Ziffern-> char - Konvertierung, während %ceinfach das Argument ausgibt

3
Veröffentlichte eine erweiterte Version dieses Makros mit 16, 32, 64-Bit-Int-Unterstützung: stackoverflow.com/a/25108449/432509
ideasman42

2
Beachten Sie, dass dieser Ansatz nicht stapelfreundlich ist. Angenommen, intauf dem System sind 32 Bit vorhanden, erfordert das Drucken eines einzelnen 32-Bit-Werts Platz für 32 * 4-Byte-Werte. insgesamt 128 Bytes. Was je nach Stapelgröße ein Problem sein kann oder nicht.
user694733

1
Es ist wichtig, Klammern um das Byte im Makro einzufügen, da sonst beim Senden einer Operation BYTE_TO_BINARY (a | b) -> a | Probleme auftreten können b & 0x01! = (a | b) & 0x01
Ivan Hoffmann

203

Binär für jeden Datentyp drucken

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

Prüfung

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
Schlagen Sie size_t i; for (i=size; i-- > 0; )zu vermeiden size_tvs. intmis-match.
chux

1
Könnte jemand bitte die Logik hinter diesem Code näher erläutern?
jII

2
Nehmen Sie jedes Byte in ptr(äußere Schleife); Maskieren Sie dann für jedes Bit das aktuelle Byte (innere Schleife) das Byte durch das aktuelle Bit ( 1 << j). Verschieben Sie dieses Recht nach rechts, was zu einem Byte führt, das 0 ( 0000 0000b) oder 1 ( 0000 0001b) enthält. Drucken Sie das resultierende Byte printf mit Format %u. HTH.
Nielsbot

1
@ ZX9 Beachten Sie, dass der vorgeschlagene Code verwendet >mit size_tund nicht die >=von Ihrem Kommentar zu bestimmen , wann die Schleife zu beenden.
chux

3
@ ZX9 Immer noch ein nützlicher Originalkommentar von Ihnen, da Codierer angesichts der Verwendung von >und >=mit vorzeichenlosen Typen in Randfällen vorsichtig sein müssen . 0ist ein vorzeichenloser Randfall und tritt häufig auf, im Gegensatz zu vorzeichenbehafteter Mathematik mit weniger häufigem INT_MAX/INT_MIN.
chux

151

Hier ist ein kurzer Hack, um Techniken zu demonstrieren, mit denen Sie das tun können, was Sie wollen.

#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
Dies ist sicherlich weniger "seltsam" als das benutzerdefinierte Schreiben einer Escape-Überladung für printf. Es ist auch für einen Entwickler, der neu im Code ist, einfach zu verstehen.
Furious Coder

43
Einige Änderungen: strcatDies ist eine ineffiziente Methode zum Hinzufügen eines einzelnen Zeichens zur Zeichenfolge bei jedem Durchlauf der Schleife. Fügen Sie stattdessen ein hinzu char *p = b;und ersetzen Sie die innere Schleife durch *p++ = (x & z) ? '1' : '0'. zsollte bei 128 (2 ^ 7) statt 256 (2 ^ 8) beginnen. Erwägen Sie eine Aktualisierung, um einen Zeiger auf den zu verwendenden Puffer (zur Thread-Sicherheit) zu verwenden, ähnlich wie inet_ntoa().
Tomlogic

3
@ EvilTeach: Sie verwenden selbst einen ternären Operator als Parameter für strcat()! Ich bin damit einverstanden, dass dies strcatwahrscheinlich einfacher zu verstehen ist als das Nachinkrementieren eines dereferenzierten Zeigers für die Zuweisung, aber selbst Anfänger müssen wissen, wie sie die Standardbibliothek richtig verwenden. Vielleicht wäre die Verwendung eines indizierten Arrays für die Zuweisung eine gute Demonstration gewesen (und funktioniert tatsächlich, da sie bnicht jedes Mal, wenn Sie die Funktion aufrufen, auf Null gesetzt wird).
Tomlogic

3
Zufällig: Das binäre Pufferzeichen ist statisch und wird in der Zuweisung auf alle Nullen gelöscht. Dies löscht es nur beim ersten Ausführen und danach nicht mehr, sondern verwendet stattdessen den letzten Wert.
Markwatson

8
Außerdem sollte dies dokumentieren, dass das vorherige Ergebnis nach erneutem Aufrufen der Funktion ungültig ist, sodass Aufrufer nicht versuchen sollten, es wie folgt zu verwenden : printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann

84

Normalerweise gibt es in glibc keinen binären Konvertierungsspezifizierer.

In glibc können der Funktionsfamilie printf () benutzerdefinierte Konvertierungstypen hinzugefügt werden. Siehe register_printf_function für Details. Sie können eine benutzerdefinierte% b-Konvertierung für Ihren eigenen Gebrauch hinzufügen, wenn dies den Anwendungscode vereinfacht, damit er verfügbar ist.

Hier ist ein Beispiel für die Implementierung eines benutzerdefinierten Druckformats in glibc.


Ich habe immer mein eigenes v [snf] printf () für die begrenzten Fälle geschrieben, in denen ich verschiedene Radixe haben wollte: Ich bin so froh, dass ich darüber gestöbert habe.
Jamie

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Es gibt jedoch eine neue Funktion, um dasselbe zu tun : register_printf_specifier(). Ein Beispiel für die neue Nutzung finden Sie hier: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

Sie können einen kleinen Tisch verwenden, um die Geschwindigkeit 1 zu verbessern . Ähnliche Techniken sind in der eingebetteten Welt nützlich, um beispielsweise ein Byte zu invertieren:

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 Ich beziehe mich hauptsächlich auf eingebettete Anwendungen, bei denen Optimierer nicht so aggressiv sind und der Geschwindigkeitsunterschied sichtbar ist.


27

Drucken Sie das niedrigstwertige Bit und verschieben Sie es nach rechts. Wenn Sie dies tun, bis die Ganzzahl Null wird, wird die Binärdarstellung ohne führende Nullen in umgekehrter Reihenfolge gedruckt. Mit der Rekursion kann die Reihenfolge ganz einfach korrigiert werden.

#include <stdio.h>

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

Für mich ist dies eine der saubersten Lösungen für das Problem. Wenn Sie ein 0bPräfix und ein nachfolgendes neues Zeilenzeichen mögen , empfehle ich, die Funktion einzuschließen.

Online-Demo


Fehler: Zu wenige Argumente für Funktionsaufruf, erwartete 2, haben 1 putc ((Nummer & 1)? '1': '0');
Koray Tugay

@KorayTugay Danke, dass du darauf hingewiesen hast. Ich habe den Funktionsaufruf korrigiert und eine Demo hinzugefügt.
Danijar

6
Sie sollten auch eine vorzeichenlose int-Nummer verwenden, da die Funktion bei einer negativen Nummer einen endlosen rekursiven Aufruf ausführt.
Puffy

Effizienterer Ansatz, da in ASCII '0' + 1 = '1':putc('0'+(number&1), stdout);
Roger Dueck

22

Basierend auf @William Whyte Antwort, das ist ein Makro , das bietet int8, 16, 32& 64Versionen, die Wiederverwendung von INT8Makro um Wiederholungen zu vermeiden.

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

Dies gibt aus:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Zur besseren Lesbarkeit möchten Sie möglicherweise ein Trennzeichen hinzufügen, z.

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

Das ist ausgezeichnet. Gibt es einen bestimmten Grund für das Drucken der Bits, beginnend mit den am wenigsten signifikanten Bits?
Gaganso

2
Wie würden Sie das Hinzufügen des Kommas empfehlen?
nmz787

Fügt eine gruppierte Version von PRINTF_BYTE_TO_BINARY_INT#Definitionen hinzu, die optional verwendet werden soll.
ideasman42

16

Hier ist eine Version der Funktion, die nicht unter Wiedereintrittsproblemen oder Einschränkungen der Größe / des Typs des Arguments leidet:

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

Beachten Sie, dass dieser Code für jede Basis zwischen 2 und 10 genauso gut funktioniert, wenn Sie nur die 2 durch die gewünschte Basis ersetzen. Verwendung ist:

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

Wo xist ein integraler Ausdruck?


7
Ja, das kannst du machen. Aber es ist wirklich schlechtes Design. Selbst wenn Sie keine Threads oder Wiedereintritte haben, muss der Aufrufer wissen, dass der statische Puffer wiederverwendet wird und dass solche Dinge char *a = binary_fmt(x), *b = binary_fmt(y);nicht wie erwartet funktionieren. Wenn der Aufrufer gezwungen wird, einen Puffer zu übergeben, wird die Speicheranforderung explizit. Dem Aufrufer steht es natürlich frei, einen statischen Puffer zu verwenden, wenn dies wirklich gewünscht wird, und dann wird die Wiederverwendung desselben Puffers explizit. Beachten Sie auch, dass bei modernen PIC-ABIs statische Puffer normalerweise mehr Code für den Zugriff kosten als Puffer auf dem Stapel.
R .. GitHub STOP HELPING ICE

8
Das ist immer noch ein schlechtes Design. In diesen Fällen ist ein zusätzlicher Kopierschritt erforderlich, und es ist nicht weniger teuer, als wenn der Anrufer den Puffer bereitstellt, selbst wenn kein Kopieren erforderlich wäre. Die Verwendung von statischem Speicher ist nur eine schlechte Redewendung.
R .. GitHub STOP HELPING ICE

3
Sie müssen den Namespace der Präprozessor- oder Variablensymboltabelle mit einem unnötigen zusätzlichen Namen verschmutzen, der verwendet werden muss, um den von jedem Anrufer zugewiesenen Speicher richtig zu dimensionieren , und jeden Anrufer dazu zwingen, diesen Wert zu kennen und die erforderliche Menge von zuzuweisen Speicher ist ein schlechtes Design, wenn die einfachere funktionslokale Speicherlösung für die meisten Absichten und Zwecke ausreicht und wenn ein einfacher Aufruf von strdup () 99% der restlichen Verwendungen abdeckt.
Greg A. Woods

5
Hier müssen wir nicht zustimmen. Ich kann nicht sehen, wie das Hinzufügen eines unauffälligen Präprozessorsymbols der Schädlichkeit nahe kommt, die Anwendungsfälle stark einzuschränken, die Schnittstelle fehleranfällig zu machen, permanenten Speicher für die Dauer des Programms für einen temporären Wert zu reservieren und bei den meisten schlechteren Code zu generieren moderne Plattformen.
R .. GitHub STOP HELPING ICE

5
Ich befürworte keine Mikrooptimierung ohne Grund (dh Messungen). Ich denke jedoch, dass Leistung, auch wenn sie auf der Mikrogewinnskala liegt, erwähnenswert ist, wenn sie als Bonus zusammen mit einem grundlegend überlegenen Design angeboten wird.
R .. GitHub STOP HELPING ICE

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

    b[y] = 0;

    return b;
}

6
Klarer, wenn Sie '1'und '0'anstelle von 49und 48in Ihrem ternären verwenden. Sollte außerdem b9 Zeichen lang sein, damit das letzte Zeichen ein Nullterminator bleiben kann.
Tomlogic

Auch B muss jedes Mal initialisiert werden.
EvilTeach

2
Nicht, wenn Sie einige ändern: 1. Platz für eine endgültige Null hinzufügen: static char b[9] = {0}2. Deklaration aus der Schleife verschieben: int z,y;3. Endgültige Null hinzufügen : b[y] = 0. Auf diese Weise ist keine Reinitalisierung erforderlich.
Kobor42

1
Schöne Lösung. Ich würde allerdings ein paar Sachen ändern. Dh in der Zeichenfolge rückwärts gehen, damit Eingaben beliebiger Größe ordnungsgemäß verarbeitet werden können.
Kobor42

Alle diese 8s sollten durch ersetzt werden CHAR_BIT.
Alk

12

Schnelle und einfache Lösung:

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

Funktioniert für alle Größen und für signierte und nicht signierte Ints. Das '& 1' wird benötigt, um vorzeichenbehaftete Ints zu verarbeiten, da die Verschiebung möglicherweise eine Vorzeichenerweiterung bewirkt.

Es gibt so viele Möglichkeiten, dies zu tun. Hier ist eine supereinfache Methode zum Drucken von 32 Bit oder n Bits von einem vorzeichenbehafteten oder vorzeichenlosen 32-Bit-Typ (kein Negativ setzen, wenn signiert, nur Drucken der tatsächlichen Bits) und ohne Wagenrücklauf. Beachten Sie, dass i vor der Bitverschiebung dekrementiert wird:

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

Was ist mit der Rückgabe einer Zeichenfolge mit den Bits, die später gespeichert oder gedruckt werden sollen? Sie können den Speicher entweder zuweisen und zurückgeben, und der Benutzer muss ihn freigeben, oder Sie geben eine statische Zeichenfolge zurück, die jedoch beim erneuten Aufruf oder durch einen anderen Thread überlastet wird. Beide Methoden gezeigt:

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

Rufen Sie an mit:

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

Keine der zuvor veröffentlichten Antworten ist genau das, wonach ich gesucht habe, also habe ich eine geschrieben. Es ist super einfach,% B mit dem zu verwenden 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
Wird dies mit mehr als 50 Bit überlaufen?
Janus Troelsen

Guter Anruf, ja, das wird es ... Mir wurde gesagt, ich müsse Malloc verwenden, jemals das anziehen?
TechplexEngineer

ja natürlich. super einfach:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen

@ JanusTroelsen oder viel sauberer, kleiner, wartbar:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

Warum sollte sich "% B" in dieser Hinsicht von "% b" unterscheiden? Frühere Antworten besagten Dinge wie "Es gibt keine Formatierungsfunktion in der C-Standardbibliothek, um solche Binärdateien auszugeben." und " Einige Laufzeiten unterstützen"% b ", obwohl dies kein Standard ist." .
Peter Mortensen


7

Dieser Code sollte Ihre Anforderungen bis zu 64 Bit erfüllen. Ich habe 2 Funktionen pBin & pBinFill erstellt. Beide machen dasselbe, aber pBinFill füllt die führenden Felder mit fillChar aus. Die Testfunktion generiert einige Testdaten und druckt sie dann mit der Funktion aus.



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" steht in Konflikt mit stream.h von log4cxx. Bitte verwenden Sie konventionell zufällig definierte Namen :)
Kagali-San

5
@mhambra: Sie sollten log4cxx für die Verwendung eines generischen Namens wie widthstattdessen ausschalten !
u0b34a0f6ae

7

Gibt es einen printf-Konverter zum Drucken im Binärformat?

Die printf()Familie kann nur in Basis 8, 10 und 16 direkt mit den Standardspezifizierern drucken. Ich schlage vor, eine Funktion zu erstellen, die die Zahl gemäß den besonderen Anforderungen des Codes in eine Zeichenfolge konvertiert.


Zum Drucken in einer beliebigen Basis [2-36]

Alle anderen Antworten haben bisher mindestens eine dieser Einschränkungen.

  1. Verwenden Sie den statischen Speicher für den Rückgabepuffer. Dies begrenzt die Häufigkeit, mit der die Funktion als Argument verwendet werden kann printf().

  2. Ordnen Sie Speicher, für den der aufrufende Code erforderlich ist, freien Zeigern zu.

  3. Der aufrufende Code muss explizit einen geeigneten Puffer bereitstellen.

  4. Rufen Sie printf()direkt an. Dies verpflichtet eine neue Funktion zu fprintf(), sprintf(), vsprintf()etc.

  5. Verwenden Sie einen reduzierten ganzzahligen Bereich.

Das Folgende hat keine der oben genannten Einschränkungen . Es erfordert C99 oder höher und die Verwendung von "%s". Es verwendet ein zusammengesetztes Literal , um den Pufferraum bereitzustellen. Es hat keine Probleme mit mehreren Anrufen in einem 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;
}

Ausgabe

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

Das ist sehr nützlich. Wissen Sie, wie man es in C ++ benutzt? Beim Kompilieren wird der Fehler "Schweregrad Code Beschreibung Projektdatei Zeilenunterdrückungsstatus Fehler C4576" generiert. Ein in Klammern stehender Typ gefolgt von einer Initialisiererliste ist eine nicht standardmäßige explizite Typkonvertierungssyntax. Hallo C: \ my_projects \ hello \ hello \ main.cpp 39 "
Nur ein Lernender

1
@Justalearner Dies generiert ein C ++, da bei Verwendung eines zusammengesetzten C-Feature- Literals, das nicht Teil von C ++ ist. Veröffentlichen Sie möglicherweise Ihre C ++ - Implementierung, die versucht, dasselbe zu tun - auch wenn sie unvollständig ist, ich bin sicher, dass Sie Hilfe erhalten werden -, solange Sie zuerst Ihren Versuch zeigen.
chux

6

Vielleicht ein bisschen OT, aber wenn Sie dies nur zum Debuggen benötigen, um einige von Ihnen ausgeführte Binäroperationen zu verstehen oder nachzuvollziehen, können Sie sich wcalc (einen einfachen Konsolenrechner) ansehen. Mit den Optionen -b erhalten Sie eine binäre Ausgabe.

z.B

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

1
es gibt zu wenig andere Optionen an dieser Front, ... ruby -e 'printf("%b\n", 0xabc)', dcgefolgt von 2ogefolgt von 0x123p, und so weiter.
Lindes

6

In der C-Standardbibliothek gibt es keine Formatierungsfunktion, um solche Binärdateien auszugeben. Alle Formatierungsvorgänge, die von der printf-Familie unterstützt werden, beziehen sich auf lesbaren Text.


5

Die folgende rekursive Funktion kann nützlich sein:

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

7
Seien Sie vorsichtig, dies funktioniert nicht mit negativen ganzen Zahlen.
Anderson Freitas

4

Ich habe die Top-Lösung für Größe und C ++ optimiert und bin zu dieser Lösung gekommen:

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
Wenn Sie dynamischen Speicher (durch std::string) verwenden möchten , können Sie das staticArray auch entfernen. Am einfachsten wäre es, einfach das staticQualifikationsmerkmal zu löschen und bdie Funktion lokal zu machen .
Shahbaz

((x>>z) & 0x01) + '0'ist ausreichend.
Jason C

4

Drucken Sie Bits von jedem Typ mit weniger Code und Ressourcen

Dieser Ansatz hat als Attribute:

  • Arbeitet mit Variablen und Literalen.
  • Iteriert nicht alle Bits, wenn dies nicht erforderlich ist.
  • Rufen Sie printf nur auf, wenn ein Byte vollständig ist (nicht unnötig für alle Bits).
  • Funktioniert für jeden Typ.
  • Funktioniert mit kleinen und großen Endianness (verwendet GCC #defines zur Überprüfung).
  • Verwendet typeof (), das nicht dem C-Standard entspricht, aber weitgehend definiert ist.
#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;
}

Ausgabe

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

Ich habe einen anderen Ansatz ( bitprint.h ) verwendet, um eine Tabelle mit allen Bytes (als Bitfolgen) zu füllen und sie basierend auf dem Eingabe- / Indexbyte zu drucken. Ein Blick lohnt sich.


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

}

Oder addiere 0 oder 1 zum Zeichenwert von '0';) Kein Ternär erforderlich.
Eule

3

Ich mochte den Code von paniq, der statische Puffer ist eine gute Idee. Es schlägt jedoch fehl, wenn Sie mehrere Binärformate in einem einzigen printf () möchten, da immer derselbe Zeiger zurückgegeben und das Array überschrieben wird.

Hier ist ein Drop-In im C-Stil, das den Zeiger auf einen geteilten Puffer dreht.

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
Sobald counterreicht MAXCNT - 1, würde das nächste Inkrement von countes MAXCNTanstelle von Null machen, was einen Zugriff außerhalb der Grenzen des Arrays verursachen würde. Du hättest es tun sollen count = (count + 1) % MAXCNT.
Shahbaz

1
Übrigens würde dies später einen Entwickler überraschen, der MAXCNT + 1Aufrufe dieser Funktion in einem einzigen verwendet printf. Wenn Sie die Option für mehr als eine Sache angeben möchten, machen Sie sie im Allgemeinen unendlich. Zahlen wie 4 können nur Probleme verursachen.
Shahbaz

3

Kein Standard und tragbarer Weg.

Einige Implementierungen bieten itoa () , aber es wird nicht in den meisten sein, und es hat eine etwas miese Schnittstelle. Der Code befindet sich jedoch hinter dem Link und sollte es Ihnen ermöglichen, Ihren eigenen Formatierer ziemlich einfach zu implementieren.


3

Eine generische Konvertierung eines beliebigen Integraltyps in die binäre Zeichenfolgendarstellung unter Verwendung der Standardbibliothek :

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

Oder nur: std::cout << std::bitset<sizeof(num) * 8>(num);


1
Das ist eine idiomatische Lösung für C ++, aber er fragte nach C.
Danijar

3

Meine Lösung:

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

Basierend auf @ ideasman42 Vorschlag in seiner Antwort, das ist ein Makro , das bietet int8, 16, 32& 64Versionen, die Wiederverwendung den INT8Makro um Wiederholungen zu vermeiden.

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

Dies gibt aus:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Zur besseren Lesbarkeit können Sie Folgendes ändern: #define PRINTF_BINARY_SEPARATORzu #define PRINTF_BINARY_SEPARATOR ","oder#define PRINTF_BINARY_SEPARATOR " "

Dies wird Folgendes ausgeben:

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

oder

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

danke, dass du diesen Code kopiert
hast


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

sollte funktionieren - ungetestet.


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
Das Bezahlen der Kosten für eine Mallocation beeinträchtigt die Leistung. Es ist unfreundlich, die Verantwortung für die Zerstörung des Puffers an den Anrufer weiterzugeben.
EvilTeach

2

Als nächstes wird Ihnen das Speicherlayout angezeigt:

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

Was meinst du mit "Weiter wird dir das Speicherlayout zeigen" ?
Peter Mortensen

2

Hier ist eine kleine Variation der paniq -Lösung, die Vorlagen verwendet, um das Drucken von 32- und 64-Bit-Ganzzahlen zu ermöglichen:

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

Und kann verwendet werden wie:

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

Hier ist das Ergebnis:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.