Variablen helfen dem Programmierer Daten temporär für eine bestimmte Zeit zu speichern. Konstanten sind Variablen, die nach der Initialisierung (nach der Zuweisung mit einem Wert) nicht mehr geändert werden dürfen. Sie bleiben halt konstant.
Alle Computer haben einen Microprozessor und einen Speicher, der für die temporäre Speicherung von Daten zuständig ist (Random Access Memory: RAM). Zusätzlich gibt es noch den Speicher, der Daten ständig speichert, oder zumindest könnten sie permanent gespeichert bleiben (Festplatte). Der Microprozessor führt das Programm aus und arbeitet mit dem RAM zusammen, um auf das Programm und eventuell gespeicherte Daten für das Programm zuzugreifen.
Der RAM Speicher ist eine große Lagerhalle mit einzelnen Räumen, auf die man über eine bestimmte Adresse zugreifen kann … zumindest kann man sich das so vorstellen. Angenommen man schreibt ein Programm, dass zwei Zahlen addiert, die der User vorher über die Tastatur eingegeben hat. In C++ (und auch in anderen Programmiersprachen) definiert man hierfür Variablen, die diese Werte dann speichern. Somit muss man sich nicht komplizierte Speicheradressen merken. Man kann diese einfach mithilfe der Namen der Variablen erreichen und darauf zugreifen.
Eine Variable zu definieren ist denkbar einfach und sieht in etwa so aus:
VariableType VariableName
oder
VariableType VariableName = InitialValue
Der Variablentyp (variable type) sagt dem Compiler, um was für eine Art von Variable es sich handelt und der Compiler reserviert dann den benötigten Platz dafür. Der Name (variable name), den der Programmierer bestimmt, ist ein gut lesbarer Ersatz für die Adresse im Speicher, in der der Inhalt der Variablen gespeichert wird.
Solange man keinen Anfangswert (initial value) angibt, kann man sich nicht sicher sein, welcher Inhalt gespeichert wird. Die Initialisierung mit einem Anfangswert ist zwar optional, aber gute Programmierpraxis. Im folgenden Beispiel sehen wir, wie man Variablen deklariert, initialisiert und in einem Programm benutzt, welches zwei Zahlen addiert.
#include <iostream> using namespace std; int main() { cout << "This program will add two numbers." << endl; cout << "Enter the first number: "; int firstNumber = 0; // Integer Variable cin >> firstNumber; cout << "Enter the second number: "; int secondNumber = 0; // Integer Variable 2 cin >> secondNumber; // Add to numbers, store ressult in a variable: int result = firstNumber + secondNumber; // Display result: cout << firstNumber << " + " << secondNumber; cout << " = " << result << endl; return 0; }
Ausgabe:
This program will add two numbers. Enter the first number: 34 Enter the second number: 45 34 + 45 = 79
Das Programm fragt den Nutzer nach zwei Zahlen, welche addiert werden und dann wird das Ergebnis angezeigt. Hier nutzen wir gleich mal das Eingabependat zu cout
, nämlich cin
. Hier wird so lange auf eine Eingabe gewartet bis der User die Return
Taste drückt. Mehr zur Ein- und Ausgabe folgt in einem späteren Artikel. Die eingegebenen Zahlen werden direkt in Variablen (firstNumber und secondNumber) abgelegt.
Die Auswahl eines geeigneten Namens für eine Variable ist wichtig, um guten Code zu schreiben. Variablen Namen können aus Buchstaben, Zahlen und Unterstrichen bestehen. Sie dürfen aber nicht mit einer Zahl beginnen. Leerzeichen dürfen sie nicht enthalten. Des Weiteren darf man kein reserviertes Schlüsselwort für eine eigene Variable verwenden. Variablen sollten nach Möglichkeit einen beschreibenden Namen haben, damit man im Idealfall beim Lesen des Namens schon weiß, was ihre Funktion ist.
Es besteht auch die Möglichkeit mehrere Variablen gleichzeitig zu initialisieren.
int firstNumber = 0, secondNumber = 0, result = 0;
Also könnte man am Anfang einer Funktion gleich alle Variablen deklarieren und initialisieren. Es ist guter Programmierstil alle Variablen an einem Platz am Anfang einer Funktion oder Klasse zu deklarieren.
Wichtig ist noch zu wissen, dass Variablen einen gewissen Gültigkeitsbereich haben, je nachdem wo sie deklariert wurden. Dazu erfahren wir aber an anderer Stelle noch mehr.
Grundlegende Variablentypen
Bisher haben wir nur Integer Variablen benutzt. C++ kennt natürlich noch weitere fundamentale Datentypen. Den richtigen Datentyp für eine Aufgabe zu wählen ist äußerst wichtig. Eine Integerzahl ohne Vorzeichen (unsigned) eignet sich schlecht um eine negative Zahl zu speichern. Im Folgenden sehen wir die verschiedenen grundlegenden Variablentypen.
bool // true or false char // 256 character values unsigned short int // 0 to 65.535 short int // -32.768 to 32.767 unsigned long int // 0 to 4.294.967.295 long int // -2.147.483.648 to 2.147.483.647 unsigned long int // 0 to 18.446.744.073.709.551.615 long long // -9.223.372.036.854.775.808 to // 9.223.372.036.854.775.807 int (16 bit) // -32.768 to 32.767 int (32 bit) // -2.147.483.648 to 2.147.483.647 unsigned int (16 bit) // 0 to 65.535 unsigned int (32 bit) // 0 to 4.294.967.295 float // 1.2e-38 to 3.4e38 double // 2.2e-308 to 1.8e308
Ok, was sind das alles für Datentypen?
Mit bool
speichert man Boolesche Variablen
C++ bietet einen Variablentypen an, der speziell dafür gedacht ist Boolesche Werte zu speichern: true
oder false
. Diese Werte sind entweder an oder aus, wahr oder falsch, 1 oder 0. Ein Beispiel für eine Deklarierung einer initialisierten Booleschen Variablen:
bool programmingIsFun = true;
Ein Ausdruck, der einen Booleschen Wert zuweist:
bool friendOfJake = (name == "Finn");
Was passiert hier? Falls der Text in der Variablen name
„Finn“ entspricht, wird der Booleschen Variablen friendOfJake
ein true
zugewiesen, falls nicht, ein false
. Aber zu Bedingungen kommen wir einem späteren Artikel.
Der Typ char
speichert Zeichen
Der Variablentyp char
speichert ein einzelnes Zeichen, z.B.:
char firstLetter = 'B';
Man sollte nicht vergessen, dass der Speicher in einem Computer aus Bits und Bytes besteht. Ein Bit ist entweder eine 1 oder eine 0 und ein Byte besteht aus mehreren Einsen und Nullen, um Zahlen darzustellen. Ein Zeichen wird im Speicher daher ebenfalls als Zahl dargestellt. Welche Zahl welches Zeichen entspricht kann man in einer ASCII Tabelle nachlesen. Der Buchstabe ‚B‚ entspricht der Dezimalzahl 66 und daher wird für die Variable firstLetter
eine 66 gespeichert.
Integer Zahlen mit und ohne Vorzeichen (signed / unsigned)
Ein Vorzeichen kann entweder positiv oder negativ ein. Wie bereits erwähnt werden Zahlen im Speicher als Bits oder Bytes abgelegt. Ein Speicherbereich von einem Byte beinhaltet 8 Bits. Jedes dieser Bits kann entweder 0 oder 1 sein (also einer von 2 Zuständen). Also kann ein Speicherbereich von 1 Byte maximal \(2^8\) Werte speichern, das sind 256 unterschiedliche Werte. Ein Speicherbereich von 16 Bit (2 Byte) kann demnach \(2^{16}\), also 65.536 unterschiedliche Werte speichern.
Falls diese Werte nun ohne Vorzeichen sind, also nur positiv, dann könnte 1 Byte Werte von 0 bis 255 speichern und 2 Byte dementsprechend Werte von 0 bis 65.535. Es ist also recht einfach positive Werte in Bits und Bytes darzustellen. Aber wie sieht es mit negativen Zahlen aus?
Eine Möglichkeit wäre, ein Bit zu opfern um das Vorzeichen darzustellen, also ob eine Zahl positiv oder negativ ist. Somit hätte man 1 Bit für das Vorzeichen und 15 Bits (Bit 0 bis 14) für die Werte zur Darstellung der Zahl, bei einem Speicherbereich von 2 Byte. Man könnte somit die Zahlen von -32.768 bis 32.767 darstellen.
Positive und negative Integer Typen short
, int
, long
und long long
Diese Variablentypen unterschieden sich in ihrer Größe und somit auch in den Zahlenbereichen, die sie speichern können. int
wird wohl am meisten benutzt und von den wichtigsten Compilern 32 Bit lang umgewandelt. Es ist wichtig zu wissen, welchen Zahlenbereich man benötigt und ausgehend davon den richtigen Typen zu wählen. Integer Typen deklariert man folgendermaßen:
short int numberOfSisters = 3; int moneySpentLastMonth = -2780; long moneySpentLastDecade = -255000;
Integer Typen ohne Vorzeichen unsigned short
, unsigned int
, unsigned long
und unsigned long long
Ganze Zahlen ohne Vorzeichen müssen kein Bit für das Vorzeichen verschwenden und können somit doppelt so große Zahlen speichern, wie ihre mit Vorzeichen belasteten Kollegen:
unsigned short presentsForBirthday = 5; unsigned int numOfStamps = 1355; unsigned long numOfCarsInBremen = 41000;
Wenn man davon ausgehen kann, dass eine Zahl mit Sicherheit nicht negativ werden kann (wenn man etwas zählt zum Beispiel), sollte man einen unsigned
Typen verwenden. Ansonsten muss man einen Typen mit Vorzeichen wählen (z.B. bei einem Bankkonto).
Fließkommatypen float
und double
Fließkommazahlen können selbstverständlich auch positiv und negativ sein. Und sie können Nachkommastellen haben. Wenn ich also die Zahl Pi (3.14) in einer Variablen speichern möchte, dann nehme ich einen Fließkommatypen.
Die Deklaration von Fließkommazahlen sieht genauso aus wie die von bspw. int
Zahlen. Ein float
, der es einem erlaubt Zahlen mit Nachkommastellen zu speichern, könnte so aussehen:
float pi = 3.14;
Und ein double precision float
(einfach double
) könnte dann so aussehen:
double morePrecisePi = 22 / 7;
Übrigens: Seit der Version C++14 kann man bei größeren Zahlen einen Tausender-Trennungspunkt angeben. Allerdings wird er als einfaches Anführungszeichen geschrieben. Das macht die Darstellung von Zahlen im Code übersichtlicher:
int moneyInBank = -70'000; // -70000 double pi = 3.141'592'653'59; // 3.14159265359
Die Größe einer Variablen mit sizeof
bestimmen
Die Größe einer Variablen ist die Größe des Speichers, den die Variable für einen bestimmten Typen benötigt. C++ besitzt einen praktischen Operator namens sizeof
, der einem die Speichergröße einer Variablen in Bytes sagen kann.
Die Nutzung von sizeof
ist einfach. Man ruft die Funktion einfach auf und übergibt den gewünschten Typen als Parameter in Klammern.
// sizeOfAVariable.cpp // Determine the size of a variable with sizeof #include <iostream> using namespace std; int main() { cout << "Computing the size of some C++ variable types" << endl; cout << "Size of bool: " << sizeof(bool) << endl; cout << "Size of char: " << sizeof(char) << endl; cout << "Size of unsigned short int: " << sizeof(unsigned short) << endl; cout << "Size of short int: " << sizeof(short) << endl; cout << "Size of unsigned long int: " << sizeof(unsigned long) << endl; cout << "Size of long: " << sizeof(long) << endl; cout << "Size of unsigned int: " << sizeof(unsigned int) << endl; cout << "Size of int: " << sizeof(int) << endl; cout << "Size of long long: " << sizeof(long long) <<endl; cout << "Size of unsigned long long: " << sizeof(unsigned long long) << endl; cout << "Size of float: " << sizeof(float) << endl; cout << "Size of double: " << sizeof(double) << endl; cout << "The output changes with compiler, hardware and OS" << endl; return 0; }
Die Ausgabe zeigt die Größe einiger Variablentypen auf dem Computer, auf dem das Programm ausgeführt wird.
Verengende Konvertierungsfehler vermeiden
Wenn ich eine Zahl vom Typ float
einer Variablen vom Typ int
zuweise, wird der Dezimalteil abgeschnitten und wenn ich eine große long
Zahl in einer short
Variablen speichere, dann bekomme ich ebenfalls einen falschen Wert. Bei einer normalen Zuweisung ist das aber kein Fehler während der Kompilierung – bestenfalls schmeißt der Compiler eine Warnung aus.
int largeNum = 5000000; short smallNum = largeNum; // compiles OK, yet narrowing error
Um solche Konvertierungsfehler zu vermeiden, gibt es seit C++11 die sogenannte List Initialisierung. Um das Feature zu benutzen, muss man die Werte / Variablen in geschweifte Klammern schreiben.
int largeNum = 5000000; short anotherNum{ largeNum }; // error! Amend types int anotherNum{ largeNum }; // OK! float someFloat{ largeNum }; // error! An int may be narrowed float someFloat{ 5000000 }; // OK! 5000000 can be accomodated
Konstanten
Mal angenommen, wir schreiben ein Programm zur Berechnung der Fläche und des Umfangs eines Kreises. Die Formeln lauten:
area = pi * radius * radius; circumference = 2 * pi * radius;
In diesen Formeln hat pi
immer einen konstanten Wert von 22/7. Dieser Wert ändert sich an keiner Stelle des Programms und wir wollen auch nicht, dass man den Wert aus Versehen irgendwo ändern kann. C++ ermöglicht es, pi
als Konstante zu definieren, welcher nach der Deklaration nicht mehr verändert werden kann. Änderungen einer konstanten Variable verursachen einen Kompilierfehler. Konstanten in C++ könnten sein:
- Literale Konstanten
- Deklarierte Konstanten mit dem Schlüsselwort
const
Literale Konstanten
Literale Kontanten können beliebigen Typs sein – Integer, Zeichenketten usw…. Im allerersten C++ Programm haben wir „Hello, C++ world!“ auf dem Bildschirm dargestellt, mit folgendem Ausdruck:
cout << "Hello, C++ world!" << endl;
In diesem Beispiel ist „Hello, C++ world!“ eine literale Zeichenkettenkonstante. Literale benutzt man ständig. Wenn ich schreibe:
int ageOfFinn = 12;
Dann ist die Dezimalzahl 12 ein Teil des Codes und wird in das Programm kompiliert. Sie kann nicht verändert werden und ist literal konstant.
Variablen als Konstanten mit const
deklarieren
Die wichtigsten Arten von Konstanten in C++ Programmen sind Variablen, die vor dem Typen das Schlüsselwort const
stehen haben. Hier ein kleines Beispiel, dass den Inhalt einer konstanten Variablen PI anzeigt:
// showPi.cpp #include <iostream> using namespace std; int main() { const double PI = 22.0 / 7; cout << "The value of constant pi is: " << PI << endl; // Uncomment next line to view compile error: // PI = 345; return 0; }
Wenn ich den Kommentar nun wegnehme und versuche das Programm zu kompilieren, dann bekomme ich folgenden Fehler:
$> c++ showPi.cpp -o showPi showPi.cpp: In function 'int main()': showPi.cpp:10:11: error: assignment of read-only variable 'PI' PI = 345; ^~~
Man beachte die Deklaration der Konstanten PI
in Zeile 7. Wir haben das Schlüsselwort const
benutzt um dem Compiler mitzuteilen, dass es sich bei PI
um eine Konstante handelt. Wenn wir jetzt in Zeile 10 versuchen PI
einen neuen Wert zuzuweisen, dann erhalten wir den obigen Compilerfehler. Genau deswegen sind Konstanten ein guter Weg zu verhindern, dass bestimmte Variablen später nochmal geändert werden.
Übrigens: Es ist ein guter Programmierstil, wenn man konstante Variablen komplett in Großbuchstaben benennt.
Ok, heute ging es darum Speicher für das Ablegen von bestimmten Werten mithilfe von Variablen und Konstanten bereitzustellen. Wir haben gesehen, dass Variablen eine bestimmte Größe haben, abhängig von ihrem Typ und dass man mithilfe von sizeof
diese Größe ermitteln kann. Die Auswahl des geeigneten Variablentyps für einen Wert ist wichtig für eine effektive Programmierung.
1 Pingback