Forum: PC-Programmierung Variablen in C als binärwerte eingeben


von Rabofon (Gast)


Lesenswert?

Hallo, wie kann ich denn eine Binärzahl als Variable abspeichern? Hex 
(0x...)oder Dez ist ja kein Problem, aber Binär mag er nicht (hab 
0b...probiert). Kann ja nicht sein, dass ich alles im 
Windowstaschenrechner erst in hex oder dez umwandeln muss...

von Karl H. (kbuchegg)


Lesenswert?

Welcher Compiler?

Es gibt keine standardisierte Schreibweise für Binärzahlen.


> Kann ja nicht sein, dass ich alles im
> Windowstaschenrechner erst in hex oder dez umwandeln muss...

Binär nach Hex ist doch ein Kinderspiel. Genau deswegen ist ja hex so 
beliebt, weil man mit einem Blick das Bitmuster erkennen kann (wenn mans 
kann)

    0  0000       4  0100       8  1000       C  1100
    1  0001       5  0101       9  1001       D  1101
    2  0010       6  0110       A  1010       E  1110
    3  0011       7  0111       B  1011       F  1111

und eine 2 stellige Hexzahl ist dann einfach 2 '4-Bit-Muster' 
hintereinander.


   hab ich also die Hexzahl  5A
dann sind das die Bits 0101 und 1010 oder als 8 Bit Zahl  01011010

Hex Tabelle auswendig lernen!
(Aber ist doch im Grunde simpel: Alle ungeraden Zahlen haben an der 
letzten Position eine 1^
die Zahlen 1, 2, 4, 8, merkt man sich auch leicht, das ist einfach nur 
ein 1 Bit welches durchwandert.
Detto für 3, 7, und F. Alle Bits 1 bis auf die abnehmenden führenden 
0-en
Die Zahlen nach den 2-er Potenzen, also 5 und 9 sind auch simpel. einmal 
0101 und die andere 1001
dezimal 10, oder hex A ist auch leicht: die 10 einfach 2 mal 
hintereinander: 1010
In der Mitte liegt die Kraft: die beiden mittleren Bits 1 ergibt sex.
Und die paar für die man jetzt noch keine Eselsbrücke hat zählt man 
einfach an den Fingern ab.

von Vlad T. (vlad_tepesch)


Lesenswert?

schreib dir doch Makros

1
#define _Bit(b) (((b)==0)?0:1)
2
3
#define BIN8(b7,b6,b5,b4,b3,b2,b1,b0) \
4
            (  (_Bit(b7)<<7)|(_Bit(b6)<<6)|(_Bit(b5)<<5)|(_Bit(b4)<<4) \
5
              |(_Bit(b3)<<3)|(_Bit(b2)<<2)|(_Bit(b1)<<1)|_Bit(b0)  )
6
#define BIN16(b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0)\
7
            (   (BIN8((b15),(b14),(b13),(b12),(b11),(b10),(b9),(b8))<<8) \
8
               | BIN8((b7),(b6),(b5),(b4),(b3),(b2),(b1),(b0))              )

von Peter (Gast)


Lesenswert?

Einige Compiler (z.B. der aktuelle AVR-GCC) verstehen 0b10010110

von Yalu X. (yalu) (Moderator)


Lesenswert?

Vlad Tepesch schrieb:
> schreib dir doch Makros

Dann aber lieber gleich ein solches (Version für 8 Bit):
1
#define B(x) ( \
2
  0##x >>  0 & 0001 | \
3
  0##x >>  2 & 0002 | \
4
  0##x >>  4 & 0004 | \
5
  0##x >>  6 & 0010 | \
6
  0##x >>  8 & 0020 | \
7
  0##x >> 10 & 0040 | \
8
  0##x >> 12 & 0100 | \
9
  0##x >> 14 & 0200 )
10
11
// Anmwendung:
12
unsigned char a = B(110101);  // = 0x35 = 53

von Vlad T. (vlad_tepesch)


Lesenswert?

Yalu X. schrieb:
> Dann aber lieber gleich ein solches (Version für 8 Bit):
> #define B(x) ( \
>   0##x >>  0 & 0001 | \
>   0##x >>  2 & 0002 | \
>   0##x >>  4 & 0004 | \
>   0##x >>  6 & 0010 | \
>   0##x >>  8 & 0020 | \
>   0##x >> 10 & 0040 | \
>   0##x >> 12 & 0100 | \
>   0##x >> 14 & 0200 )
>
> // Anmwendung:
> unsigned char a = B(110101);  // = 0x35 = 53

cool,

wie kommt man auf sowas?
hab ne Weile gebraucht bis ich da durch gestiegen bin.

von Andreas F. (aferber)


Lesenswert?

Vlad Tepesch schrieb:
> wie kommt man auf sowas?

Nach meiner Erfahrung: meistens sind vergleichbare Sachen Dinge, bei 
denen es einen schon 1001mal genervt hat, dass das "nicht schöner geht", 
und dann trifft einen eines Tages aus heiterem Himmel die Erkenntnis wie 
ein Blitz.

Wenn dann passiert mir sowas meist wenn ich von der Arbeit nach Hause 
laufe, aber da gibt es wohl bei jedem einen anderen "Trigger" ;-)

> hab ne Weile gebraucht bis ich da durch gestiegen bin.

Gängige Eigenschaft von sowas: es gibt genau einen einzelnen nicht 
direkt offensichtlichen Kniff an der Lösung. Hat man den einmal, ist der 
Rest plötzlich einfach. Auf den Trick zu kommen ist der "Blitz". Den 
Trick muss man aber eben auch beim Verstehen der Lösung erstmal finden, 
da er gerade nicht offensichtlich ist.

Andreas

von Yalu X. (yalu) (Moderator)


Lesenswert?

Vlad Tepesch schrieb:
> wie kommt man auf sowas?

Der Trick ist schon etwas älter und nicht auf meinem Mist gewachsen. Ich
schätze aber, der Gedankengang des Erfinders lief etwa so ab:

Oje, wieso kann ich in C keine Binärkonstanten schreiben?

Aber C wäre nicht C, wenn man dieses fehlende Feature nicht irgendwie
nachrüsten könnte, vielleicht mit einem Makro.

Ein Makro mit 8 Argumenten, wie von dir gepostet, funktioniert zwar,
aber das Tippen der vielen Kommata ist etwas lästig. Angenehmer wäre es,
wenn man die Ziffern direkt hintereinander schreiben könnte.

Aber in welchem Kontext kann man in C eine Folge von Nullen und Einsen
schreiben, ohne dass der Compiler gleich mit Syntaxfehlermeldungen um
sich schmeißt? Oder anders gefragt: Welche lexikalischen Elemente in C
enthalten solche 0-1-Folgen?

Z.B. ein Identifier wie a11101101. Aber wie soll man einen Variablenna-
men o.ä. in eine Integerzahl umrechnen? So geht's leider nicht.

Eine Stringkonstante wie "11101101" ist da schon besser geeignet. Da
kann man ja über den Index auf die einzelnen Zeichen zugreifen und aus
diesen dann die Integerzahl berechnen, also etwas so:
1
#define B(x) ( \
2
  (x[0] == '1') << 7 | \
3
  (x[1] == '1') << 6 | \
4
  (x[2] == '1') << 5 | \
5
  (x[3] == '1') << 4 | \
6
  (x[4] == '1') << 3 | \
7
  (x[5] == '1') << 2 | \
8
  (x[6] == '1') << 1 | \
9
  (x[7] == '1') << 0 )
10
11
  printf("%d\n", B("11101101"));

Super, funktioniert!

Allerdings muss der String immer exakt 8 Ziffern enthalten, führende
Nullen können nicht einfach weggelassen werden. Sonst gibt's Probleme
beim Arrayzugriff. So etwas geht also nicht:
1
  printf("%d\n", B("1101"));

Probieren wir auch mal
1
  unsigned char a = B("11101101");

Mist, der Compiler meckert, weil der Ausdruck im Makro aus C-Sicht nicht
konstant ist und deswegen nicht als Initialisierer verwendet werden
darf :-/

Mit den Strings geht es also doch nicht. Was gibt es außer Identifiern
und Stringkonstanten noch für Möglichkeiten, eine Folge von Binärziffern
zu schreiben?

Ja, natürlich! Ich schreibe einfach eine ganz normale Zahl, extrahiere
per Modulofunktion die einzelnen Ziffern und setze diese neu zusammen:
1
#define B(x) ( \
2
  x /        1 % 10 << 0 | \
3
  x /       10 % 10 << 1 | \
4
  x /      100 % 10 << 2 | \
5
  x /     1000 % 10 << 3 | \
6
  x /    10000 % 10 << 4 | \
7
  x /   100000 % 10 << 5 | \
8
  x /  1000000 % 10 << 6 | \
9
  x / 10000000 % 10 << 7 )
10
11
  printf("%d\n", B(11101101));
12
  printf("%d\n", B(1101));
13
14
  unsigned char a = B(11101101);
15
  unsigned char b = B(1101);

Perfekt, alle Beispiele funktionieren, und man erspart sich zusätzlich
das Tippen der Anführungszeichen.

Manchmal möchte man aber trotzdem die führenden Nullen mit angeben:
1
  unsigned char c = B(00001101);

Huch, wieso steht jetzt in c eine 31 statt einer 13? Grübel, grübel ...
Ja klar, eine Zahl mit einer führenden Null wird in C als Oktalzahl
interpretiert. Damit kann natürlich die Modulo-10-Rechnung nicht mehr
funktionieren.

Also rechnen wir eben statt im Dezimal- einfach im Oktalsystem. Dazu
muss nur bei alle Operanden der Divisions- und Modulooperationen eine 0
vorangestellt werden, um sie als Oktalzahlen zu kennzeichnen. Das gilt
auch für x, da ja nicht sichergestellt ist, dass das Makroargument im
Aufruf bereits eine führende 0 enthält. Zum Glück gibt es in C den
##-Operator mit dem genau solche Dinge bewerkstelligt werden können.
Damit sieht das Makro jetzt so aus:
1
#define B(x) ( \
2
  0##x /        01 % 010 << 0 | \
3
  0##x /       010 % 010 << 1 | \
4
  0##x /      0100 % 010 << 2 | \
5
  0##x /     01000 % 010 << 3 | \
6
  0##x /    010000 % 010 << 4 | \
7
  0##x /   0100000 % 010 << 5 | \
8
  0##x /  01000000 % 010 << 6 | \
9
  0##x / 010000000 % 010 << 7 )
10
11
  printf("%d\n", B(11101101));
12
  printf("%d\n", B(1101));
13
14
  unsigned char a = B(11101101);
15
  unsigned char b = B(1101);
16
  unsigned char c = B(00001101);

Und es funktioniert tatsächlich in allen Fällen :)

Die Variante in meinem letzten Beitrag ersetzt die Division und die
Modulofunktion durch Bit-Operationen und wird dadurch etwas kompakter.

Statt im Oktal- könnte man genauso gut auch im Hexadezimalsystem rechnen
und würde vor die Konstanten statt einer '0' einfach ein '0x' stellen.

So oder so ähnlich ist das Makro vielleicht entstanden. Der Gedankengang
ist eigentlich so geradlinig, dass man auch selber darauf kommen hätte
können, oder?

Um einen solchen Gedankengang selber zu generieren, ist es allerdings
von Vorteil, wenn man sämtliche Features, die einem C so anbietet (in
diesem Fall Makros, Oktalkonstanten, ##-Operator usw.), abrufbereit im
Kopf hat. Leute die schon so lange C programmieren, dass dies der Fall
ist, benutzen aber meist keine Binärschreibweise, da sie mit Hexadezi-
mal- oder Oktalkonstanten besser umgehen können. So sehen sie auf einen
Blick, dass das Bit 4 in 0xd4 gesetzt ist, während sie bei 11010100 erst
in Gedanken die Ziffern abzählen müssten, um Bit 4 zu finden. Bei 16-
und spätestens bei 32-Bit-Zahlen sind Binärkonstanten einfach nur noch
unhandlich.

von Rolf Magnus (Gast)


Lesenswert?

Yalu X. schrieb:
> Leute die schon so lange C programmieren, dass dies der Fall
> ist, benutzen aber meist keine Binärschreibweise, da sie mit Hexadezi-
> mal- oder Oktalkonstanten besser umgehen können.

Also eine Oktahlzahl habe ich ehrlich gesagt noch nie gebraucht. Außer 
der POSIX-Funktion chmod() wüßte ich auch nichts, wo ich sie jemals 
brauchen könnte.

von Andreas F. (aferber)


Lesenswert?

Rolf Magnus schrieb:
> Also eine Oktahlzahl habe ich ehrlich gesagt noch nie gebraucht. Außer
> der POSIX-Funktion chmod() wüßte ich auch nichts, wo ich sie jemals
> brauchen könnte.

Die Schreibweise mit führender Null sollte man aber immer im Hinterkopf 
haben, das fällt einem sonst irgendwann unerwartet auf den Fuss.

Konkreten Fall gehabt: Kunde schreibt IP-Adressen mit führender Null 
(also jedes Oktett dreistellig), Software (inet_aton()) interpretiert 
das als Oktal -> Boom.

Andreas

von D. I. (Gast)


Lesenswert?

Ja das mit der führenden 0 ist ein Teufelskonstrukt. Man fragt sich 
wirklich warum man nicht von Anfang an gleich ein 0x, 0o und ein 0b 
eingeführt hat.

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

Rolf Magnus schrieb:
> Also eine Oktahlzahl habe ich ehrlich gesagt noch nie gebraucht.

Dann bist du noch zu jung ;-)

Frühere Computer (bis in die 70er Jahre) hatten meist eine durch 3 teil-
bare Wortbreite, bspw. 12 (PDP-8), 18 (PDP-1), 36 (PDP-4) oder 60 Bit
(CDC 6600). Die Adressbreite der CDC 6600 war 18 Bit, die Befehlsbreite
15 oder 30 Bit, die Peripherieprozessoren arbeiteten mit 12 Bits, ein
Byte hatte ebenfalls 12 Bits und ein Zeichen 6 Bits.

Da war es ganz natürlich, Speicherinhalte im Oktalsystem zu schreiben,
genauso wie es heute, im Zeitalter von 8-, 16-, 32- und 64-Bit-Prozes-
soren, natürlich ist, das Hexadezimalsystem zu benutzen.

Aber auch bei 16-/32-Bit-Prozessoren wie dem MC68000, dessen Nachfolger
heute noch eingesetzt werden, spielt das Oktalsystem noch eine Rolle,
weil man damit Maschinencodes wegen ihrer Unterteilbarkeit in 3-Bit-
Gruppen relativ leicht im Kopf kodieren und dekodieren kann (s. Anhang).

D. I. schrieb:
> Man fragt sich wirklich warum man nicht von Anfang an gleich ein 0x,
> 0o und ein 0b eingeführt hat.

Die Entwicklung von Unix, in dessen Umfeld auch die Programmiersprache C
entstand, begann auf einer PDP-7 bit 18 Bit Wortbreite. Obwohl C für
einen moderneren 16-Bit-Computer (PDP-11) entwickelt wurde, war die
Oktalschreibweise noch fest in den Köpfen der Entwickler verankert, so
dass es in C zunächst nur Dezimal- und Oktalzahlen gab.

Aus dem 31 Seiten dünnen "C Reference Manual" von Dennis M. Ritchie:
1
  2.3.1 Integer constants
2
3
  An integer constant is a sequence of digits. An integer is taken to be
4
  octal if it begins with 0, decimal otherwise. The digits 8 and 9 have
5
  octal value 10 and 11 respectively.

Hexadezimalzahlen und -zeichen und auch der '%x'-Formatspecifier für
printf kamen erst später. Ein einzelnes Zeichen als Präfix für Hexzahlen
war aber nun nicht mehr möglich, so dass man sich für einen Präfix aus
zwei Zeichen ('0x') entschied. Entsprechendes geschah bei den Zeichen-
konstanten ('\x'). Wären die Oktal- und Hexkonstanten gleichzeitig
entstanden, hätten erstere ziemlich sicher gemäß deinem Vorschlag den
'0o'-Präfix.

Wie vieles, ist eben auch C langsam über die Zeit gewachsen, wobei —
wenn irgendwie möglich und sinnvoll — versucht wurde, die Kompatibilität
zu älteren Versionen zu wahren. Leider wurden die 8 und 9 als Oktalzif-
fern irgendwann abgeschafft, so dass ich meinen gesamten Codebestand von
damals nicht mehr nutzen kann ;-)

von Andreas F. (aferber)


Lesenswert?

Yalu X. schrieb:
> Wären die Oktal- und Hexkonstanten gleichzeitig
> entstanden, hätten erstere ziemlich sicher gemäß deinem Vorschlag den
> '0o'-Präfix.

Oder einen ganz anderen. Meine Vermutung dazu, warum es ausgerechnet die 
'0' ist, ist dass sie einerseits eine Ziffer ist (leicht von Text zu 
unterscheiden), andererseits aber wie ein 'O' aussieht. '0x' für Hex 
entstand dann als Erweiterung daraus.

Andreas

von Klaus W. (mfgkw)


Lesenswert?

Ich werfe auch noch einen Aspekt dazu:
Hexadezimal macht Sinn auf 8-Bit-Rechnern (2 Hex-Stellen geben
gerade ein Byte) bzw. Vielfachen davon.
Das war damals aber noch gar nicht so ausgegoren; genauso
"normal" erschienen 9-Bit-Systeme (und für die Zukunft vielleicht
Vielfache davon, also Wortlängen 18-27-48...).
Dann wäre Oktal praktischer, weil da drei Stellen wieder glatt
in 9 Bit passen.
Ich könnte mir denken, daß man damals oktalen Darstellungen
den Vorzug gab, weil man vielleicht eher an 9 als an 8 Bit dachte.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.