Variablen

Zweck

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.

nach oben

Variabeltypen

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.

nach oben

Lister der Datentypen

nach oben

Tabelle der Datentypen

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*1038
auf 6 Ziffern genau
double 8 Byte = 64 Bit 1.7*10-308 bis 1.7*10308
auf 15 Ziffern genau
long double 10 Byte = 64 Bit 3.4*10-4932 bis 3.4*104932
auf 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;
}
Ausgabe auf meinem 64-Bit System:
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

nach oben

Tabelle der Typensuffixe

TYP dezimal hexadezimal oktal
int 30 0x1e 036
unsignd int 30U 0x1eu 036U
long 30L 0x1eL 036l
double 11.
0.07
11.0
.11e2
.11E2
11e0
0.7e-2
7E-3
long double 11.L
0.07l
11.0L
.11e2L
.11E2l
11e0l
0.7e-2L
7E-3L
float 11.f
0.07F
11.0f
.11e2F
.11E2f
11e0F
0.7e-2f
7E-3f

nach oben

Automatische Typ-Vergabe

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.

nach oben

Referenzvariablen

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

nach oben

Zeiger

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.

Konstante Zeiger und Zeiger auf Konstanten

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 
Zeigerarithmetik

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
Zeiger und cstrings bzw. char-Arrays
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;
Zeiger auf Funktionen

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;
}

nach oben

Speicherklassen

Mit Speicherklassen legt man den Geltungsbereich in der Datei, die Lebensdauer und die Bindung einer Variablen fest.

Lebensdauer
Die statische Lebensdauer
Die Variable wird zu Programmbeginn einmal deklariert sowie initialsiert und existiert während der gesamten Programmausführung.
Die automatische Lebensdauer
Die Variable wird beim Eintritt in den Anweisungsblock, in dem sie definiert ist, neu erzeugt und beim Verlassen des Anweisungsblocks wieder gelöscht.
Geltungsbereich

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.

nach oben

Typqualifizierer

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.

nach oben

Zusammengestzte Datentypen

Variablentypen

nach oben

Aufzählungstypen

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

nach oben

Arrays

Arrays werden auch als Vektoren, Felder oder Reihung bezeichnet und können eine geordnete Folge von Daten eines bestimmten Typs enthalten.

zusammengesetzte Variabeltypen

nach oben

Deklaration von Arrays

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]; 

Array Übersicht

Initialisierung
Wertzuweisung an einzelne Elemente
/*Wertzuweisungen des Arrays*/
   i[0] = 5;
   i[1] = 100;
   i[2] = 66;
   i[3] = 77;
   i[4] = 1500;
Wertzuweisung über eine Schleife

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;   
}

Array Übersicht

Direkte Initialisierung bei der Deklaration

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.

Array Übersicht

Zugriff auf die Elemente

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];

Array Übersicht

Vergleich von 2 Arrays

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;
}

Array Übersicht

Anzahl der Elemente ermitteln

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;
}

Array Übersicht

Übergabe von Arrays an Funktionen
Funktionsdeklaration

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) 
Aufruf der Funktion
function(Array, Anzahl);

oder:

function(&Array[0], Anzahl);
Arrays aus Funktionen zurückgeben

Arrays können nicht als Rückgabetyp von Funktionen definiert werden. Hier hilft nur die Verwendung von Strukturen.

Array Übersicht

Mehrdimensionale Arrays
Deklarieren
int Matrix[5][4];    /* Zweidimensional */ 
Initialisieren
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} };
Wert zuweisen
Matrix[2][4]=100; 
Matrix[3][4] = 66; 

Array Übersicht

nach oben

Vektor

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:

Ermittlung der Anzahl der Elemente
v.size(); 
Zugriff auf einen Index mit Gültigkeitsprüfung
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 sind dynamisch

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);

Array Übersicht

nach oben

Chararrays / Strings

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!

Array Übersicht

nach oben

C++-Strings

string ist eine eigene Klasse die über

#include<string>

eingebunden wird und gegenüber std::vector<char> c(10); einige zusätzliche Eigenschaften.

Einen Sring definieren und initialisieren
std::string einString("Zeichenkette");
Einen String ausgeben
cout << einString;
Ein String zeichenweise ausgeben / ungeprüfter Zugriff wie bei Vector
for (size_t i=0; i < einString.size();i++)
{
	cout << einString[i];
} 
Ein String zeichenweise geprüft ausgeben mit der Vektor-Methode at

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 kopieren
string eineKopie(einString);

oder einfach als Zuweisung

string eineKopie();
eineKopie = einString;
einString = "Buchstaben";
einString = 'x';
Verkettung von Strings mit dem +-Operator
einString += "-Faktor";
einString = eineKopie + "ABC";

Array Übersicht

zusammengesetzte Variabeltypen

nach oben

Strukturen

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; 

zusammengesetzte Variabeltypen

nach oben