• Keine Ergebnisse gefunden

Software-Technik: Vom Programmierer zur erfolgreichen

N/A
N/A
Protected

Academic year: 2022

Aktie "Software-Technik: Vom Programmierer zur erfolgreichen"

Copied!
47
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

V 4.07; © Hon. Prof. Helmke 1 WS 2020/21

Software-Technik: Vom Programmierer zur erfolgreichen …

1. Von der Idee zur Software

2. Funktionen und Datenstrukturen 3. Organisation des Quellcodes

4. Werte- und Referenzsemantik 5. Entwurf von Algorithmen

6. Fehlersuche und –behandlung 7. Software-Entwicklung im Team

8. Abstrakte Datentypen: Einheit von Daten und Funktionalität

9. Vielgestaltigkeit (Polymorphie) 10. Entwurfsprinzipien für Software Anhang A: Die Familie der C-Sprachen

Anhang B: Grundlagen der C++ und der Java-Programmierung

(2)

Übung 4.3 für alle mit Papier und Bleistift

Implementieren Sie eine C++-Funktion mit30_40Belegen,

sodass dessen Aufruf im folgenden Programmausschnitt zu der Ausgabe vz1: 30, vz2: 40 führt.

V 4.07; © Hon. Prof. Helmke 2

WS 2020/21

void funk() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

mit30_40Belegen(vz1, vz2);

cout << "vz1: " << vz1->dauer << ", vz2: " << vz2->dauer << "\n";

delete vz1; vz1 = nullptr; delete vz2; vz2 = nullptr;

}

struct Vorgang { double dauer;

double fruehanf;

double spaetend;

};

(3)

Übung 4.3 für alle mit Papier und Bleistift

V 4.07; © Hon. Prof. Helmke 3

WS 2020/21

void main() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

#ifndef SCHON_FERTIG

vz1 = new Vorgang(); vz2 = new Vorgang();

vz1->dauer = 30; vz2->dauer = 40;

#else

// Implementieren Sie nun Schnittstelle und Code der folgenden Funktion // sodass sich die gleiche Ausgabe wie beim direkten Setzen ergibt

mit30_40Belegen(vz1, vz2);

#endif

cout << "vz1: " << vz1->dauer << ", vz2: " << vz2->dauer << "\n";

delete vz1; vz1 = nullptr; delete vz2; vz2 = nullptr;

}

struct Vorgang { double dauer;

double fruehanf;

double spaetend;

};

(4)

Ausgangssituation

V 4.07; © Hon. Prof. Helmke 4

WS 2020/21

1000 vz1 0 1004 vz2 0

Vorgang* vz1 = nullptr;

Vorgang* vz2 = nullptr;

struct Vorgang { double dauer;

double fruehanf;

double spaetend;

};

Zielsituation:

Vorgang* vz1 = nullptr;

Vorgang* vz2 = nullptr;

mit30_40Belegen(vz1, vz2);

1000 vz1 7000 1004 vz2 4488

dauer: 30 fruehanf: xxx spaetend: yyy 7000

dauer: 40 fruehanf: kk spaetend: mm 4488

(5)

Vereinfachung; ohne Funktion

V 4.07; © Hon. Prof. Helmke 5

WS 2020/21

void funk() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

vz1 = new Vorgang(); vz2 = new Vorgang();

vz1->dauer = 30; vz2->dauer = 40;

cout << "vz1: " << vz1->dauer << ", vz2: " << vz2->dauer << "\n";

delete vz1; vz1 = nullptr; delete vz2; vz2 = nullptr;

}

struct Vorgang { double dauer;

double fruehanf;

double spaetend;

};

Es sind somit drei Schritte von der Funktion zu leisten:

1. 2 Vorgänge erzeugen

2. In die Vorgänge die Dauern 30 und 40 eintragen

3. vz1 und vz2 auf die beiden erzeugten Vorgänge zeigen lassen

(6)

Nur Schritt 2 wird von der Funktion geleistet

V 4.07; © Hon. Prof. Helmke 6

WS 2020/21

void Mit30_40Belegen(Vorgang* v1, Vorgang* v2){

v1->dauer = 30; v2->dauer = 40;

}

struct Vorgang { double dauer;

double fruehanf;

double spaetend;

Es sind somit drei Schritte von der Funktion zu leisten: };

1. 2 Vorgänge erzeugen

2. In die Vorgänge die Dauern 30 und 40 eintragen

3. vz1 und vz2 auf die beiden erzeugten Vorgänge zeigen lassen

void funk() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

vz1 = new Vorgang(); vz2 = new Vorgang();

Mit30_40Belegen();

cout << "vz1: " << vz1->dauer << ", vz2: " << vz2->dauer << "\n";

delete vz1; vz1 = nullptr; delete vz2; vz2 = nullptr;

}

(7)

Auch Schritt 1 und 3 in die Funktion verlagert

/** Die Funktion fordert Speicher auf dem Heap für z1 und z2 an und belegt die Vorgangsdauern dann mit 30 bzw. 40. */

void mit30_40Belegen(Vorgang* z1, Vorgang* z2) { z1 = new Vorgang(); z1->dauer=30;

z2 = new Vorgang(); z2->dauer=40;

}

V 4.07; © Hon. Prof. Helmke 7

WS 2020/21

int main() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

mit30_40Belegen(vz1, vz2);

cout << "vz1: " << vz1->dauer << ", vz2: " << vz2->dauer << "\n";

delete vz1; vz1 = nullptr; delete vz2; vz2 = nullptr;

}

Die Änderung von z1 und z2 kommt in main gar nicht an.

z1 und z2 sind keine Ausgabeparameter.

(8)

Falsche Lösung

V 4.07; © Hon. Prof. Helmke 8

WS 2020/21

void mit30_40Belegen(Vorgang* z1, Vorgang* z2) { z1 = new Vorgang(); z1->dauer=30;

z2 = new Vorgang(); z2->dauer=40; } int main() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

mit30_40Belegen(vz1, vz2); …. }

1008 z1 7000 1012 z2 4488

dauer: 30 fruehanf: xxx spaetend: yyy 7000

dauer: 40 fruehanf: kk spaetend: mm 4488

1000 vz1 0 1004 vz2 0

Die Verbindung zwischen vz1 und z1 bzw. vz2 und z2 fehlt.

(9)

Übung 4.3 -- Lösung

/** Die Funktion fordert Speicher auf dem Heap für z1 und z2 an und belegt die Vorgangsdauern dann mit 30 bzw. 40. */

void mit30_40Belegen(Vorgang*& z1, Vorgang*& z2) { z1 = new Vorgang; z1->dauer=30;

z2 = new Vorgang; z2->dauer=40;

}

V 4.07; © Hon. Prof. Helmke 9

WS 2020/21

int main() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

mit30_40Belegen(vz1, vz2);

cout << "vz1: " << vz1->dauer << ", vz2: " << vz2->dauer << "\n";

delete vz1; vz1 = nullptr; delete vz2; vz2 = nullptr;

}

(10)

Richtige Lösung

V 4.07; © Hon. Prof. Helmke 10

WS 2020/21

void mit30_40Belegen(Vorgang*& z1, Vorgang*& z2) { z1 = new Vorgang(); z1->dauer=30;

z2 = new Vorgang(); z2->dauer=40; } int main() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

mit30_40Belegen(vz1, vz2); …. }

1008 z1 1000 1012 z2 1004

dauer: 30 fruehanf: xxx spaetend: yyy 7000

dauer: 40 fruehanf: kk spaetend: mm 4488

1000 vz1 7000 1004 vz2 4488

(11)

Lösung mit Zeiger auf Zeiger

V 4.07; © Hon. Prof. Helmke 11

WS 2020/21

void mit30_40Belegen(Vorgang** z1, Vorgang** z2) {

*z1 = new Vorgang(); (*z1)->dauer=30;

*z2 = new Vorgang(); (*z2)->dauer=40; } int main() {

Vorgang* vz1 = nullptr; Vorgang* vz2 = nullptr;

mit30_40Belegen(&vz1, &vz2); …. }

1008 z1 1000 1012 z2 1004

dauer: 30 fruehanf: xxx spaetend: yyy 7000

dauer: 40 fruehanf: kk spaetend: mm 4488

1000 vz1 7000 1004 vz2 4488

(12)

Noch eine Anwendung von

Zeiger auf Zeiger

(13)

Sinnvolle Anwendung von Zeiger auf Zeiger (char**) :

char* pch = 0;

char name[100];

cin >> name;

LosGibSpeicherHer( &pch, strlen(name)+1 );

strcpy(pch, name);

V 4.07; © Hon. Prof. Helmke 13

WS 2020/21

/* Implementierung (Definition): */

void LosGibSpeicherHer(char** ppch, int nBytes) {

*ppch = new char [nBytes]; // C++

}

//*ppch = (char*) malloc(nBytes); // C

überspringen

(14)

Wichtige Ergänzungen

zu Zeigern

(15)

Werte- und Zeigersemantik mit Strukturen in C++

struct Cmpl { double re, im;

};

. . .

Cmpl cx, cy;

cx.re = 1.5; cx.im = 3.7;

cy = cx;

. . .

V 4.07; © Hon. Prof. Helmke 15

WS 2020/21

struct Cmpl { double re, im;

};

. . .

Cmpl *cx, *cy;

. . .

cx = new Cmpl;

cy = new Cmpl;

. . .

cx->re = 1.5; cx->im = 3.7;

*cy = *cx;

. . .

delete cx; delete cy;

Wertesementik Zeigersemantik

//(*cx).re = 1.5; (*cx).im = 3.7;

(16)

Clicker-“Abstimmung“

V 4.07; © Hon. Prof. Helmke 16

WS 2020/21

/* Rückgabe von Summe und Differenz der Argumente */

int* returnArray(int i1, int i2){

int arr[2];

arr[0] = i1 + i2; arr[1] = i1 - i2;

return arr;

}

/* Ausgabe von Summe und Differenz von 1 und 2 sowie 13 und 4*/

void main() {

int* retValues1 = returnArray(1, 2);

cout << "retValues1[0, 1] : " << retValues1[0]

<< " " << retValues1[1];

int* retValues2 = returnArray(13, 4);

cout << "retValues2[0, 1] : " << retValues2[0]

<< " " << retValues2[1];

}

Was wird auf dem Bildschirm ausgegeben?

1. 3 -1 17 9 2. 17 9 17 9

3. 17 9 18510766 18510776 4. Keine Ahnung, mal so mal so

(17)

Clicker-“Abstimmung“

V 4.07; © Hon. Prof. Helmke 17

WS 2020/21

int main(){

int N=5;

int arr[100] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

arr[-1] = 12;

for (int i=1; i < N; ++i) { cout << arr[i] << " ";

} }

Ergebnis:

___ 1 __ 2 __ 3 __ 4

Und das wollen wir in den nächsten 2-3 Vorlesungen verstehen (u.a.)?

Was wird auf dem Bildschirm ausgegeben?

1. 2 3 4 5 2. 1 2 3 4 5 3. 2 3 4 5 6

4. 2 3 4 5 6 7 8 9 10 0 0

(18)

Wertesemantik bei der Funktionsrückgabe

typedef struct { double re, im; } Cmpl;

/* bzw. struct Cmpl { double re, im; }; */

. . .

Cmpl Add(Cmpl a, Cmpl b) { Cmpl temp;

temp.re = a.re + b.re; temp.im = a.im + b.im;

return temp;

} . . .

Cmpl cx, cy, cz;

cx.re = 1.5; cx.im = 3.7; cy = cx;

cz = Add(cx, cy);

. . . ;

V 4.07; © Hon. Prof. Helmke 18

WS 2020/21

Der im Stackbereich der Funktion Add reservierte

Speicherplatz für die Variable temp wird beim Beenden der Funktion automatisch wieder freigegeben.

(19)

Zeigersemantik bei der Funktionsrückgabe in C++

Cmpl* Add(Cmpl* a, Cmpl* b) { Cmpl* temp = new Cmpl;

temp->re = a->re + b->re; temp->im = a->im + b->im;

return temp;

}

V 4.07; © Hon. Prof. Helmke 19

WS 2020/21

. . .

Cmpl *cx, *cy, *cz;

cx = new Cmpl;

cy = new Cmpl;

cz = new Cmpl;

cx->re = 1.5; cx->im = 3.7; *cy = *cx;

cz = Add(cx, cy);

. . . ;

delete cx; delete cy; delete cz;

/* Der ursprünglich für cz reservierte Speicherplatz wird

nicht freigegeben, es entsteht ein Speicherleck !!! */

Warum/ Wo tritt in diesem Programmfragment

ein Problem auf?

(20)

Zeiger als Parameter in Funktionen

void funk(int i, int j) {

i= 67;

j=j+4;

}

int main() {

int par1 = 22;

int par2 = 88;

funk(par1, par2);

// par1, par2 haben hier immer // noch den Wert 22 bzw. 88.

}

void funk(int i, int *pj) { i= 67;

*pj = *pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(par1, &par2);

// par1 ist immer noch 22 // par2 ist nun aber 92.

}

V 4.07; © Hon. Prof. Helmke 20

WS 2020/21

// mit Referenzen

void funk(int i, int &pj) { i= 67; pj = pj + 4;

}

int main() {

int par1 = 22, par2 = 88;

funk(par1, par2);

}

(21)

Zeiger als Parameter in Funktionen (2)

Statt der Übergabe der Adresse von par2 kann auch direkt ein Zeiger übergeben werden:

V 4.07; © Hon. Prof. Helmke 21

WS 2020/21

int main() {

int par1 = 22;

int par2 = 88;

funk(par1, &par2);

} int main()

{

int par1 = 22;

int par2 = 88;

int * ppar2 = &par2;

funk(par1, ppar2);

}

überspringen

(22)

Clicker

void funk(int i, int *pj) { i= 67;

*pj = *pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(par1, &par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 22

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 71 3. 22 92 4. 67 92

überspringen

(23)

Clicker

void funk(int i, int *pj) { i= 67;

*pj = *pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(par1, &par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 23

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 71 3. 22 92 4. 67 92

__ 22 88 __ 67 71 _-_ 22 92 __ 67 92

überspringen

(24)

Clicker

void funk(int i, int pj) { i= 67;

pj = pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(par1, par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 24

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 71 3. 22 92 4. 67 92

überspringen

(25)

Clicker

void funk(int i, int pj) { i= 67;

pj = pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(par1, par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 25

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 71 3. 22 92 4. 67 92

_-_ 22 88 __ 67 71 __ 22 92 __ 67 92

überspringen

(26)

Clicker

void funk(int& i, int pj) { i= 67;

pj = pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(par1, par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 26

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 88 3. 22 92 4. 67 92

überspringen

(27)

Clicker

void funk(int& i, int pj) { i= 67;

pj = pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(par1, par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 27

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 88 3. 22 92 4. 67 92

__ 22 88 _-_ 67 88 __ 22 92 __ 67 92

überspringen

(28)

Clicker

void funk(int* i, int pj) {

*i= 67;

pj = pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(&par1, par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 28

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 88 3. 22 92 4. 67 92

überspringen

(29)

Clicker

void funk(int* i, int pj) {

*i= 67;

pj = pj + 4;

}

int main() { int par1 = 22;

int par2 = 88;

funk(&par1, par2);

cout << par1 << " " << par2;

}

V 4.07; © Hon. Prof. Helmke 29

WS 2020/21

Bildschirmausgabe ? 1. 22 88

2. 67 88 3. 22 92 4. 67 92

__ 22 88 _-_ 67 88 __ 22 92 __ 67 92

überspringen

(30)

Nicht typisierte Zeiger

double* px; // typisierter Zeiger

void* p; // nicht typisierter Zeiger

p = px; // richtig

px = p; // in C++ falsch, in C schlecht

px = (double*) p; // in Ordnung, C-Stil

px = reinterpret_cast<double*>(p); // in Ordnung, C++-Stil

V 4.07; © Hon. Prof. Helmke 30

WS 2020/21

• Nicht typisierte Zeiger sind mit allen typisierten Zeigern kompatibel !

• Sie können nicht dereferenziert werden.

• Sie können verwendet werden, um generisch zu programmieren!

• Was aber häufig eine unsichere Sache ist.

überspringen

(31)

Beispiel für das Verwenden nicht typisierter Zeiger

void copy(void* ziel, const void * quelle, int count) { char* y = reinterpret_cast<char*>(ziel);

char* x = reinterpret_cast<char*>(quelle);

for (i=0; i < count; ++i) {

*y = *x; ++y; ++x; } }

. . .

Somewhat a, b;

int i, j;

. . .

copy(&b, &a, sizeof(Somewhat));

copy(&j, &i, sizeof(int)); // entspricht j = i

V 4.07; © Hon. Prof. Helmke 31

WS 2020/21 überspringen

(32)

Weiteres zu Zeigern

(33)

const_cast-Operator

Der Operator const_cast erlaubt einen Zeiger auf ein konstantes Objekt in einen Zeiger auf ein nicht konstantes Objekt zu konvertieren:

const_cast ist der einzige C++-Konvertierungsoperator, der ein const-Attribut einer Variablen wegcasten kann.

V 4.07; © Hon. Prof. Helmke 33

WS 2020/21

const int size = 100;

int* ptrSize = const_cast<int*>(& size);

*ptrSize = 150; // d.h. size erhält den Wert 150

(34)

Sinnvolle Anwendung von const_cast

void Print(char* text) // Nicht sauber programmiert, da

{ // const vergessen wurde

cout << text; // besser: void Print(const char* text) }

void MeinPrint(const char* vname, const char* nname) {

Print(const_cast<char*> (vname));

cout << " ";

Print(const_cast<char*> nname);

}

V 4.07; © Hon. Prof. Helmke 34

WS 2020/21

In diesem Fall wäre es vermutlich besser, die Schnittstelle von Print zu ändern. In der Praxis ruft aber Print auch wieder eine Funktion auf, und dort wird wieder eine Funktion

aufgerufen usw.

überspringen

(35)

Sinnvolle Anwendung von const_cast mit C-Syntax

void Print(char* text) // Nicht sauber programmiert, da

{ // const vergessen wurde

cout << text; // besser: void Print(const char* text) }

void MeinPrint(const char* vname, const char* nname) {

Print((char*) (vname));

cout << " ";

Print((char*) nname);

}

V 4.07; © Hon. Prof. Helmke 35

WS 2020/21

Der Zweck des Typecast ist hier nicht erkennbar!

überspringen

(36)

Verschiedene Funktionsparameterarten in C und C++

In C und C++

• Übergabe per Wert: void f (T x)

• Übergabe per Zeiger: void f (T* x)

• Übergabe per konstantem Zeiger: void f (const T* x) Nur in C++:

• Übergabe per Referenz: void f (T& x)

• Übergabe per konstanter Referenz: void f (const T& x) Redundant für den Aufrufer:

• Übergabe per konstantem Wert: void f (const T x)

• Übergabe per konstantem Zeiger: void f (T *const x)

• Übergabe per konstantem Zeiger: void f (const T *const x)

V 4.07; © Hon. Prof. Helmke 36

WS 2020/21 überspringen

(37)

Const Zeiger

(38)

Verschiedene Rückgabearten für Funktionswerte

In C und C++:

• Rückgabe eines Werts: T f(...)

• Rückgabe eines Zeiger: T* f(...)

• Rückgabe eines Zeigers auf einen konstanten Wert: const T* f(...) Nur in C++

• Rückgabe einer Referenz: T& f(...)

• Rückgabe einer konstanten Referenz : const T& f(...) Keine Information für den Aufrufer

• const T f(...)

• T* const f(...)

• const T* const f(...)

V 4.07; © Hon. Prof. Helmke 38

WS 2020/21 überspringen

(39)

Verwendung des Attributs const bei Zeigern

Bei der Deklaration von Zeigern kann sich die Konstanz auf den Zeiger oder auf den Inhalt oder auf beides beziehen:

V 4.07; © Hon. Prof. Helmke 39

WS 2020/21

char Name1[20] = "Isernhagen";

char Name2[20] = "Meyer";

char* s1 = Name1; /* var. Zeiger, var. Inhalt */

const char* s2 = Name1; /* var. Zeiger, konst. Inhalt */

char* const s3 = Name1; /* konst. Zeiger, var. Inhalt */

const char* const s4 = Name1; /* konst. Zeiger, konst. Inhalt */

s1[0] = 'A'; /* i.O. */ /* entspricht *s1 = 'A'; */

strcpy(s1, "xxxx"); /* i.O. */

s1 = Name2; /* i.O. */

s2[0] = 'A'; /* Fehler: L-Wert gibt ein konstantes Objekt an */

strcpy(s2, "xxxx"); /* Fehler: versch. Typen fuer form. & akt. Param. */

s2 = Name2; /* i.O. */

überspringen

(40)

Verwendung des Attributs const bei Zeigern (2)

char* s1 = Name1; /* var. Zeiger, var. Inhalt */

const char* s2 = Name1; /* var. Zeiger, konst. Inhalt */

char* const s3 = Name1; /* konst. Zeiger, var. Inhalt */

const char* const s4 = Name1; /* konst. Zeiger, konst. Inhalt */

s3[0] = 'A'; /* i.O. */

strcpy(s3, "xxxx"); /* i.O. */

s3 = Name2; /* Fehler: L-Wert gibt ein konstantes Objekt an */

s4[0] = 'A'; /* Fehler: L-Wert gibt ein konstantes Objekt an */

strcpy(s4, "xxxx"); /* Fehler: versch. Typen fuer form. & akt. Param. */

s4 = Name2; /* Fehler: L-Wert gibt ein konstantes Objekt an */

V 4.07; © Hon. Prof. Helmke 40

WS 2020/21 überspringen

(41)

Zeiger - Speicherbedarf von Zeigern

Der Speicherplatz, der für einen Zeiger reserviert wird, muss eine Speicheradresse aufnehmen können.

Das bedeutet, dass ein Zeiger des Typs int und ein Zeiger auf einen Datentyp double normalerweise gleich groß sind.

Der Typ, der einem Zeiger zugeordnet ist, gibt den Inhalt und damit auch die Größe des adressierten Speicherbereichs an.

V 4.07; © Hon. Prof. Helmke 41

WS 2020/21

int* pint ; // pint belegt 4 Byte (*) double* pdouble; // pdouble belegt auch 4 Byte (*) (*) Der für Zeiger vorgesehene Speicherplatz ist natürlich implementierungsabhängig. Der Wert von 4 Byte ist der für heutige 32-Bit-Systeme in der Regel verwendete.

(42)

Beziehung zwischen Zeigern und Arrays

(43)

Beziehungen zwischen Zeigern und Arrays

Einem Zeiger kann der Name eines Arrays zugewiesen werden:

Der Arrayname entspricht damit der Adresse des ersten Arrayelementes, d.h. dem mit dem Index 0.

Ein Zeiger kann wie ein Array benutzt werden, z.B. „ptr [4] = 16;“:

V 4.07; © Hon. Prof. Helmke 43

WS 2020/21

int arr[10];

int* ptr = arr; // entspricht: int* ptr = &arr[0]

void funk(int* ptr, int arr[], int k) {

ptr [k] = arr [4];

}

int main(){

int a[14], b[25];

funk(a, &b[0], 4);

funk(&a[2], b, 2);

}

(44)

Zeiger, Adressen und Vektoren, Beispiel

#include <iostream>

#include <iomanip>

using namespace std;

int main(void) { int i;

int a[10]; /* Array fuer 10 int-Werte */

int b[10]; /* Array fuer 10 int-Werte */

int* pb; /* ein Zeiger auf einen int-Wert */

int c[9]; /* ein Array fuer 9 int-Werte */

int* pc; /* ein Zeiger auf einen int-Wert */

pb = &b[0]; /* oder auch pb = b */

pc = c; /* oder auch pc = &c[0] */

V 4.07; © Hon. Prof. Helmke 44

WS 2020/21

(45)

Zeiger, Adressen und Vektoren, Beispiel (2)

#include<iostream>

#include<iomanip>

using namespace std;

int main(void) { int i;

int a[10];

int b[10];

int* pb;

int c[9];

int* pc;

pb = &b[0];

pc = c;

for (i = 0; i < 10; ++i) { a[i] = i * i;

*pb = i * i; ++pb;

*pc = i * i; ++pc; /* Achtung: es ist nur */

} /* Speicherplatz fuer c[0] bis c[8] reserviert !!! */

pb = &b[0]; pc = c;

for (i = 0; i < 10; ++i) {

cout << setw(4) << i << setw(4) << a[i]

<< setw(4) << b[i] << setw(4) << *pb

<< setw(4) << c[i] << setw(4) << *pc

<< endl;

++pb; ++pc;

}

return 0;

}

V 4.07; © Hon. Prof. Helmke 45

WS 2020/21

(46)

Dualität zwischen Vektoren und Zeigern

• int a[10] reserviert Speicher für 10 Integer: a[0] bis a[9]

• int* pb bedeutet: der Inhalt von pb ist vom Typ int, also pb ist ein Zeiger

• Mit pb = &b[0] wird der Zeiger pb auf das erste Element gesetzt

• pb = b bewirkt das gleiche!

• Ein Vektor (Array) wird durch seine Adresse dargestelt

• Zugriff auf das i-te Element: b[i] oder *(b+i) bzw. pb[i] oder *(pb+i)

b+i bedeutet b wird typgerecht um i Schritte erhöht

• Es findet keine Bereichsüberprüfung des Index statt!

• Adressrechnungen werden ohnehin nicht überprüft

Das obige Programm funktioniert meistens, nicht immer !!!

(Es ist kein Platz für c[9] reserviert !!!)

V 4.07; © Hon. Prof. Helmke 46

WS 2020/21

(47)

Dualität zwischen Vektoren und Zeigern (2)

V 4.07; © Hon. Prof. Helmke 47

WS 2020/21

Referenzen

ÄHNLICHE DOKUMENTE

Mitglieder des Beirates Vertreter Anschrift Ministerium für Umwelt,.. Landwirtschaft und Energie des Landes

Sie dienen einer Überwachung und ermöglichen eine qualitative und quantitative, mehr oder weniger spezifische Erfassung von Schadstoffen (z.B. Schadstoffgehalte wie

Im ¨ Ubrigen gehen wir wieder davon aus, dass alle Zeiger genau vier Bytes im Arbeitsspeicher belegen... Vervollst¨ andige nun

Am Ende der ¨ Ubung mag sich der eine oder andere fragen, was man denn nun eigentlich von Zeigern hat, denn Zeiger sind erst einmal nur kompliziert.. Die Antwort ist sehr einfach:

läuft durch die array Elemente setzt das erste array Element auf 500 setzt das zweite array Element auf 1000 setzt das dritte array Element auf 3000. setzt das vierte

Lege einen speziellen Speicherbereich für jeden Aufruf einer Funktion an. In sequentiellen Programmiersprachen können diese Speicherbereiche auf dem Keller

Oft ist zu dem Zeitpunkt, an dem ein Programm geschrieben wird, noch nicht klar, wieviele Daten zu verwalten sind: sollen 100 B¨ucher oder 1.000.000 B¨ucher gespeichert werden.. Da

Ieber l3egriUe. Tstbedto- llowüel; Hendel5Ve.rhbr %Wisdlen dar Bundes:e.publlk und de.r TS&lt;hecboslowaket für die Zelt vom 1. I.. 5.2 Olfentlic:be Bekanntmachung