Suffix bei konstanten Werten

#2675602
Lesenswert?

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
Gast #2675623
Lesenswert?

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.
#2675661
Lesenswert?

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.
Gast #2676850
Lesenswert?

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;
Gast #2677334
Lesenswert?

>> 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.
#2677450
Lesenswert?

./. 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.
#2677650
Lesenswert?

./. 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.
Persönliche Seite #2677730
Lesenswert?

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.

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