Forum: Compiler & IDEs Schieben mit unsigned char?


von Manfred S. (Gast)


Lesenswert?

Hallo,
im ATMega8 beschäftige ich mich gerade mit dem USART. Da ist folgender
Beispielcode auf S. 145 angegeben:

unsigned int USART_Receive(void)
{
unsigned char status, resh, resl;
... bla
/*Filter the ninth bit, then return*/
resh = (resh >> 1) & 0x01;
return ((resh << 8) | resl);
}

1.) In der vorletzten Zeile wäre es doch auch Ressourcenschonender ohne
schieben gegangen, wenn man
resh = resh & 0x02;
geschrieben hätte?
2.) resh ist als unsigned char (8 Bit) deklariert. Wenn da jetzt ein 8
Bit Shift kommt, stehen doch nur noch Nullen da oder macht der Compiler
eine automatische Erweiterung auf 16 Bit, da der Returntyp 16 Bit ist?

von johnny.m (Gast)


Lesenswert?

Zu 1.:
Nein, denn das Bit muss ja auf das LSB verschoben werden, deshalb ist
die Schiebeoperation notwendig. Das neunte Bit steht nach dem Empfang
an der Stelle '1' im UCSRB, das in resh eingelesen wird. Um das
Datenwort korrekt zusammenzusetzen, wird es an die Stelle '0'
geschoben und maskiert, so dass der Rest von UCSRB wegfällt.

Zu 2.:
Im Prinzip ja. Wenn man sichergehen will, kann man
return ((unsigned int)((resh << 8) | resl));
schreiben.

von johnny.m (Gast)


Lesenswert?

Ergänzung: Man könnte das Schieben um eins nach rechts wegoptimieren,
wenn man schreibt:

resh &= 0x02;
return ((resh << 7) | resl);

Wenn ich nix übersehen habe, müsste das so funktionieren. Allerdings
ist in der Originalvariante besser erkennbar, was da genau gemacht
wird.

von Manfred S. (Gast)


Lesenswert?

Danke, klingt logisch. Ich muß das 9. Bit ja eben an diese 9. Stelle des
16 Bit-Wertes bringen. Sonst würde es ja an 10. Stelle stehen.

von A.K. (Gast)


Lesenswert?

Zu 2: Jeder korrekte C Compiler hat folgende Eigenschaften:
- "int" ist mindestens 16bit gross.
- jede Rechnung von Daten kleiner als "int" erfolgt als "int".
Wobei das natürlich vom Ergebnis her zu sehen ist, wenn das gleiche
rauskommt darf er auch kleiner rechnen. Hier aber nicht.

Der Return-Typ hingegen ist für die Rechnung nicht relevant: so ist
  return 1 << 8
gleich 256
  return 1 << 16
hingegen 0, auch wenn die Funktion als "long" deklariert ist.

Bei 8-Bit Microcontrollern sollte man freilich prüfen ober er
tatäschlich Standardkonform ist. Würd' mich nicht wirklich wundern,
wenn manche da Kompromisse eingehen.

von johnny.m (Gast)


Lesenswert?

@A.K.:
Afaik gilt das mit den Integer-Berechnungen aber nur für den
Präprozessor. Und die Operationen in dem return-Ausdruck im
Code-Beispiel aus dem Datenblatt können nicht zur Compiler-Laufzeit
(also vom Präprozessor) durchgeführt werden, da sie Variablen
enthalten, deren Werte zur Compile-Zeit nicht bekannt sind. Alle
anderen Operationen werden mit der für die verwendeten Größen
erforderlichen Datenbreite durchgeführt, eben auch in diesem Fall. Dein
Beispiel bezieht sich nur auf konstante Werte, die bereits zur
Compiler-Laufzeit bekannt sind. Und da trifft es tatsächlich zu, dass
diese Operationen standardmäßig in int durchgeführt werden. Will man
mehr, dann muss man dem Präprozessor das mitteilen. Und wenn die
Funktion einen Rückgabewert vom Typ long besitzt, dann müsste es z.B.
mit 'return (1 << 16)UL;' gehen.

von A.K. (Gast)


Lesenswert?

"Afaik gilt das mit den Integer-Berechnungen aber nur für den
Präprozessor."

Nö.

Ausserdem rechnet der Präprozessor ausschliesslich in seinen eigenen
Statements (#if 1+2 == 2), nie im C-Code (return 1+2).

Du meinst wahrscheinlich den Teil des Compilers, der konstante
Berechnungen selber ausführt und durch das Resultat ersetzt. Das ist
nicht der Präprozessor, sondern eine optionale Komponente des
Compilers. Und für den gilt was für alle Optimierungen gilt: er muss
genau so arbeiten als wenn es ihn nicht gäbe (im Rahmen des C-Standards
allerdings, es darf etwas anderes rauskommen, wenn das Ergebnis
undefiniert ist).

"'return (1 << 16)UL;'"

Was soll das denn für C sein?

Der Typ links einer Zuweisung oder des Resultats einer Funktion ändert
garantiert nie etwas an der Rechnung selber. Erst das Ergebnis der
Rechnung wird dann ggf. umgewandelt.

von A.K. (Gast)


Lesenswert?

"Alle anderen Operationen werden mit der für die verwendeten Größen
erforderlichen Datenbreite durchgeführt, eben auch in diesem Fall."

Nein

C89: "A char , a short int [...] may be used in an expression wherever
an int or unsigned int may be used.  If an int can represent all values
of the original type, the value is converted to an int ; otherwise it
is converted to an unsigned int ."

D.h.: Mit Operanden kleiner als "int" muss so gerechnet werden, als
würde die Rechnung mit "int" ausgeführt.

von johnny.m (Gast)


Lesenswert?

Richtig, aber bei für 8-Bit-µCs angepassten Compilern werden anscheinend
tatsächlich einige Änderungen gegenüber dem ANSI-Standard gemacht. Wie
das jetzt im Compiler intern aussieht, weiß ich nicht. Möglicherweise
wird da intern tatsächlich (nach ANSI-Standard) mit int gerechnet, aber
wenn 8 Bit reichen, dann kommt (in Assembler) auch eine 8-Bit-Operation
raus!

von Rolf Magnus (Gast)


Lesenswert?

Solange es im Ergebnis keinen Unterschied macht, darf der Compiler
beliebige Optimierungen durchführen. Das ist bekannt als "as-if
rule". Es ist egal, was der Compiler tatsächlich macht, solange es für
ein konformes Programm so aussieht, als hätte er es so gemacht, wie in
der C-Norm steht.

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.