Wofür braucht man ein Suffix bei Variablen(C++)?
Ich frage mich wofür man die braucht, wenn man doch sowieso schon davor einen Typ angibt. Was ist denn der Unterschied zwsichen float b = 1.567 und float b = 1.567f ?
Das sind doch beides float-Werte, wofür brauch ich dann dieses f am Ende ?
3 Antworten
Vorweg: Du meinst keine Variablen-, sondern Literal-Suffixe. Zu Variablensuffixen gibt es auch viel zu erzählen, aber das ist ein anderes Thema ...
Literalsuffixe sind - gerade bei "float" - extrem wichtig!Wenn du einen double-Literal nutzt, um eine float-Variable zu initialisieren oder einen neuen Wert zuzuweisen (oder umgekehrt), dann kommt es intern zu einem impliziten Casting, der in sehr vielen Fällen einige Bits Datenverlust mit sich führt.
Demonstration:#include <ios> // boolalpha
#include <iostream> // cout, endl
int main(void) {
using namespace ::std;
cout << boolalpha;
float a = 0.1234f;
cout << "a: " << (a == 0.1234f) << endl;
float b = 0.1234;
cout << "b: " << (b == 0.1234) << endl;
}
Die Ausgabe lautet:
a: true
b: false
Und das nur, wegen dem ollen Suffix! Gemein, oder? Guck dir bitte ganz genau an, welche Literale über ein angehängtes "f" verfügen, und welche nicht!
Der obige Effekt ist übrigens einer der Gründe dafür, warum man Gleitpunktzahlen niemals direkt mit "==" auf Gleichheit prüfen sollte, aber das nur am Rande ...
Wie du siehst, ist dieses "sinnlose" Suffix doch manchmal gar nicht sooooooooo "sinnlos". :)
Fazit:Wenn du innerhalb von Berechnungen / Algorithmen schwer auffindbare Fehler vermeiden willst, nutze immer die Suffixe (falls vorhanden), die zu deinen Variablentypen passen.
Das gilt insbesondere bei C++ noch viel stärker, als bei C, allein schon wegen der neuen universellen Initialisierung, der Überladung aus dem Beispiel von Isendrak oder noch extremer bei der Programmierung mit Templates. (Nichts dieser exklusiven C++ Features existiert bei C.)
Meine und die Antworten der anderen decken übrigens nicht alle Gründe FÜR die Literalsuffixe ab, es gibt dazu noch mehr zu wissen, aber ich will das hier jetzt nicht zu sehr in die Länge ziehen. (Habe unter der Antwort von PeterLustig1999 schon einen Roman verfasst ... sorry!) ><
Also dann ... schönen Tag noch! :)Letztlich bindest du ein Literal eines bestimmten Datentyps an eine Variable eines unter Umständen anderen Datentyps.
Wenn du schreibst
unsigned long a = 12345678;
weiß der Compiler nicht, ob die Zahl rechts vom Gleichheitszeichen ein Integer-Literal, oder ein Unsigned-Long-Literal sein soll. Unsereins als Mensch schaut sich die Seite links vom Gleichheitszeichen an und erkennt direkt "Die linke Seite muss ganz klar eine Konstante vom Typ unsigned long sein!" Für den Compiler ist dies nicht so einfach, denn er weiß nicht, ob das wirklich das ist, was du damit erreichen willst, oder ob du vielleicht doch eine Integer-Konstante meinst.
Das macht bei dieser Konstante erst mal keinen Unterschied. Aber wenn du irgendwann mal eine Konstante an die Variable binden willst, die höher ist, als es ein int zulassen würde, meckert der Compiler, sagt dir, dass die Zahl diesen Raum verlässt. Um dies zu umgehen, schreibt man einfach
unsigned long a = 12345678ul;
um dem Compiler zu zeigen "Pass auf, ich will eine Konstante vom Typ unsigned long an eine Variable des gleichen Typs binden."
EDIT: Hier eine Seite, die dir die Unterschiede aufzählt:
https://www.tutorialspoint.com/cplusplus/cpp_constants_literals.htm
Tja, man lernt nie aus. Was die Fehlermelduingen angeht, das war mein Fehler, da hab ich den Java-Compiler und den C(++)-Compiler durcheinander geworfen, mein Fehler ^^
... weiß der Compiler nicht, ob die Zahl rechts vom Gleichheitszeichen ein Integer-Literal, oder ein Unsigned-Long-Literal sein soll.
Weder bei C, noch bei C++ kennt der Compiler vorzeichenbehaftete Ganzzahl-Literale. Die sind ausschließlich und immer "unsigned". :)
Wenn du ...
int x = -123;
... schreibst, dann ist nur (!) "123" der Literal, und das Minus davor ein unärer Operator, der zur Kompilierzeit angewendet wird, und aus dem "unsigned int" einen "signed int" macht.
Dieses kleine feine Detail spielt in der Praxis allerdings wirklich (fast) nie eine Rolle. ;)
Einiges hat PeterLustig1999 ja schon beschrieben, hier aber noch eine Kleinigkeit:
void foo(float bar){
std::cout << "float" << std::endl;
}
void foo(double bar){
std::cout << "double" << std::endl;
}
foo(3.1415926);
//Ausgabe: double
foo(3.1415926f);
//Ausgabe: float
Im von dir genannten Beispiel macht das ganze (meistens) tatsächlich keinen (relevanten) Unterschied (ausser evtl. 4 Byte für nen Float und 8 Byte für nen Double).
Aber es gibt Situationen, in denen die Unterscheidung nicht ganz so einfach ist und daher explizit sein sollte. (Okay, man könnte in meinem Beispiel auch einfach nen Cast verwenden, braucht dann aber immer noch ganze 4 Byte mehr ;)
P.S.: Ohne Suffix wird eine Zahl mit Nachkommastellen in C++ standardmäßig als Double angenommen und in deinem Fall implizit zu nem Float "gecastet".
Noch ein Zusatz: Der Compiler weiß bei dem Literal aus deinem Beispiel ...
... übrigens ganz genau um welchen Typen es sich handelt, und beachtet während dem Parsen gar nicht den Typen, der sich vor dem Variablennamen befindet.
Wenn der Literal locker in einen "int" passt, dann wird es ein "int". Da hier "12345678" auf den gängigsten 32- und 64-Bit-Systemen also locker in einen 32-Bit breiten "int" passt, würde der Compiler erst mal von einem "int" ausgehen.
Beachte, dass der Typ des Literals vorzeichenbehaftet ist, der Wert des Literals aber nicht! (siehe meinen vorherigen Kommentar!)
Wäre deine Zahl zu groß für einen "int", würde der Compiler als nächstes probieren, ob die Zahl in einen "long" passt. Wenn nicht, dann (bei neueren C-Versionen bzw. Compilern) einen "long long", und wenn der Literal auch dafür zu groß ist, bekommst du einen Fehler um die Ohren gehauen. :)
Zurück zum Thema: Du hast den Literal "12345678", der automatisch (da kein Suffix vorhanden) ganz eindeutig vom Compiler als "int" identifiziert wurde und danach einer "unsigned long" Variablen zugewiesen wird. Dafür ist ein impliziter Cast nötig, der allerdings von allen großen und modernen Compilern wegoptimeirt wird. Bei kleinen und älteren Compilern (besonders bei herstellerspezifischen Compilern, Mikrocontrollern und eingebetteten Systemen) findet man aber im Kompilat tatsächlich oft noch Reste eines Castings. (Genau das selbe Thema habe ich hier letzte Woche schon mal unter einer anderen Frage angesprochen, aber ist ja auch egal ...)
Würdest du jetzt allerdings ...
... also mit abschließendem "u" und "l" Suffix schreiben, wüsste der Compiler auf anhieb, dass es sich um einen "unsigned long" Literal handelt. Wie gesagt, er beachtet in dieser Phase gar nicht, was auf der anderen Seite des Gleichheitszeichens steht.
Erst im nächsten Schritt sieht er, dass dieser "unsigned long" Literal auch genutzt wird, um eine "unsigned long" Variable zu initialisieren und freut sich ein Loch in den Bauch, weil er jetzt nicht extra casten muss. :)
Noch zwei Anmerkungen:
Naja, das war jetzt zwar noch nicht alles zu Integerliteralen, aber ich denke das reicht. ;)
Gute Nacht! :)