İkili biçimde yazdırmak için bir printf dönüştürücü var mı?


434

Printf ile onaltılık veya sekizli sayı olarak yazdırabilirim. İkili veya rastgele temel olarak yazdırılacak bir biçim etiketi var mı?

Gcc çalıştırıyorum.

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

Bunu bildiğim kadarıyla printf kullanarak yapamazsınız. Açıkçası, bunu gerçekleştirmek için yardımcı bir yöntem yazabilirsiniz, ancak bu gitmek istediğiniz yöne benzemiyor.
Ian P

Bunun için önceden tanımlanmış bir biçim yok. Kendinizi bir dizeye dönüştürmeniz ve sonra dizeyi yazdırmanız gerekir.
rslite

Hızlı bir Google araması, bu sayfayı yararlı olabilecek bazı bilgilerle üretti: forums.macrumors.com/archive/index.php/t-165959.html
Ian P

12
ANSI Standart C Kütüphanesinin bir parçası olarak değil - taşınabilir kod yazıyorsanız, en güvenli yöntem kendinizinkini döndürmektir.
tomlogic

C ++ 'da ikili dizeye dönüşümün standart ve genel (herhangi bir uzunlukta herhangi bir İntegral türü için) çözümü: stackoverflow.com/a/31660310/1814353
luart

Yanıtlar:


266

Hacky ama benim için çalışıyor:

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

Çok baytlı tipler için

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

Ne yazık ki tüm ekstra tırnak gerekir. Bu yaklaşım, makroların verimlilik risklerine sahiptir (argüman olarak bir işlevi geçmeyin BYTE_TO_BINARY), ancak buradaki diğer bazı tekliflerde bellek sorunlarından ve çoklu strcat çağrılarından kaçınır.


13
Ayrıca, tamponlu printfolanların yapamayacağı birçok kez çağrılma avantajı da vardır static.
Patrick Schlüter

4
Değişmeye özgürlük geçtiniz %diçin %cbile daha hızlı olması gerektiği, çünkü ( %dederken, digit-> karakter dönüşümü gerçekleştirmek zorundadır %cbasitçe argüman verir

3
16, 32, 64 bit int desteği ile bu makronun genişletilmiş bir versiyonunu
yayınladı

2
Bu yaklaşımın yığın dostu olmadığını unutmayın. intSistemde 32 bit olduğu varsayıldığında , tek bir 32 bit değerinin yazdırılması 32 * 4 bayt değerleri için alan gerektirir; toplam 128 bayt. Yığın boyutuna bağlı olarak sorun olabilir veya olmayabilir.
user694733

1
makroda bayt çevresine parantez eklemek önemlidir veya bir işlem gönderirken sorun yaşayabilirsiniz BYTE_TO_BINARY (a | b) -> a | b & 0x01! = (a | b) & 0x01
Ivan Hoffmann

203

Herhangi Bir Veri Türü İçin İkili Yazdırma

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

Ölçek

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
Öner size_t i; for (i=size; i-- > 0; )önlemek için size_tvs intyanlış eşleme.
chux - Monica

1
Birisi lütfen bu kodun arkasındaki mantık üzerinde durulabilir mi?
jII

2
Her baytı alın ptr(dış döngü); ardından her bit için geçerli bayt (iç döngü), baytı geçerli bit ( 1 << j) ile maskeler . Bu sağa kaydırarak 0 ( 0000 0000b) veya 1 ( 0000 0001b) içeren bir bayt elde edilir . Formatı ile sonuçlanan bayt printf yazdır %u. HTH.
nielsbot

1
ZX9 Bildirimi @ olduğunu öne kod kullanılmış >olan size_tve olmayan >=döngü sonlandırmak için zaman yorumunuzun belirlemek için.
chux - Monica'yı eski durumuna döndür

3
@ ZX9 Kodlayıcılar olarak hala yararlı bir orijinal yorumunuz, işaretsiz türlerin >ve son durum kullanımının dikkate alınmasına dikkat edilmelidir >=. 0imzasız bir kenar durumu ve daha az yaygın olan imzalı matematik aksine, yaygın olarak ortaya çıkar INT_MAX/INT_MIN.
chux - Monica'yı eski durumuna döndür

151

İşte istediğinizi yapmak için teknikleri göstermek için hızlı bir kesmek.

#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
Bu kesinlikle printf için bir kaçış yüklemesi yazmaktan daha az "tuhaf". Kodda yeni bir geliştirici için de anlaşılması kolaydır.
Öfkeli Kodlayıcı

43
Birkaç değişiklik: strcatdöngünün her geçişinde dizeye tek bir karakter eklemek için verimsiz bir yöntemdir. Bunun yerine, a ekleyin char *p = b;ve iç halkayı ile değiştirin *p++ = (x & z) ? '1' : '0'. z256 (2 ^ 8) yerine 128 (2 ^ 7) ile başlamalıdır. Kullanılacak ara belleğe (iş parçacığı güvenliği için) benzer bir işaretçi almak için güncellemeyi düşünün inet_ntoa().
tomlogic

3
@EvilTeach: Üçlü bir operatörü parametre olarak kendiniz kullanıyorsunuz strcat()! strcatMuhtemelen anlaşılması için ödevin kaydı kaldırılmış bir işaretçiyi arttırmadan daha kolay olduğunu kabul ediyorum , ancak yeni başlayanların bile standart kütüphaneyi nasıl kullanacaklarını bilmeleri gerekiyor. Belki de atama için dizine alınmış bir dizi kullanmak iyi bir gösterim olurdu (ve bişlevi her çağırdığınızda tüm sıfırlara sıfırlanmadığından , aslında işe yarayacaktır ).
tomlogic

3
Rastgele: İkili arabellek karakteri statiktir ve atamadaki tüm sıfırlara temizlenir. Bu, yalnızca ilk çalıştırıldığında temizlenecek ve bundan sonra net olmayacak, bunun yerine son değeri kullanacak.
markwatson

8
Ayrıca, bu önceki sonuç yine işlevini çağırarak sonra geçersiz olacağı belgelemek gerekir, bu nedenle arayanlar bu gibi kullanmak çalışmamalıdır: printf("%s + %s = %s", byte_to_binary(3), byte_to_binary(4), byte_to_binary(3+4)).
Paŭlo Ebermann

84

Normal olarak glibc'de ikili bir dönüşüm belirteci yoktur.

Glibc'deki printf () ailesine özel dönüşüm türleri eklemek mümkündür. Ayrıntılar için register_printf_function bölümüne bakın . Uygulama kodunu kullanılabilir hale getirmek için basitleştirirse, kendi kullanımınız için özel bir% b dönüşümü ekleyebilirsiniz.

İşte glibc'de özel bir printf formatlarının nasıl uygulanacağına dair bir örnek .


Her zaman farklı radkss istediğim sınırlı durumlar için kendi v [snf] printf () 'i yazdım: çok mutluyum.
Jamie

3
warning: 'register_printf_function' is deprecated [-Wdeprecated-declarations]Aynı olsa yapmak için yeni bir işlevi yoktur: register_printf_specifier(). Yeni kullanımın bir örneğini burada bulabilirsiniz: codereview.stackexchange.com/q/219994/200418
Cacahuete Frito

47

Hızı artırmak için küçük bir tablo kullanabilirsiniz 1 . Benzer teknikler gömülü dünyada, örneğin bir baytı ters çevirmek için yararlıdır:

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 Çoğunlukla optimize edicilerin çok agresif olmadığı ve hız farkının görüldüğü gömülü uygulamalardan bahsediyorum.


27

En küçük biti yazdırın ve sağdan dışarı kaydırın. Tamsayı sıfır oluncaya kadar bunu yapmak, ikili gösterimleri baştaki sıfırlar olmadan, ancak ters sırada yazdırır. Özyineleme kullanarak, sipariş oldukça kolayca düzeltilebilir.

#include <stdio.h>

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

Bana göre, bu sorunun en temiz çözümlerinden biri. 0bÖnek ve sondaki yeni satır karakterini beğendiyseniz , işlevi sarmanızı öneririz.

Çevrimiçi demo


hata: işlev çağrısı için çok az argüman, beklenen 2, 1 putc ((sayı & 1)? '1': '0');
Koray Tugay

@KorayTugay Bunu işaret ettiğiniz için teşekkürler. İşlev çağrısını düzelttim ve bir demo ekledim.
danijar

6
ayrıca işaretsiz int numarası kullanmalısınız, çünkü verilen sayı negatif olduğunda fonksiyon hiç bitmeyen bir özyinelemeli çağrıya girer.
Kabarık

ASCII'de '0' + 1 = '1' olduğundan daha verimli yaklaşım:putc('0'+(number&1), stdout);
Roger Dueck

22

@William Whyte'ın cevap dayanarak, bu sağlayan bir makro olduğunu int8, 16, 32ve 64yeniden sürümlerini INT8önlemek tekrarına makro.

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

Bu çıktılar:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Okunabilirlik için aşağıdakiler için bir ayırıcı eklemek isteyebilirsiniz:

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

Bu mükemmel. En Az Önemli Bits ile başlayan bitleri yazdırmak için özel bir neden var mı?
gaganso

2
virgül eklemeyi nasıl önerirsiniz?
nmz787

PRINTF_BYTE_TO_BINARY_INT#İsteğe bağlı olarak kullanmak için tanımlanmış gruplanmış bir sürümü ekler .
17:41

16

İşlevin yeniden girme sorunlarından muzdarip olmayan veya bağımsız değişkenin boyutuna / türüne ilişkin sınırlamalar olmayan bir işlev sürümü:

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

Sadece 2'leri istediğiniz tabanla değiştirirseniz, bu kodun 2 ile 10 arasındaki herhangi bir taban için de işe yarayacağını unutmayın. Kullanımı:

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

xHerhangi bir integral ifade nerede .


7
Evet, yapabilirsin . Ama bu gerçekten kötü bir tasarım. İş parçacıkları veya yeniden girintileriniz olmasa bile, arayan statik tamponun yeniden kullanıldığını ve bunun gibi şeylerin char *a = binary_fmt(x), *b = binary_fmt(y);beklendiği gibi çalışmayacağını bilmelidir . Arayıcının bir tamponu geçmeye zorlanması, depolama gereksinimini ortadan kaldırır; arayan, elbette, gerçekten isteniyorsa statik bir tampon kullanmakta serbesttir ve daha sonra aynı tamponun tekrar kullanılması açık hale gelir. Ayrıca, modern PIC ABI'lerde statik arabelleklerin genellikle yığındaki arabelleklerden daha fazla kod maliyeti olduğunu unutmayın.
R .. GitHub BUZA YARDIMCI DURDUR

8
Bu hala kötü bir tasarım. Bu durumlarda fazladan bir kopyalama adımı gerektirir ve kopyalamanın gerekmediği durumlarda bile arayanın arabelleğe sahip olmasından daha ucuz değildir. Statik depolama kullanmak sadece kötü bir deyimdir.
R .. GitHub BUZA YARDIMCI DURDUR

3
Önişlemcinin veya değişken sembol tablosunun ad alanını, her arayan tarafından tahsis edilmesi gereken depolamayı düzgün bir şekilde boyutlandırmak için kullanılması gereken gereksiz bir ek adla kirletmek ve her arayanı bu değeri bilmeye ve gerekli miktarda tahsis etmeye zorlamak depolama, daha basit işlev-yerel depolama çözümü çoğu amaç ve amaç için yeterli olduğunda ve basit bir strdup () çağrısı geri kalan kullanımların% 99'unu kapsadığında kötü bir tasarımdır.
Greg A. Woods

5
Burada katılmamanız gerekecek. Bir göze çarpmayan önişlemci sembolü eklemenin, kullanım durumlarını ciddi şekilde sınırlamanın, arayüzün hataya yatkın hale gelmesinin, program süresince geçici bir depolama için kalıcı depolama alanı ayırmanın ve çoğu zaman daha kötü kod üretmenin zararlılığına yakın bir yere geldiğini göremiyorum. modern platformlar.
R .. GitHub BUZA YARDIMCI DURDUR

5
Mikro optimizasyonu sebepsiz olarak savunmuyorum (yani ölçümler). Ancak, performansın, mikro kazanç ölçeğinde bile, temelde üstün bir tasarımla birlikte bir bonus olarak geldiğinde bahsetmeye değer olduğunu düşünüyorum.
R .. GitHub BUZA YARDIMCI DURDUR

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
Daha net kullanırsanız '1've '0'yerine 49ve 48sizin üçlü içinde. Ayrıca, b9 karakter uzunluğunda olmalıdır , böylece son karakter boş bir sonlandırıcı olarak kalabilir.
tomlogic

Ayrıca B'nin her seferinde başlatılması gerekir.
EvilTeach

2
: Nihai sıfır 1. eklenti alanı: Bazı değişmez eğer static char b[9] = {0}halkanın dışında 2. hamle beyanı: int z,y;3. Final sıfır ekleyin: b[y] = 0. Bu şekilde reinitalizasyona gerek yoktur.
Kobor42

1
Güzel çözüm. Yine de bazı şeyleri değiştirirdim. Herhangi bir boyuttaki girişin düzgün bir şekilde işlenmesi için dizede geriye doğru gidiyorum.
Kobor42

Bunların hepsi 8ile değiştirilmelidir CHAR_BIT.
alk

12

Hızlı ve kolay çözüm:

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

Her boyutta ve imzalı ve imzasız girişlerde çalışır. Vardiya işaret uzantısı yapabileceğinden, imzalanan girişleri işlemek için '& 1' gereklidir.

Bunu yapmanın pek çok yolu var. Burada, imzalı veya imzasız 32 bit türden 32 bit veya n bit yazdırmak (imzalı ise negatif koymamak, sadece gerçek bitleri yazdırmak) ve satırbaşı yok için çok basit bir işlemdir. İ, bit kaydırmasından önce azaltılır:

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

Daha sonra depolamak veya yazdırmak için bitlerle bir dize döndürmeye ne dersiniz? Ya belleği ayırabilir ve geri gönderebilirsiniz ve kullanıcının serbest bırakması gerekir, aksi takdirde statik bir dize döndürürsünüz, ancak yeniden çağrılırsa veya başka bir iş parçacığı tarafından engellenir. Her iki yöntem de gösterilmiştir:

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

İle ara:

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

Daha önce gönderilen cevapların hiçbiri tam olarak aradığım şey değildi, bu yüzden bir tane yazdım. % B ile 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
50 bitten fazla taşacak mı?
Janus Troelsen

İyi çağrı, evet olacak ... Malloc kullanmam gerektiği söylendi, hiç yapmadı mı?
TechplexEngineer

Evet tabi ki. süper kolay:char* buffer = (char*) malloc(sizeof(char) * 50);
Janus Troelsen

@JanusTroelsen veya çok daha temiz, daha küçük, bakım yapılabilir:char *buffer = malloc(sizeof(*buffer) * 50);
Shahbaz

"% B" neden bu açıdan "% b" den farklı olabilir? Önceki yanıtlar, "C standart kitaplığında böyle ikili çıktı verecek biçimlendirme işlevi yoktur." ve " Bazı çalışma zamanları standart olmasa da"% b "yi destekler." .
Peter Mortensen


7

Bu kod 64 bite kadar ihtiyaçlarınızı karşılamalıdır. 2 fonksiyon pBin & pBinFill oluşturdum. Her ikisi de aynı şeyi yapar, ancak pBinFill, önde gelen boşlukları fillChar ile doldurur. Sınama işlevi bazı sınama verileri oluşturur, ardından bu işlevi kullanarak yazdırır.



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" log4cxx dosyasından stream.h ile çakışıyor. Lütfen, geleneksel olarak rastgele tanımlayan isimler kullanın :)
Kagali-san

5
mhambra: widthbunun yerine böyle bir genel ad kullanmak için log4cxx komutunu söylemelisiniz !
u0b34a0f6ae

7

İkili biçimde yazdırmak için bir printf dönüştürücü var mı?

printf()Aile sadece doğrudan standart belirteçleri kullanarak tabanı 8, 10 ve 16 baskı yapabiliyor. Kodun özel ihtiyaçlarına göre bir dizeye sayı dönüştüren bir işlev oluşturmanızı öneririz.


Herhangi bir tabanda yazdırmak için [2-36]

Şimdiye kadar olan diğer tüm cevaplar bu sınırlamalardan en az birine sahiptir.

  1. Dönüş arabelleği için statik bellek kullanın. Bu, işlevin bağımsız değişken olarak kaç kez kullanılabileceğini sınırlar printf().

  2. Arama kodunu gerektiren belleği boş göstergelere ayırın.

  3. Arama kodunun açıkça uygun bir arabellek sağlamasını isteyin.

  4. printf()Doğrudan arayın . Bu için yeni işlev mecbur fprintf(), sprintf(), vsprintf()vb

  5. Daha düşük bir tamsayı aralığı kullanın.

Aşağıdakiler yukarıdaki sınırlamalardan hiçbirine sahip değildir . C99 veya daha yenisini ve kullanımını gerektirir "%s". Tampon alanı sağlamak için bileşik bir değişmez değeri kullanır . İçinde birden fazla çağrı ile hiçbir sorun yoktur 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;
}

Çıktı

1020304 5060708
1000000100000001100000100 101000001100000011100001000
100401404
A2F44

Bu çok faydalı. C ++ ile nasıl kullanacağınızı biliyor musunuz? Derlediğimde, "Başlangıç ​​Kodu Açıklama Proje Dosya Satırı Bastırma Durumu Hatası C4576, parantez içine alınmış bir tür ve ardından bir başlatıcı listesi izleyen bir hata oluşturur standart olmayan açık tür dönüşüm sözdizimi merhaba C: \ my_projects \ hello \ hello \ main.cpp 39 "
Sadece bir öğrenci

1
@Justalearner Bu bir C ++ üretir, çünkü eğer C ++ parçası olmayan bir C özelliği bileşik değişmezini kullanır . Belki de aynı şeyi yapmaya çalışan C ++ uygulamanızı gönderin - eksik olsa bile, ilk denemenizi gösterdiğiniz sürece yardım alacağınızdan eminim.
chux - Monica'yı geri yükle

6

Belki biraz OT, ancak yaptığınız bazı ikili işlemleri anlamak veya izlemek için hata ayıklamak için buna ihtiyacınız varsa, wcalc'e (basit bir konsol hesap makinesi) göz atabilirsiniz. -B seçenekleri ile ikili çıktı elde edersiniz.

Örneğin

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

1
bu cephede başka birkaç seçenek daha var ... ruby -e 'printf("%b\n", 0xabc)'ve dconu 2otakip ediyor 0x123p, vb.
Lindes

6

C standart kütüphanesinde böyle ikili çıktı verecek formatlama fonksiyonu yoktur. Printf ailesinin desteklediği tüm format işlemleri insan tarafından okunabilir metne yöneliktir.


5

Aşağıdaki özyinelemeli işlev yararlı olabilir:

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

7
Dikkatli olun, bu negatif tamsayılarla çalışmaz.
Anderson Freitas

4

Boyut ve C ++ için en iyi çözümü optimize ettim ve bu çözüme ulaştım:

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
Dinamik belleği (aracılığıyla std::string) kullanmak istiyorsanız , staticdiziden de kurtulabilirsiniz . En basit yol, staticniteleyiciyi bbırakmak ve işleve yerel yapmaktır .
Shahbaz

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

4

Daha az kod ve kaynak kullanarak her türden bit yazdırın

Bu yaklaşımın nitelikleri vardır:

  • Değişkenler ve değişmez değerler ile çalışır.
  • Gerekli olmadığında tüm bitleri yinelemez.
  • Printf'yi yalnızca bir bayt tamamlandığında çağırın (tüm bitler için gereksiz değildir).
  • Her tür için çalışır.
  • Küçük ve büyük endianness ile çalışır (kontrol için GCC #defines kullanır).
  • C standardı olmayan ancak büyük ölçüde tanımlanmış typeof () yöntemini kullanır.
#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;
}

Çıktı

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

Kullandım başka bir yaklaşım ( bitprint.h (bit dizeleri gibi) tüm bayt içeren bir tablo doldurmak ve giriş / index byte dayalı bunları yazdırmak için). Bir göz atmaya değer.


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

}

Veya '0' karakter değerine 0 veya 1 ekleyin;) Üçlü gerekmez.
Baykuş

3

Paniq tarafından kodu sevdim, statik tampon iyi bir fikir. Ancak, her zaman aynı işaretçiyi döndürdüğü ve dizinin üzerine yazdığı için tek bir printf () içinde birden çok ikili biçim istiyorsanız, başarısız olur.

Burada, bölünmüş bir arabellekteki işaretçiyi döndüren bir C stili açılır listesi var.

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
Bir kez countulaşıldığında MAXCNT - 1, bir sonraki artışı sıfır yerine countbunu yapar ve bu MAXCNTda dizinin sınırlarının dışına erişilmesine neden olur. Yapmalıydın count = (count + 1) % MAXCNT.
Shahbaz

1
Bu arada, MAXCNT + 1bu işleve çağrıları tek bir şekilde kullanan bir geliştiriciye daha sonra sürpriz olacaktı printf. Genel olarak, 1'den fazla şey için seçenek vermek istiyorsanız, onu sonsuz yapın. 4 gibi sayılar sadece soruna neden olabilir.
Shahbaz

3

Standart ve taşınabilir bir yol yok.

Bazı uygulamalar itoa () sağlar , ancak çoğunda olmayacak ve biraz crummy bir arayüze sahiptir. Ancak kod bağlantının arkasındadır ve kendi biçimlendiricinizi kolayca uygulamanıza izin vermelidir.


3

Herhangi bir integral türünün standart kütüphane kullanılarak ikili dize temsiline dönüştürülmesi için bir deyim genel dönüştürmesi :

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

Ya da sadece: std::cout << std::bitset<sizeof(num) * 8>(num);


1
Bu C ++ için deyimsel bir çözüm ama C
istiyordu

3

Çözümüm:

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

Onun cevabını @ ideasman42 önerisine dayanarak, bu sağlayan bir makro olduğunu int8, 16, 32ve 64yeniden sürümlerini INT8önlemek tekrarına makro.

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

Bu çıktılar:

My Flag 0001011011100001001010110111110101111000100100001111000000101000

Okunabilirliği için, değiştirebilirsiniz: #define PRINTF_BINARY_SEPARATORiçin #define PRINTF_BINARY_SEPARATOR ","veya#define PRINTF_BINARY_SEPARATOR " "

Bu çıktı:

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

veya

My Flag 00010110 11100001 00101011 01111101 01111000 10010000 11110000 00101000

teşekkürler bu kodu kopyalayarak, ilk ben üzerinde çalışıyorum bu projede kopyalamak, bu yazma sıkıcı görev gibi hissettim :)
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);
        }
}

çalışmalı - denenmemiş.


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
Bir mallocation maliyetini ödemek performansa zarar verecektir. Tamponun arayana imha edilmesinden sorumlu olmak kaba değildir.
EvilTeach

2

Sonra size bellek düzenini gösterecektir:

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

"Sonraki size bellek düzenini gösterecek" ile ne demek istiyorsun ?
Peter Mortensen

2

İşte 32 ve 64 bit tamsayıların yazdırılmasına izin vermek için şablonlar kullanan küçük bir paniq çözümü çeşidi :

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

Ve şu şekilde kullanılabilir:

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

İşte sonuç:

  0x1e127ad: 00000001111000010010011110101101
0x2e0b04ce0: 0000000000000000000000000000001011100000101100000100110011100000
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.