Hallo zusammen, ich habe schon öfter im Code das Suffix U (unsigned) hinter konstanten Werten gesehen, also z.B.: int tmp; tmp = 15U; Was hat die Kennzeichnung als unsigned für einen Sinn? Macht der GCC dann spezielle Operationen? Wie sieht das mit L (Long) aus? Viele Grüße
In deinem Beispiel macht es keinen Sinn. Der Suffix ist optional und nicht GCC spezifisch, sondern z.B. im C99 Standard enthalten. Er spezifiziert den Typ einer Ganzzahlkonstanten und hat Auswirkungen darauf, wie ein Ausdruck mit dieser Konstanten ausgewertet wird. Im einfachsten Fall ersetzt es einen Cast.
Beispiel, wo es einen Unterschied macht: Auf einem Rechner mit 4 Byte für int könnte man folgendes schreiben:
1 | #include <stdlib.h> |
2 | #include <stdio.h> |
3 | |
4 | |
5 | int main( int nargs, char **args ) |
6 | {
|
7 | unsigned long long ul = 4000*1000000; |
8 | |
9 | printf( "%llu\n", ul ); |
10 | |
11 | return 0; |
12 | }
|
Das ergibt fälschlicherweise (Ubuntu, gcc): 18446744073414584320 Grund: sowohl 4000 als auch 1000000 sind (signed) int, also wird die Berechnung in int ausgeführt. Das Produkt daraus überschreitet den Wertebereich aber, und zwar gerade so, daß das höchste Bit gesetzt ist (hexadezimal: 0xee6b2800). Das Ergebnis wird zur Zuweisung auf 8 Byte aufgeblasen, und weil es eine int mit gesetztem Bit ist, wird dieses vermeintliche Vorzeichenbit auf die neuen 4 Byte aufgezogen (0xffffffffee6b2800). Mit printf als unsigned long long ausgegeben wird dann eine viel zu große positive Zahl daraus. Andere Variante:
1 | #include <stdlib.h> |
2 | #include <stdio.h> |
3 | |
4 | |
5 | int main( int nargs, char **args ) |
6 | {
|
7 | unsigned long long ul = 4000u*1000000u; |
8 | |
9 | printf( "%llu\n", ul ); |
10 | |
11 | return 0; |
12 | }
|
Das ergibt jetzt korrekt: 4000000000 Jetzt wird nämlich ein Produkt aus zwei unsigned berechnet, ebenfalls wieder als Bitmuster 0xee6b2800. Weil dem Compiler aber klar ist, daß es unsigned ist, wird beim Erweitern auf 8 Byte das oberste Bit ignoriert und nur 0x00... vorne ergänzt. Das resultierende 0x00000000ee6b2800 ist in dezimal die erwartete 4000000000.
Bei 2 Byte für int könnte man ein ähnliches Beispiel mit 40*1000 konstruieren (überschreitet das maximal mögliche 32767 von signed int etwas, sodaß das oberste Bit 1 wird im Produkt).
Super Antwort, danke schön! Hatte mir so etwas in die Richtung schon gedacht. Aber bei Werten die als Zustandsbeschreibung genutzt werden, hat es dann keinen tieferen Sinn. Also zeugt das nur von einem gutem Programmierstil, wenn man es einfach immer benutzt.
im Prinzip ja, aber bei deinem Beispiel erscheint es mir eher zweckfrei - wie bereits von Krapao festgestellt: > int tmp; > tmp = 15U;
Bei einer Zuweisung ist der Suffix unnötig. Er wird nur benötigt, um eine Berechnung in einem bestimmten Format durchzuführen, z.B. 32bit Schieben oder unsigned Division. Peter
Peter Dannegger schrieb: > Bei einer Zuweisung ist der Suffix unnötig. Wenn man darauf Wert legt, daß der Compiler z.B. nur die Routinen für float-Berechnungen und nicht für double-Arithmetik benutzt, ist es sinnvoll auch bei Zuweisungen einen Suffix zu verwenden. Das trifft im weiteren auch auf Konstanten in Berechnungen zu. float pi; pi=3.1415926F; grad=3.1415926F*rad/4.0F;
./. schrieb: > ist es sinnvoll auch bei Zuweisungen einen Suffix zu verwenden. Warum? Eine Zuweisung erfolgt immer im Format der Variable. Z.B.:
1 | uint8_t i = -1; |
ergibt 255. ./. schrieb: > Das trifft im weiteren auch auf Konstanten in Berechnungen zu. Genau das sagte ich bereits. Peter
./. schrieb: > float pi; > > pi=3.1415926F; Welchen Unterschied erwartest du bei dieser Anweisung ohne das F? > grad=3.1415926F*rad/4.0F; Hier wird es ja wieder als Teil einer Berechnung verwendet und nicht einfach nur zugewiesen.
>> float pi; >> >> pi=3.1415926F; Das ist mir mal bei Renesas-Compilern aufgefallen. Ohne das F an den Konstanten landeten immer Teie der recht umfänglichen double-Library im viel zu kleinen Flash. Und ich brauchte nur float. Ob das jetzt standardkonform ist, will ich gar nicht beurteilen.
./. schrieb: >>> float pi; >>> >>> pi=3.1415926F; > > Das ist mir mal bei Renesas-Compilern aufgefallen. > > Ohne das F an den Konstanten landeten immer Teie der recht umfänglichen > double-Library im viel zu kleinen Flash. > Und ich brauchte nur float. > > Ob das jetzt standardkonform ist, will ich gar nicht beurteilen. Dazu hat der Standard nichts zu sagen. Der Standard kümmert sich nicht um Implementierungen sondern beschreibt das geforderte Verhalten eines Programms. Nach Ausführung der Zeilen muss der Wert in der Variablen stehen. Wie er dorthin gelangt ist Sache des Compilers.
./. schrieb: > Ohne das F an den Konstanten landeten immer Teie der recht umfänglichen > double-Library im viel zu kleinen Flash. > Und ich brauchte nur float. Das kann passieren, muss aber nicht. Abgesehen von Berechnung von konstanten Ausdrücken in einem Kontext, der eine Konstante erwartet, sind Optimierungen im Standard nicht vorgeschrieben. Allerdings hätte man schon erwarten können, dass die konstante Umrechnung einer double Konstanten in eine float Konstante vom Compiler übernommen und nicht erst zu Laufzeit durchgeführt wird.
Die Suffix spielt auch bei folgendem Code eine Rolle, Seispiel avr-gcc:
1 | void a (); |
2 | |
3 | void f (void) |
4 | {
|
5 | asm volatile ("" :: "r" (0l)); |
6 | a (-1l); |
7 | }
|
Ohne die Suffix werden 16-Bit Werte bereitgestellt, Mit dem L werden es 32-Bit Werte. A. K. schrieb: > Allerdings hätte man schon erwarten können, dass die konstante > Umrechnung einer double Konstanten in eine float Konstante vom > Compiler übernommen und nicht erst zu Laufzeit durchgeführt wird. Das setzt u.U eine komplette float- und double Emulation im Compiler voraus, da er sicherstellen muss, daß sich zB das Rundungsverhalten nicht durch die Optimierung ändert. Selbst wenn auf dem Host-Rechner, auf dem der Compiler läuft, eine FPU vorhanden ist, kann diese nur dann verwendet werden, wenn die Ergebnisse genauso sind wie auf dem Target. Das genaue Verhalten kann etwa vom jeweiligen float-Layout abhängen. Dies ist auch der Grund dafür, daß GCC Bibliotheken wie GMP und MPFR verwendet und voraussetzt: GCC verwendet nicht die Host-FPU für solche Compilezeit-Optimierungen, sondern macht alle Berechnungen per Emulation und mit dem Layout wie auf dem Target.
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.