Programmieren in C++ Arrays, Strings und Zeigerarithmetik
Inhalt Eindimensionale C-Arrays C-Strings und Strings (Mehrdimensionale C-Arrays) Arrays und Vektoren (C++) Unique Pointers (C++11) Zeigerarithmetik Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-2
Eindimensionale C-Arrays Grundsätze Länge des Arrays wird nicht abgespeichert die Länge ist nur im Sichtbarkeitsbereich der Definition des Arrays bekannt sehr grosse Arrays sollen auf dem Heap (dynamisch) angelegt werden statische Erzeugung Länge ist konstant und zur Kompilationszeit bekannt Array kann auf dem Stack angelegt werden Beispiel char text[100]; dynamische Erzeugung Array wird zur Laufzeit auf dem Heap angelegt Beispiel const int len = 100; // len kann, aber muss nicht konstant sein char *text = new char[len]; char *const t = new char[len]; // new liefert einen konstanten Zeiger zurück delete[] text; // Speicherplatz des Arrays wird freigegeben delete[] t; Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-33
C-Strings Was ist ein C-String? ein eindimensionales Character-Array Ende der gültigen Zeichenkette ist durch ein 0-Character gekennzeichnet wie bei allen Arrays in C/C++: die Länge wird nicht mitgespeichert Unterschied zu anderen Arrays vereinfachte Initialisierung erlaubt char s[] = "Das ist ein Test."; // String-Schreibweise anstatt Initialisierungsliste s zeigt auf eine Kopie des konstanten Strings "Das ist ein Test." sizeof(s) gibt den Speicherbedarf der Kopie zurück (im Sichtbereich der Def.) implizite Konstante const char *t = "Das ist ein Test."; t zeigt direkt auf den konstanten String der Länge 18! sizeof(t) gibt die Anzahl Bytes des Zeigers t zurück Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-4
Neue Zeichentypen (C++11) bisher const char *s = "abcd"; 8-Bit String const wchar_t *s = L"abcd"; 16-Bit String neu (nicht in Visual C++ 2012) const char *s = u8"abcd"; UTF8 String-Repräsentation const char16_t *s = u"abcd"; UTF16 String-Repräsentation const char32_t *s = U"abcd"; UTF32 String-Repräsentation Unicode-Codepoints (nicht in Visual C++ 2012) 16 Bit Unicode-Codepoints: \u1234 32 Bit Unicode-Codepoints: \u123456 (4-stelliger Hex-Code) (6-stelliger Hex-Code) Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-5
C-Array als Funktionsparameter Funktion liegt ausserhalb des Sichtbarkeitsbereichs der Definition des Arrays Länge des Arrays ist nicht bekannt und sollte als Parameter mitgegeben werden sizeof kann nur die Anzahl Bytes des Zeigers ermitteln 2 gleichwertige Schreibweisen void print(char *s) { void print(char s[]) { // ist zu bevorzugen, weil Array ersichtlich ist keine Spezialbehandlung für C-Strings void foo(char s[]) { char s1[] = "ABC"; s[0] = 'a'; char *s2 = "DEF"; // deprecated const char *s3 = "GHI"; foo(s1); foo(s2); // Runtime-Error foo(s3); // Compiler-Error Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-6
Mehrdimensionale C-Arrays Grundsätze mehrdimensionale C-Arrays werden im Hintergrund als eindimensionale Arrays abgespeichert die Länge der ersten Dimensionen ist nur im Sichtbarkeitsbereich der Definition des Arrays bekannt die Längen der weiteren Dimensionen gehen mit in den Typ ein sehr grosse Arrays sollen auf dem Heap (dynamisch) angelegt werden Erzeugung statisch Anzahl Dimensionen ist fix und zur Kompilationszeit bekannt Längen der Dimensionen sind konstant und zur Kompilationszeit bekannt dynamisch Anzahl Dimensionen ist fix und zur Kompilationszeit bekannt Längen der Dimensionen sind konstant und mit Ausnahme der ersten zur Kompilationszeit bekannt Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-7
Statische Erzeugung Syntax Type Variable [dim1][dim2].. [dimn]; Type ist ein beliebiger Datentyp Variable ist ein beliebiger Bezeichner dim1 bis dimn sind Konstanten Beispiel const int dim1 = 2, dim2 = 3; int matrix[dim1][dim2]; // matrix ist nicht initialisiert Initialisierungslisten int matrix2[dim1][dim2] = {{ 1,2,3, { 4,5,6 ; int matrix3[][dim2] = { 1,2,3,4,5,6 ; // die Länge der 1. Dimension ergibt sich Zugriff lesend int m = matrix2[0][2]; // m == 3 schreibend matrix[1][0] = m; Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-8
Dynamische Erzeugung Array erzeugen mit new und löschen mit delete[] new gibt einen konstanten Zeiger auf Typ eines Arrayelementes zurück Syntax Type (* const Variable)[dim2].. [dimn] = new Type [dim1][dim2].. [dimn]; Type ist ein beliebiger Datentyp Variable ist ein beliebiger Bezeichner dim2 bis dimn sind Konstanten Beispiele int (* const pmatrix)[7] = new int[5][7]; float (* const pfloats)[4][2] = new float[15][4][2]; delete[] pmatrix; delete[] pfloats; Zugriff schreibend pmatrix[4][6] = 99; lesend int m = pmatrix[4][6]; // m == 99 Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-9
nd-array als Funktionsparameter Die Länge der ersten Dimension muss als Parameter zusätzlich übergeben werden alle Dimensionen bis auf die erste gehen in den Typ mit ein Interpretation als Folge von Arrays möglich int matrix[2][3] kann gesehen werden als zwei Arrays mit je drei Elementen kompatibler Zeiger int (*pmatrix)[3] = matrix; Beispiel void print(int m[][dim2], int dim1) { for (int i=0; i < dim1; i++) { for (int j=0; j < dim2; j++) { cout << m[i][j] << ' '; cout << ';'; Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-10
Array von dynamischen Arrays Beispiel eines dreieckigen Arrays void create(int hoehe) { // erzeuge dynamisch Array von Zeigern auf Integer int** dreieck = new int*[hoehe]; for (int i=0; i < hoehe; i++) { dreieck[i] = new int[i+1]; // erzeuge Array von Integern // Dreieck auffüllen dreieck[i][0] = 1; dreieck[i][i] = 1; for (int j=1; j < i; j++) { dreieck[i][j] = dreieck[i-1][j-1] + dreieck[i-1][j]; Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-11
Arrays (C++) Array mit fester Grösse generische Klasse aus der STL kapselt ein C-Array fixer Länge und bietet ein Set nützlicher Array- Methoden Beispiele #include <array> string s[] = { "ab", "cd", "ef", "gh" ; const int slen = sizeof(s)/sizeof(string); array<string, slen> a; for(int i=0; i < slen; i++) { cout << "a[" << i << "] = " << a[i] << endl; a[i] = s[i]; cout << "a[" << i << "] = " << a[i] << endl; // copy by value (deep copy) Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-12
Vektoren (C++) vector generische Klasse aus der STL, entspricht der ArrayList aus Java Beispiele #include <vector> string s[] = { "ab", "cd", "ef", "gh" ; const int slen = sizeof(s)/sizeof(string); vector<string> v1; vector<string*> v2; vector<shared_ptr<string>> v3; // C++11 for(const string &t: s) { // C++11: neue for-schleife wie in Java // for(const auto& t: s) { // wäre auch möglich // for each(const string& t in s) { // Erweiterung von VC++ v1.emplace_back(t); // copy by value (deep copy) v2.push_back(new string(t)); v3.push_back(make_shared<string>(t)); Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-13
Vektoren Beispiel (Fortsetzung) Iteration im alten Stil for(vector<string*>::iterator i = v2.begin(); i!= v2.end(); ++i) { delete *i; Iteration im neuen Stil mit Lambda-Ausdruck (C++11) cout << "v3 = "; for_each(begin(v3), end(v3), [&](const shared_ptr<string>& s) { cout << s << " (" << *s << "), "; ); cout << endl; Ausgabe v3 = 003AF70C (ab), 003AF7CC (cd), 003AF764 (ef), 003AF864 (gh), Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-14
Unique Pointers (C++11) Idee unique_ptr<t> implementiert strenge Besitzverhältnisse für Zeiger mittels Verschiebesemantik pro Objekt existiert höchstens ein einziger unique_ptr<t> Problematisches Beispiel int *p = new int[5]; int *q = new int[7]; p = q; Strenge Besitzverhältnisse (strong ownership) unique_ptr<int[]> up(new int[5]); unique_ptr<int[]> uq(new int[7]); up = uq; up = move(uq); up.reset(new int[6]); // keine Zuweisung möglich // dafür Move-Semantik // gibt früheren Besitz auf und // räumt den Speicher ab Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-15
Zeigerarithmetik Voraussetzung Zeigertyp: in einer Zeigervariablen wird eine Speicheradresse verwaltet Idee aus bestehender Speicheradresse eine neue zu berechnen Erlaubte Operationen +, +=, ++ -, -=, -- Ergebnis ist vom gleichen Zeigertyp +1 bedeutet nicht + 1 Byte, sondern + Anzahl Bytes des Zielobjekts des Zeigers Typischer Einsatz durch die Bildpunkte eines Rasterbildes (Arrays) iterieren Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-16
Zeigerarithmetik: Rasterbild typedef unsigned char BYTE; const int width = 41; const int height = 31; const int bytesperline = ((width + 3)/4)*4; // verbessertes Alignment BYTE *grayimage = new BYTE[bytesPerLine*height]; BYTE *row = grayimage; for(int h=0; h < height; h++) { for(int w=0; w < width; w++) { row[w] = (BYTE)w; row += bytesperline; // *(row + w) = (BYTE)w; // Zeigerarithmetik delete[] grayimage; Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-17
Zeigerarithmetik: Beispiel 2 Programm int intarray[20]; const int isize = sizeof intarray/sizeof(int); int *pi = intarray; for (int i=0; i < isize; i++) { *pi = i; // intarray[i] = i; cout << *pi++ << ' '; cout << endl; Ausgabe 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-18
Zeigerarithmetik: Beispiel 3 Programm double darray[] = { 1.1, 1.2, 1.3, 1.4, 1.5, 1.6 ; const int dsize = sizeof darray/sizeof(double); double *pd = darray + dsize - 1; while(pd >= darray) { cout << *pd-- << ' '; cout << endl; Ausgabe 1.6 1.5 1.4 1.3 1.2 1.1 Prof. Dr. C. Stamm Programmieren in C++, FS 13 3-19