Variablen werden benötigt, um Werte zwischenzuspeichern. Damit kann an anderer Stelle durch Nutzung der Variable auf den zuvor in dieser Variable ermittelten Wert zugegriffen werden.
Die Werte der Variabeln werden im Arbeitsspeicher abgespeichert. Der Computer selbst kann aber ausschließlich binäre Zahlen verarbeiten. Entsprechend werden auch alle Daten in Binärer Form also ausschließlich mit 0 und 1 in den Speicherzellen abgelegt. Um sie hinterher wieder korrekt zu deuten, muss mitgeteilt werden, welches Format die Daten haben. Dadurch wird deutlich wieviele Speicherzellen zusammengehören und ob die darin enthaltenen Ziffern tatsächlich als Zahlen oder vielleicht doch als Buchstaben gedeutet werden müssen.
Das mitteilen des Datenformats von Werten in einer Variable erfolgt dadurch, das die Variable mit einem bestimmten Typ definiert wird.
Name | min Größe | Wertebereich |
---|---|---|
char | 1 Byte = 8 Bit | -128 bis +128 |
wchar_t | 2 Byte = 16 Bit | -32768 bis +32767 |
unsigned char | 1 Byte = 8 Bit | 0 bis 255 |
short | 2 Byte = 16 Bit | -32768 bis +32767 |
unsigned short | 2 Byte = 16 Bit | 0 bis +65535 |
int | 4 Byte = 32 Bit | -2147483648 bis +2147483648 |
unsigned int | 4 Byte = 32 Bit | 0 bis 4294967295 |
long | 4 Byte = 32 Bit | -2147483648 bis +2147483648 |
unsigned long | 4 Byte = 32 Bit | 0 bis 4294967295 |
float | 4 Byte = 32 Bit | 3.4*10-38 bis 3.4*1038auf 6 Ziffern genau |
double | 8 Byte = 64 Bit | 1.7*10-308 bis 1.7*10308auf 15 Ziffern genau |
long double | 10 Byte = 64 Bit | 3.4*10-4932 bis 3.4*104932auf 19 Ziffern genau |
Bei der Angabe der Genauigkeit wird immer von links nach rechts gezählt. Haben wir eine Genauigkeit von 6 und 4 Ziffern vor dem Komma, bleiben noch 2 Ziffern für die Nachkommastellen. Die Größe in Bytes eines Datentyps auf dem jeweiligen Sytem kann verschieden sein. Auf einen 16 Bit-Betriebssystem kommen so ganz andere Wertebereiche zustande als auf einem 64 Bit-Sytem. Um die Größe des Datentyps zu bestimmen gibt es die Funktion sizeof():
#include <iostream> #include <limits.h> #include <float.h> using namespace std; int main(int argc, char **argv) { cout << "char:" << sizeof(char) << " von " << CHAR_MIN << " bis " << CHAR_MAX << " Bits: " << CHAR_BIT << endl; cout << "unsigned char:" << sizeof(unsigned char) << " bis " << UCHAR_MAX << endl; cout << "short:" << sizeof(short)<< " von " << SHRT_MIN << " bis " << SHRT_MAX <<endl; cout << "unsigned short:" << sizeof(unsigned short) << " bis " << USHRT_MAX <<endl; cout << "int:" << sizeof(int) << " von " << INT_MIN << " bis " << INT_MAX << endl; cout << "unsigned int:" << sizeof(unsigned int) << " bis " << UINT_MAX << endl; cout << "long:" << sizeof(long) << " von " << LONG_MIN << " bis " << LONG_MAX << " Bits: " << LONG_BIT << endl; cout << "unsigned long:" << sizeof(unsigned long) << " bis " << ULONG_MAX << endl; cout << "long long:" << sizeof(long long) << " von " << LONG_LONG_MIN << " bis " << LONG_LONG_MAX << endl; cout << "float:" << sizeof(float) << endl; cout << "double:" << sizeof(double) << endl; cout << "long double:" << sizeof(long double) << endl; cout << "Basis für Exponentendarstellung: " << FLT_RADIX << endl; cout << "Anzahl Mantissenstellen (float): " << FLT_MANT_DIG << endl; cout << "Anzahl Mantissenstellen (double): " << DBL_MANT_DIG << endl; cout << "Anzahl Mantissenstellen (long double): " << LDBL_MANT_DIG << endl; cout << "Genauigkeit in Dezimalziffern (float): " << FLT_DIG << endl; cout << "Genauigkeit in Dezimalziffern (double): " << DBL_DIG << endl; cout << "Genauigkeit in Dezimalziffern (long double): " << LDBL_DIG << endl; cout << "Minimalster negativer FLT_RADIX-Exponent (float): " << FLT_MIN_EXP << endl; cout << "Minimalster negativer FLT_RADIX-Exponent (double): " << DBL_MIN_EXP << endl; cout << "Minimalster negativer FLT_RADIX-Exponent (long double): " << LDBL_MIN_EXP << endl; cout << "Minimalster negativer Zehnerexponent (float): " << FLT_MIN_10_EXP << endl; cout << "Minimalster negativer Zehnerexponent (double): " << DBL_MIN_10_EXP << endl; cout << "Minimalster negativer Zehnerexponent (long double): " << LDBL_MIN_10_EXP << endl; cout << "Maximaler FLT_RADIX-Exponent (float): " << FLT_MAX_EXP << endl; cout << "Maximaler FLT_RADIX-Exponent (double): " << DBL_MAX_EXP << endl; cout << "Maximaler FLT_RADIX-Exponent (long double): " << LDBL_MAX_EXP << endl; cout << "Maximaler Zehnerexponent (float): " << FLT_MAX_10_EXP << endl; cout << "Maximaler Zehnerexponent (double): " << DBL_MAX_10_EXP << endl; cout << "Maximaler Zehnerexponent (long double): " << LDBL_MAX_10_EXP << endl; cout << "Maximaler Gleitpunktwert (float): " << FLT_MAX << endl; cout << "Maximaler Gleitpunktwert (double): " << DBL_MAX << endl; cout << "Maximaler Gleitpunktwert (long double): "<< LDBL_MAX << endl; cout << "Kleinster float-Wert x für den 1.0 + x ungleich 1.0 gilt: " << FLT_EPSILON << endl; cout << "Kleinster double-Wert x für den 1.0 + x ungleich 1.0 gilt: " << DBL_EPSILON << endl; cout << "Kleinster long double-Wert x für den 1.0 + x ungleich 1.0 gilt: " << LDBL_EPSILON << endl; cout << "Minimalster normalisierter Gleitpunktwert (float): " << FLT_MIN << endl; cout << "Minimalster normalisierter Gleitpunktwert (double): " << DBL_MIN << endl; cout << "Minimalster normalisierter Gleitpunktwert (long double): " << LDBL_MIN << endl; return 0; }
char:1 von -128 bis 127 Bits: 8 unsigned char:1 bis 255 short:2 von -32768 bis 32767 unsigned short:2 bis 65535 int:4 von -2147483648 bis 2147483647 unsigned int:4 bis 4294967295 long:8 von -9223372036854775808 bis 9223372036854775807 Bits: 64 unsigned long:8 bis 18446744073709551615 long long:8 von -9223372036854775808 bis 9223372036854775807 float:4 double:8 long double:16 Basis für Exponentendarstellung: 2 Anzahl Mantissenstellen (float): 24 Anzahl Mantissenstellen (double): 53 Anzahl Mantissenstellen (long double): 64 Genauigkeit in Dezimalziffern (float): 6 Genauigkeit in Dezimalziffern (double): 15 Genauigkeit in Dezimalziffern (long double): 18 Minimalster negativer FLT_RADIX-Exponent (float): -125 Minimalster negativer FLT_RADIX-Exponent (double): -1021 Minimalster negativer FLT_RADIX-Exponent (long double): -16381 Minimalster negativer Zehnerexponent (float): -37 Minimalster negativer Zehnerexponent (double): -307 Minimalster negativer Zehnerexponent (long double): -4931 Maximaler FLT_RADIX-Exponent (float): 128 Maximaler FLT_RADIX-Exponent (double): 1024 Maximaler FLT_RADIX-Exponent (long double): 16384 Maximaler Zehnerexponent (float): 38 Maximaler Zehnerexponent (double): 308 Maximaler Zehnerexponent (long double): 4932 Maximaler Gleitpunktwert (float): 3.40282e+38 Maximaler Gleitpunktwert (double): 1.79769e+308 Maximaler Gleitpunktwert (long double): 1.18973e+4932 Kleinster float-Wert x für den 1.0 + x ungleich 1.0 gilt: 1.19209e-07 Kleinster double-Wert x für den 1.0 + x ungleich 1.0 gilt: 2.22045e-16 Kleinster long double-Wert x für den 1.0 + x ungleich 1.0 gilt: 1.0842e-19 Minimalster normalisierter Gleitpunktwert (float): 1.17549e-38 Minimalster normalisierter Gleitpunktwert (double): 2.22507e-308 Minimalster normalisierter Gleitpunktwert (long double): 3.3621e-4932
TYP | dezimal | hexadezimal | oktal |
---|---|---|---|
int | 30 | 0x1e | 036 |
unsignd int | 30U | 0x1eu | 036U |
long | 30L | 0x1eL | 036l |
double | 11.0.0711.0.11e2.11E211e00.7e-27E-3 | ||
long double | 11.L0.07l11.0L.11e2L.11E2l11e0l0.7e-2L7E-3L | ||
float | 11.f0.07F11.0f.11e2F.11E2f11e0F0.7e-2f7E-3f |
Mit dem neusten C++11 Standard kann einer Variable, die bei der Deklaration initialisiert wird, durch voranstellen des Schlüsselwortes auto der zum Initialwert passende Datentyp zugeordnet werden. Beispiel:
#include <iostream> using std::cout; using std::endl; int main() { auto i=65.6; decltype(i) a; a=17.8; cout << i << endl; cout << a << endl; return 0; }
Mit dem decltype(Ausdruck) Operator kann einer Variable der selbe Datentyp, wie der des Ausdruckes gegeben werden. Der Ausdruck, kann eine Variable oder eine Funktion sein, die nicht mit void als Rückgabetyp deklariert ist.
Referenzvariablen enthalten Verweise auf bereits bestehende Variablen. Somit kann eine Speicherstelle über zwei verschiedene Namen angesprochen werden. Sie werden immer bei der Deklaration direkt initialisiert:
DTYP& RName = Name;
Sie müssen den selben Datentypen haben, wie die Variable auf die sie verweisen.
Der Verweis auf eine Variable kann im Nachgang nicht mehr gändert werden.
Wird nun der Refferenzvariable ein Wert zugewiesen, wird dieser auch der Variable zugewiesen.
Beispiel:
#include <iostream> using std::cout; using std::endl; int main() { double i=65.6; double& a=i; a=17.8; cout << i << endl; return 0; }
Ausgabe:
17.8
Zeiger sind Veriabeln, die eine Adresse im Speicher als Wert enthalten.
Dabei wird bei der Definition die Variable durch ein vorangsetellten '*' als Zeiger gekennzeichnet. Die vorweg gestellte Typbezeichnung klärt, welcher Typ von Daten an der Adresse zu erwarten ist. '*' kann an beliebiger Stelle zwischen Typ und Variable stehen und bezieht sich jeweils nur auf die direkt folgende Variable.
Durch Zuweisung einer Variable entsprechnden Typs mit vorangestelltem Adressoperator '&' wird die Adresse dieser Variable als Wert in der Zeigervariable gespeichert.
int i = 99; // Die Integervariable i wird definiert und mit 99 ininitialisiert int *ip; // Der Zeiger ip wird als Adresse definiert, an der ein Integer-Wert liegt int *ip = NULL; // [NULL aus <cstddef>] Initialisiert zunächst mit dem int *ip = 0; // abfragbaren Wert nichts. (NULL-Zeiger) ip = &i; //Deklaration Adresse von i wird zugewiesen int *ip2 = &i; // gleichzeitige Definition und Initialisierung int* ip3 = &i; // '*' kann beliebig zwischen Typ und int * ip4 = &i; // Variable stehen int* ip5, *ip6 , x; // Erstellt die Zeiger ip5+6 // und die Integervariable x // besser die Definition der Integervariable int y; // in eine extra Zeile char* cp; // Zeiger auf ein Charobjekt void* vp // void-Zeiger auf ein beliebiges undefiniertes Objekt // wobei vp = cp // möglich ist aber cp = vp // eine Fehlermeldung des Compilers verurasacht // Typumwandlung erfoderlich: cp = static_cast<char*>(vp); // Zuweisung eines void-Zeiger // an einen Char-Zeiger // umgeht die Typ-Kontrolle // nur in Ausnahmefällen nutzen !!!
Der Wert der Variable i kann jetzt sowohl durch Zuweisung an i, als auch *ip oder *ip2 auf die gleiche Weise verändert werden, wobei *ip und *ip2 Alias-Namen von i sind. Demnach bewirken die folgenden Zuweisungen jeweils, dass der Wert von i auf 10 gesetzt wird:
i = 10; *ip = 10; *ip2 = 10;
WICHTIG: Die Zeigervariable sollte den selben Gültigkeitsbereich wie die Variable haben, auf deren Inhalt sie zeigt, da es zu schwerwiegenden Fehlern führt, wenn die Variable, auf die der Zeiger zeigt bereits nicht mehr gültig ist.
Wenn ein Zeiger immer auf die selbe Variable verweist, kann er als konstant definiert werden:
Typ *const Variablenname // Zeiger kann nicht verändert werden
Wenn ein Zeiger auf eine Konstante definiert werden soll wird er wie folgt definiert:
const char * Variablenname // Wert kann nicht geändert werden
Kombiniert sieht es dan so aus:
const char *const variablenName
Dabei liest man die Definition immer von rechts nach links, sprich: Variable ist ein und setzt für '*' Zeiger auf ein:
const int* variable; // variable ist ein Zeiger auf int const -> Konstanter Wert int* const variable; // variable ist ein const Zeiger auf int -> Zeiger konstant const int* const variable; // variable ist ein const Zeiger auf int Const -> Wert und Zeiger Konstant int const* variable; // variable ist ein zeiger auf const int -> Wert kostant int const* const variable; // variable ist ein const Zeiger auf const int -> Wert und Zeiger Konstant
Interessant ist die Nutzung von Zeigern innerhalb von carrays. Statt Felder[feldNR] kann auch die Zeigerschreibweise: *(Felder+Feldnummer) angewendet werden. Aufgrund des vorgesetzten Sternchens wird der Wert - ohne würde die Adresse - zurückgegeben.
Bei einem Zeiger auf ein Array, wird bei der Initiierung immer die Adresse des ersten Feldes übernommen. Durch Addition lassen sich dahinter liegende Felder anspringen.
Beispiel:
double d[10]; double *dptr1 = d; double *dptr2 = dptr1; dptr2++; // dptr2 -dptr1 = 1
Es gilt:
*p++ oder *(p++) : geben den Wert zurück und setzen dach den Zeiger auf das folgende Feld
(*p)++: Zählt den Wert hoch, der an der Speicherstelle auf die p zeigt abgelegt ist
Beispiel: Zeiger für die Suche nach einem Schlüssel im Array
a[N] = key; int *p = a; while(*p++ != key); // *p++ gibt erst den Wert des Zeigers // und zählt dann eins hoch i = p-a-1; // der gesuchte index
const char * str = "ABC"; // "ABC" ist eine Zeichenkettenkostante cout << str; // gibt aus: ABC
Durch diese Schreibweise wird vom Compiler automatische ein kostanntes (nur lesbares) Array {'A','B', 'C','\0'} angelegt auf das der Zeiger str zeigt. Der Datentyp eines C-Strings ist immer char*. Da hier allerdings mit "ABC" eine Stringkonstante übergeben wurde, ist diese nicht änderbar! Deshalb sind Zeiger auf Stringliterale immer als konstant zu deklarieren ( const char* ). Dabei bleibt eine Zuweisung einer neuen Zeichenkette möglich, wobei die Information über die vorherige Stelle verloren geht.
Die Nullterminierung macht folgende einfache Funktion zur Ermittlung der Länge eines cstrings oder chararrays möglich:
#include <iostream> using std::cout; using std::endl; int main ( int argc, char* argv[] ) { const char* eineZeichenkette ="Hallo Welt!"; int laenge = 0; while(*eineZeichenkette++) { ++laenge; } cout << "Stringlänge von " << (eineZeichenkette-laenge-1) << " = " << laenge << endl; }
Oder das einfache kopieren eines cstrings
const char* quelle = "kopiere jeden Buchstaben von mir!"; laenge = 0; while(quelle[laenge++]); // laenge inclusive '\0' char ziel[laenge]; const char* q = quelle; char* z = ziel; while( (*z++ = *q++) ); cout << quelle << " = " << ziel;
Mit Hilfe von Zeigern auf Funktionen ist es möglich über einen Zeiger zur Laufzeit zu entscheiden, welche Funktion angesprungen wird:
char c; cout << "max (1) oder min (0) ausgeben (sonst = Ende)?"; cin >> c; // Zuweisung von max() oder min() switch(c) { case ’0’: fp = &min; break; // Funktionsadresse zuweisen // Ohne den Adressoperator & wandelt der Compiler den // Funktionsnamen automatisch in die Adresse um: case ’1’: fp = max; break; default : fp = 0; // gleichbedeutend: NULL } if(fp) { // d.h if(fp != 0) // Dereferenzierung des Funktionszeigers und Aufruf cout << (*fp)(a, b) << endl; // oder direkt Funktionszeiger als Name verwenden: // (implizite Typumwandlung des Zeigers in die Funktion) cout << fp(a, b) << endl; }
Mit Speicherklassen legt man den Geltungsbereich in der Datei, die Lebensdauer und die Bindung einer Variablen fest.
auto kann man bei der Deklaration einer Variable auch einfach weg lassen, da es das Standardverhalten ist. Hierbei wird die Variable automatisch angelegt und später auch automatisch wieder gelöscht, ohne dass man sich als Programmier darum kümmern muss.
extern vor einer Variablendeklaration zeigt an, dass die Variable in einer anderen ebenfalls zum Programm gehörenden Datei deklariert wurde und in dieser Datei genutzt werden soll.
register setzt man nur ein, wenn mann meint schlauer zu sein als der Compiler. Man bewirkt damit, dass die Variable so lange wie möglich im super schnellen Prozessregister gehalten wird, um entsprechend schnell darauf zugreifen zu können.
static wird vor immer währenden Variablen mit einem beschränkten Geltungsbereich gesetzt.
volatile
do { printf("Gerät X wird überprüft.....\n"); }while(reg & (STATUS_A|STATUS_B) == 0); printf("Gerät X Status ...... [OK]\n");
Eigentlich soll hier auf ein bestimmtes Ereignis wie die Antwort eines Gerätes gewartet werden. Manche Compiler erkennen jetzt an der while-Schleife, dass hier immer auf die gleiche Adresse überprüft wird, und optimieren die do while-Schleife einfach weg. Dieser Vorgang wird dann nur einmal durchgeführt, da die Schleife weg ist. Wird dabei die Hardware nicht erkannt, entsteht ein Problem. Somit gilt für Variablen, die mit volatile deklariert sind, dass diese ohne jede Optimierung neu aus dem Hauptspeicher geladen und neue Werte auch sofort wieder dort abgelegt werden.
const definiert eine Konstante, die während der Laufzeit des Programms ihren Wert nicht mehr ändert.
Mit Enummerationen bzw. Aufzählungstypen ist es möglich einen Datentyp
zu definieren, der nur die in der Aufzählliste angegeben Werte annehmen kann.
Bsp: einen Datentyp Werktag deklarieren:
enum Werktag {Montag,Dienstag,Mittwoch,Donnerstag,Freitag}
Danach können Variabeln mit diesem Typ definiert und initialisiert werden:
Werktag Wochenanfang, letzterTag; Werktag heute = Dienstag;
oder, falls nur einmalig eine Variable vom Typ mit einer Aufzählung definiert werden soll:
enum {Montag,Dienstag,Mittwoch,Donnerstag,Freitag} einTag;
All diesen Typen können danach ausschließlich Werte aus der Liste zugewiesen werden. Bei Rechnungen werden die Werte zuvor in int umgewandelt, können aber als int nicht den Enumvariabeln zugewiesen werden. Außerdem können den Aufzählwerten nach der Typdefinition keine Werte zugewiesen werden. Die Werte werden intern auf die natürlichen Zahlen beginnend bei Null abgebildet, so dass 0 für den Wert Montag aus dem obigen Beispiel steht. Falls eine andere Zuordnung gewünscht ist kann diese in der Definiton mit angegeben werden:
enum Werktag {Montag=1,Dienstag=2,Mittwoch=4,Donnerstag=8,Freitag=16}
oder noch einfacher:
enum Werktag {Montag=1 << 0 ,Dienstag=1 << 2 ,Mittwoch=1 << 3,Donnerstag= 1 << 4,Freitag= 1 << 5}
Im gleichen Gültigkeitsbereich dürfen die gleichen Werte nicht in unterschiedlichen Aufzähltypen verwendet werden. folgendes wäre also nicht erlaubt:
enum Werktag {Montag,Dienstag,Mittwoch,Donnerstag,Freitag}; enum Wochentage {Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag}
zusammengesetzte Variabeltypen
Arrays werden auch als Vektoren, Felder oder Reihung bezeichnet und können eine geordnete Folge von Daten eines bestimmten Typs enthalten.
zusammengesetzte Variabeltypen
Bei der Deklaration wird immer der Typ und die Anzahl der Elemente festgelegt. Entsprechend wird für das Array Speicher reserviert. Ein dynamisch wachsendes Array gibt es nicht.
Datentyp Arrayname[Anzahl_der_Elemente]; int i[5];
/*Wertzuweisungen des Arrays*/ i[0] = 5; i[1] = 100; i[2] = 66; i[3] = 77; i[4] = 1500;
Beim Ansprechen der Felder ist darauf zu achten, dass die Zählung bei Null beginnt. Entsprechend hat ein Array mit 10 Federn die Indeces 0-9 !!!
int main() { int test[10]; for(int i=0; i<10; i++) test[i]=i; }
Dabei wird die Größe automatisch auf die Anzahl der gegbenen Elemente festgelegt.
int numbers[] = {1,2,4,5,9};
Möchte man mehr Elemente haben, kann mann allerdings auch eine größere Feldlänge angeben. Die restlichen Feldelemente werden dann automatisch mit Null initialisiert:
int numbers[20] = {1,2,4,5,9};
Damit kann man ein größeres Array auch auf folgende Weise kommplett, also in allen Feldern mit Null (aber auch nur mit Null) initialisieren:
int bigarray[1000]={0};
Globale und statische Arrays werden automatisch mit Null initialisiert.
Auf die einzelnen Elemente kann dann über den Indizierungsoperators [] zugegriffen werden. Wird der Name des Arrays ohne diesen Operator angegben, so enthält diese nur die Speicheradresse, an dem das Array beginnt.
cout << bigarray[100];
Möchte mann zwei Arrays vergleichen, so muss man deren Felder vergleichen:
int main() { int i; int array1[10]; int array2[10]; for(i=0; i<10; i++) { array1[i]=i; array2[i]=i; } array2[5] = 100; /* array2 an Pos. 5 verändern */ for(i=0; i<10; i++) { if( array1[i] == array2[i] ) continue; else { cout << "Unterschied an Position" << i; break; } } return 0; }
Alternativ kann man auch die Funktion int memcmp(const void *adr1, const void *adr2, size_t n); aus der Headerdatei <string.h> nutzen:
#include <iostream> #include <string.h> int main() { int i; int array1[10]; int array2[10]; for(i=0; i<10; i++) { array1[i]=i; array2[i]=i; } array2[5] = 100; /* Verändert array2 an Pos. 5 */ if(memcmp(array1, array2, sizeof(array1)) == 0 ) cout << "Beide Arrays haben den gleichen Inhalt\n"; else cout << "Die Arrays sind unterschiedlich\n"; return 0; }
Die Anzahl der Elemente kann mit Hilfe des sizeof-Operators ermittelt werden. Dabei ermittelt man die Größe des Speichers des Arrays und teilt dies dann durch die Größe des Datentyps des Arrays.
#include <iostream> using std::cout; using std::endl; int main() { int zahlen[] = {0,1,2,3,4,5,6,7,8,9}; cout << "Anzahl der Elemente: " << sizeof(zahlen)/sizeof(int) << endl; return 0; }
Der Funktion wird als Parameter das Array ohne eine vorgeschriebene Größe übergeben, damit die Funktion Arrays in allen Größen verarbeiten kann. Die Anzahl der Elemente kann dann als weiteres Argument übergeben werden: Dabei wird allerdings tatsächlich nicht das Array sondern nur die Startadresse im Speicher an der sich das Array befindet übergeben. (call-by-reference)
Möchte mann dieses Verhalten umgehen muss man Strukturen verwenden
void function(int feld[], int n_Anzahl)
function(Array, Anzahl);
oder:
function(&Array[0], Anzahl);
Arrays können nicht als Rückgabetyp von Funktionen definiert werden. Hier hilft nur die Verwendung von Strukturen.
int Matrix[5][4]; /* Zweidimensional */
int Matrix[5][4] = { {10,20,30,40,50}, {15,25,35,45,55}, {20,30,40,50,60}, {25,35,45,55,65}};
Auch hier kann man wieder die automatische Initialisierung mit Null nutzen:
int Matrix[4][4] = { {0}, {1}, {0,1}, {0,0,1} };
Matrix[2][4]=100; Matrix[3][4] = 66;
Einspaltige Arrays werden in C++ durch eine Konstruktion abgebildet, die sich Vektor nennt. Ihr Vorteil gegenüber gewöhnlichen Arrays liegt in einem deutlich höheren Kompfort. So stehen einige Funktionen zur Verfügung:
v.size();
v.at(10);
Gibt es Den Index 10 in diesem Vektor nicht wird beim Zugriff über die at-Funktion das Programm abgebrochen und eine Fehlermeldung ausgegeben.
Vektoren können bei Bedarf Elemente angehängt werden. Falls der Platz an der Speicherstelle nicht ausreicht, wird der Vektor automatisch an eine Stelle mit ausreichend Platz kopiert.
v.push_back(wert);
Ein Vektor ist eine Tabelle von Elementen desselben Datentyps. Dieser wiederum ist mit Ausnahme von Referenzen beliebig. Der Zugriff erfolgt wie bei einem Array über den Index des Elementes. Diese sind stehts von 0 bis zur Anzahl-der-Elemente -1 durchnummeriert. Wird allerdings ein zu großer Index beim Zugriff angegeben erfolgt keine Fehlermeldung!
Für die Nutzung der Klasse Vektor muss diese zunächst includiert werden. Außerdem wird in der bei der Arbeit mit Vektoren in Schleifen der Datentyp size_t benötigt für den cstddef includiert werden muss:
#include<vector> #include<cstddef>
Ein Vektor wird wie folgt definiert:
vektor <Datentyp> Variablenname (AnzahlDerElemente);
Beispielsweise wird ein Vektor v der 10 Elemente des Datentyps int
aufnehmen kann so definiert:
vektor<int> v(10);
Arrays vom Datentyp Char werden Strings genannt. Dabei ist es wichtig, dass das letzte Zeichen ein '\0' ist. Chararrays / Strings können wie folgt initialisiert werden:
char hallo[]={'H','a','l','l','o', ' ', 'W','e','l','t','\n','\0'}; char hallo2[] = {"Hallo Welt\n"};
Auf die einzelnen Buchstaben der Zeichenkette, kann auf die für ein Array übliche Art und Weise zugegriffen werden.
hallo[5]='_'
Die Länge des Strings kann ermittelt werden, in dem nach dem Zeichen '\0' gesucht wird. Diese Länge muss nicht mit der Länge des Arrays übereinstimmen. Bsp:
#include <iostream> using std::cout; using std::endl; int main() { char Maik[] = {"Maik\0ist der Beste!"}; int i; cout << "Anzahl der Elemente: " << sizeof(Maik)/sizeof(char) << endl; for(i=0; Maik[i]!='\0';i++); cout << "Länge des Strings: " << Maik << ' ' << i << endl; Maik[i]=' '; cout << "Und jetzt die ganze Wahrheit: " << Maik << endl; return 0; }
ergibt folgende Ausgabe:
Anzahl der Elemente: 20 Länge des Strings: Maik 4 Und jetzt die ganze Wahrheit: Maik ist der Beste!
string ist eine eigene Klasse die über
#include<string>
eingebunden wird und gegenüber std::vector<char> c(10); einige zusätzliche Eigenschaften.
std::string einString("Zeichenkette");
cout << einString;
for (size_t i=0; i < einString.size();i++) { cout << einString[i]; }
Die String-Länge kann auch mit der String-Methode length() ermittelt werden:
for( size_t i=0; i < einString.length(); i++) { cout << einString.at(i); }
string eineKopie(einString);
oder einfach als Zuweisung
string eineKopie(); eineKopie = einString; einString = "Buchstaben"; einString = 'x';
einString += "-Faktor"; einString = eineKopie + "ABC";
zusammengesetzte Variabeltypen
Die Struktur fast einen Datensatz zusammen und kann darin entsprechend
unterschiedlichste Datentypen aufnehmen.
Dabei ist eine Struktur in C++ nichts anderes als eine Klasse mit
standardmäßig öffentlichen Elementen.
Die Syntax einer Struktur ist wie folgt aufgebaut:
struct Typname {Elemente} Variablenliste;
Beispiel: Definition eines Punktes p und q mit seinen Koordinaten x und y einer Farbe und einer Angabe, ob der Punkt sichtbar ist:
enum Farbetyp {rot,gelb,braun}; struct Punkt { int x; int y; Farbtyp punktFarbe; bool sichtbar; } p; Punkt q;
Der Zugriff auf die Elemente erfolgt über die Punkt-Notation:
p.x = 270; p.y=300; p.sichtbar=true; p.punktFarbe = gelb;