Bits schieben

Gast #1859082
Lesenswert?

Ich wollte die (invertierten) Portleitungen D7~4 abfragen.
Ergebnis sollte eine Zahl von 0 bis 15 sein.
1
unsigned char n;
2
n = (~PIND) >> 4;
Zuerst wird doch PIND invertiert.
Dann wird das Ergebniss viermal rechtsgeschoben.
Da n unsigned char ist, sollten doch links nullen nachgeschoben werden?!
Leider klappt das so nicht.
1
unsigned char n;
2
n = ((~PIND) >> 4) & 0b00001111;
Aber so klappt es. Warum ist die maskierung mit 0b00001111 nötig?
Moderator Persönliche Seite #1859121
Lesenswert?

Heiopei schrieb:

> Da n unsigned char ist, sollten doch links nullen nachgeschoben werden?!

Diese Begründung ist nicht richtig. Mit n hat das nix zu tun, da n gar 
nicht geschoben wird.

Jetzt fragt sich, ob der Kompiler aus den 8Bit bei der Komplementierung 
von PIND einen Integer macht. Der wäre auf dem AVR 16 bit breit. Dann 
wird bei

   PIND = 0b01010101

das Ergebnis von ~PIND auf 16 Bit aufgebohrt, also:

   ~PIND : 0b1111111110101010

Wenn man das nach rechts schiebt um 4 Stellen, dann wird daraus:

   (~PIND) >> 4: 0b1111111111111010

Oben schieben sich bei negativen Zahlen (2er Komplement) 1en rein[1], 
spielt hier aber keine Rolle, denn es werden nur die untersten 8 Bit im 
n gespeichert. Dann sieht n so aus:

   n: 0b11111010

Wohlgemerkt, alles nur, wenn der Compiler das Ergebnis von ~PIND auf 16 
Bit (= Integer auf z.B. ATmega8) aufbohrt. Um welchen Compiler/Prozessor 
handelt es sich denn?

Gruß,

Frank

[1] Vielleicht wird aus ~PIND auch ein unsigned int, dann kommen halt 4 
0en von oben rein. Nützt Dir aber auch nix. Du hast immer noch 4 Einsen 
vor Deinem Wert.
Moderator Persönliche Seite #1859127
Lesenswert?

Klaus Wachtler schrieb:
> Überrascht mich jetzt spontan auch.
> Offenbar ist ~PIND erstmal wieder signed.

Ob signed oder nicht, ist egal, ~PIND es ist halt ein Integer mit 16 
Bit. Kann auch unsigned int sein, Du hast trotzdem 8 Einsen im 
höherwertigen Byte. Die kriegst Du durch Shiften um 4 Stellen auch nicht 
weg ;-)

> Die Lösung ist:
>
1
> n = ( (unsigned char /* bzw. uint8_t */)(~PIND) >> 4) & 0b00001111;
2
>

Jepp. Hier verkleinerst Du (~PIND) wieder auf 8 Bit. Damit ist wieder 
alles im Lot und das "& 0b00001111" kann man sich wieder sparen.

Gruß,

Frank
Gast #1859166
Lesenswert?

vielen Dank für die rege Anteilnahme

Frank M. schrieb:
> Um welchen Compiler/Prozessor handelt es sich denn?

Der Compiler ist der GCC vom AVR Studio 4.
Der Proz ist der ATmega8.

In der Datei IOM8.h steht:
#define PIND  _SFR_IO8(0x10)

Nun ist mir immer noch nicht klar, wie der Compiler darauf kommt,
das Ergebnis von ~PIND auf 16 Bit aufgebohren.
Klar, das maskieren mit 0b00001111 oder casting auf unsigned char hilft, 
ist aber irgendwie nur ein kurieren der Sympthome...   ;-)
#1859177
Lesenswert?

Stichwort: Integer promotion.  Die gilt auch für den ~-Operator.  Sie 
besagt (vereinfacht), dass alle Berechnungen mindestens in int 
durchzuführen sind und für kleinere Typen vor der Anwendung des 
Operators zunächst die Variablen nach int aufgeblasen werden müssen.

Der Compiler berechnet also zu Recht das Komplement von 0x00n0 anstelle 
von 0xn0.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren