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?
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.
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.
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.
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.
@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.
"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.
"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.
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!
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.