pdf 850 KB Stand 06/11 - Hochschule Ravensburg

Werbung
Programmieren in C
Martin Zeller
Hochschule Ravensburg-Weingarten
Raum:
E-Mail:
M. Zeller
T 110, Tel. 9760
[email protected]
[email protected]
Programmieren
1
Tagesordnung
M. Zeller
❑
Organisatorisches
❑
Ziel der Vorlesung
❑
Überblick „Programmieren“
Programmieren
2
Erwartung an Hörer (1)
M. Zeller
❑
Mitdenken, Kenntnisse prüfen
❑
Nachfragen bei Unklarheiten
❑
selbständig Arbeiten (Literatur)
❑
Tutoren und Assistenten ansprechen
❑
Lerngruppen bilden und „leben“
❑
Übungen vorbereiten
❑
üben, üben, üben
Programmieren
3
Erwartung an Hörer (2)
❑
M. Zeller
Keine Störungen
❑
keine Nebendiskussionen
❑
nicht essen
❑
Laptops nur zum Mitschreiben
(Papier ist m.E. besser)
Programmieren
4
Erwartung an Vortragenden
❑
Grundkenntisse Programmieren
vermitteln
❑
Kontext herstellen
❑
Sinnhaftigkeit vermitteln
❑
Ansprechbar sein
❑
❑
❑
M. Zeller
Programmieren
5
Aktuelles
❑
Seminar Einführung in das Studium
/Hüttenwochenende
www.hs-weingarten.de/~huette
❑
Änderungen im Stundenplan, Ersatztermin
❑
M. Zeller
....
Programmieren
6
Ziel der Vorlesung
Grundkenntisse in Programmieren vermitteln
❑
Syntax und Semantik von C
❑
Programmier-Stil
❑
Vorgehensweise der Software-Erstellung
(Ansätze)
❑
Beispiele für Algorithmen
❑
Darstellung von Daten im Rechner (Ansätze)
❑
Programmierumgebung (z.Zt. Linux)
M. Zeller
Programmieren
7
Inhalt der Vorlesung
❑
Ein- und Ausgabe: Tastatur und Bildschirm
❑
Zahlen, Berechnungen
❑
Verzweigungen
❑
Schleifen
❑
Arrays und Strings
❑
Entwurf und Dokumentation
❑
Funktionen
❑
Sichtbarkeit von Variablen
M. Zeller
Programmieren
8
Inhalt der Vorlesung
❑
Programmierstil
❑
Pointer
❑
Strukturierte Datentypen
❑
Dynamische Speicherverwaltung
❑
Zugriff auf Dateien
❑
Rekursive Funktionen
❑
Aufzählungstypen/enumerations
❑
Präprozessoranweisungen
M. Zeller
Programmieren
9
Quellenangaben
Die in der Vorlesung ausgegebenen Unterlagen
ersetzen kein Buch.
❑
Wolf, J.: C von A bis Z. Galileo Press, 2009
auch online verfügbar
❑
Darnell, Margolis: C A Software Engineering
Approach. Springer Verlag, 1996
❑
Schildt, H.: C: The Complete Reference.
Osborne McGraw-Hill, 2000.
M. Zeller
Programmieren
10
Einführung
Aufbau eines Entwicklungs-Computers:
❑
❑
Hardware
❑
Betriebssystem
❑
Programmierumgebung
M. Zeller
Programmieren
11
Rechnerarchitektur
❑
Die „von Neumann“ Rechnerarchitektur
❑
Gemeinsamer Speicher für Daten und
Programme
❑
Kommunikation über einen gemeinsamen Bus
M. Zeller
Programmieren
12
Betriebssystem/Systemsoftware
❑
Verwaltet bzw. koordiniert die verschieden
Hardware-Komponenten
❑
Startet und beendet Programme
❑
Benutzerschnittstelle (Desktop, Shell, DOS-Eing.)
❑
Bibliotheken, Komponenten
❑
Entwicklungs-Tools (Editor, Compiler, Linker)
❑
Dienstprogramme
(Nutzerverwaltung, Netzwerk-Programme, etc.)
M. Zeller
Programmieren
13
Programmierumgebung
Einzelkomponenten
❑
❑
Editor
❑
Compiler
❑
Linker
❑
Bibliotheken
❑
Debugger, Versionskontroll-Tool, Test-Tool, etc.
❑
M. Zeller
IDE (Integrated Development Environment)
Alle Komponenten unter einer GUI
(Graphical User Interface)
Programmieren
14
Arbeitsschritte
(1) Aufgabenstellung
(2) Problemlösung
(3) Programm
analysieren
entwerfen (ggf. zurück zu (1))
erstellen (ggf. zurück zu (2) oder (1))
(4) Progamm
testen
(ggf. zurück zu (3), (2) oder (1))
M. Zeller
Programmieren
15
Programmieren i.e. Sinne
M. Zeller
Programmieren
16
Ein Mini-Programm
/**************************************************** * Modul hello.c : Ein sehr schlichtes C­Programm
* Aufgabe: Das Programm gibt eine Begruessung auf * dem Bildschirm aus ****************************************************/
/*
* Die Bibliothek stdio.h enthaelt Funktionen fuer
* die Ein­ und Ausgabe von Daten; hier: printf()
* (stdio steht fuer standard input output)
*/ #include <stdio.h> /* 01 */ int main(void){ /* 02 */
printf("\n Hello World \n"); /* 03 */
printf(" Aller Anfang ... \n\n"); /* 04 */
return 0; /* 05 */
} /* 06 */
M. Zeller
Programmieren
17
Ein- und Ausgabe
#include <stdio.h> int main(void){ int sum;
int fix_number = 12345; /* 01 */
int input_number; /* 02 */
printf("\n fix_number: %d \n", fix_number); /* 03 */
printf(" input_number: %d \n", input_number); printf(" Bitte Zahl eingeben: "); scanf("%d", &input_number); /* 04 */ printf(" input_number: %d \n", input_number); sum = input_number + fix_number; /* 05 */
printf(" Summe: %d \n", sum); return 0; } M. Zeller
Programmieren
18
ProgrammAufrufe
>a.out
fix_number: 12345
input_number: 1073795440
Bitte Zahl eingeben: 4321
input_number: 4321
Summe: 16666
>a.out
fix_number: 12345
input_number: 1073795440
Bitte Zahl eingeben: ­123
input_number: ­123
Summe: 12222
>a.out
fix_number: 12345
input_number: 1073795440
Bitte Zahl eingeben: xyz
input_number: 1073795440
Summe: 1073807785 M. Zeller
Programmieren
19
Variable für Zahlen
Der Typ einer Variablen bestimmt, wie groß der
Speicherplatz der Variable ist und wie der
Inhalt der Variable interpretiert wird.
Bsp.:
int number; Diese Anweisung vereinbart eine Variable mit
Bezeichner 'number' und dem Datentyp 'int'.
Der Inhalt der Variable 'number' wird als ganze
Zahl interpretiert. (In arithmetischen und
logischen Ausdrücken)
M. Zeller
Programmieren
20
Variable für Zahlen
Jede Variable muss mit einem Wert belegt
werden, bevor sie lesend verwendet wird.
Bsp.:
int number;
Diese Anweisung weist der Variable 'number'
keinen Wert zu. Der Wert der Variablen ist
daher quasi zufällig (s. Bsp. oben).
Deklaration mit Initialisierung:
int number = 7;
int num_count = number * 4; M. Zeller
Programmieren
21
Berechungen
Beispiel für eine Berechnung:
sum = input_number + fix_number;
Allgemeine Form der Zuweisung:
Variable = Ausdruck;
Ein Ausdruck kann u.a. eine Variable sein, eine
Konstante oder ein arithmetischer bzw. ein
logischer Ausdruck.
result = number;
result = number * (number + 1) – number/2;
M. Zeller
Programmieren
22
Programm mit Schleife
#include <stdio.h>
int main(void){
int limit;
int i = 0; printf("\n Bitte obere Grenze eingeben: “);
Scanf(“ %d“, &limit);
while (i < limit){
printf(" Zahl i = %d\n", i);
i = i + 4; }
printf(" Zahl i = %d\n", i);
return 0;
}
M. Zeller
Programmieren
23
Programm mit For-Schleife
#include <stdio.h>
int main(void){
int sum = 0;
int loops = 10;
int i; printf("\n Start Summieren mit %d\n", sum);
for(i = 0; i < loops; i = i + 1){
sum = sum + i;
printf(" Summe = %d \n", sum);
}
return 0;
}
M. Zeller
Programmieren
24
Elementare Datentypen
Typ-Name
üblicher(!) Wertebereich
char
ganze Zahl
­128 bis 127
short
ganze Zahl
­32768 bis 32767
int
ganze Zahl
­231 bis 231­1
long
ganze Zahl
­231 bis 231­1 unsigned char ganze Zahl
0 bis 255
unsigned short ganze Zahl
0 bis 65535
unsigned int
ganze Zahl
0 bis 232­1
unsigned long
ganze Zahl
0 bis 232­1 float
Kommazahl
­3.4*1038 bis 3.4*1038
double
Kommazahl
­1.7*10308 bis 1.7*10308
long double
Kommazahl
­1.1*104932 bis 1.1*104932
M. Zeller
Programmieren
25
Darstellung von Binärzahlen
2-er Komplement für ganze Zahlen mit n Bits
❑
❑
Eine Zahl k mit 0 ≤ k < 2n-1 wird als positive
Zahl k interpretiert.
Eine Zahl k mit 2n-1 ≤ k < 2n wird als
n
(negative) Zahl k-2 interpretiert.
M. Zeller
Programmieren
26
Darstellung von Binärzahlen
Umrechnung k ↔ -k; beide Richtungen gleich!
1) Alle Bits invertieren
2) Zum Ergebnis 1 addieren, eventuellen
Überlauf ignorieren
●
Eindeutige Darstellung der 0 (alle Bits 0)
●
Addition ist unabhängig vom Vorzeichen
M. Zeller
Programmieren
27
Operatoren
C enthält u. a. folgenden Operatoren:
M. Zeller
=
+
­
*
/
%
Zuweisung
Addition
Subtraktion
Multiplikation
Division Modulo (Rest der ganzzahligen Division)
>
<
==
!=
>=
<=
&&
||
!
Vergleich auf größer
Vergleich auf kleiner
Vergleich auf gleich
Vergleich auf ungleich Vergleich auf größer oder gleich
Vergleich auf kleiner oder gleich
Logische und­Verknüpfung von log. Ausdrücken
Logische oder­Verknüpfung von log. Ausdrücken
Logische Negation Programmieren
28
Programm mit Schleife
#include <stdio.h>
int main(void){
int number;
printf("\n Bitte Anfangswert eingeben: ");
scanf("%d", &number);
while (number < 1000000){
number = number * number; printf(" Zahl = %d\n", number); }
printf(" Zahl = %d\n", number); return 0;
}
M. Zeller
Programmieren
29
Ausgabe der while-Schleife
Anfangswert: 3
Ausgabe:
9, 81, 6561, 43046721
Anfangswert: 5
Ausgabe:
25, 625, 390625, -2030932031
-2052264063 , -1083564287,
781532673
Anfangswert: 4
Ausgabe:
16, 256, 65536, 0, 0, 0, . . .
. . . Endlos-Schleife
M. Zeller
Programmieren
30
Verzweigung
#include <stdio.h> int main(void){ int number; printf("\n Bitte Zahl eingeben: "); scanf("%d", &number); if (number > 0){ printf("\n Eingabe war positiv; Zahl: %d", number); printf("\n zweite Anweisung im Block \n"); return 0; }
if(number == 0){
printf("\n Eingabe war null; Zahl: %d\n", number);
}else{
printf("\n Eingabe war negativ; Zahl: %d\n", number);
} return 0; } M. Zeller
Programmieren
31
Switch
#include <stdio.h> int main(void){ int number; printf("\n Bitte Zahl eingeben: "); scanf("%d", &number); switch (number){
case 1: printf(" Ausgang 1 \n");
break;
case 2: printf(" Ausgang 2 \n");
break;
case 5: printf(" Ausgang 5 \n");
break;
default: printf(" default Ausgang \n");
}
return 0; } M. Zeller
Programmieren
32
Grundbausteine
❑
Variable: Bezeichner, Datentyp, Wert
❑
Ausdruck: Zahlen, Bezeichner, Operatoren
❑
Entscheidungen/Verzweigungen:
if-, switch-Anweisung
❑
Schleifen: while- und for-Anweisung
❑
Eingabe-und Ausgabe-Funktionen:
scanf() und printf()
❑
Kommentare /*
M. Zeller
*/ und // in C99
Programmieren
33
Datentyp Array (1)
#include <stdio.h>
int main(void){
const int length = 10;
int numbers[length]; int i; for(i = 0; i < length; i = i + 1){
numbers[i] = i * i;
printf("numbers[%d] = %d\n", i, numbers[i]);
}
return 0;
}
M. Zeller
Programmieren
34
Datentyp Array (2)
❑
Eine Variable vom Typ Array speichert
mehrere Werte eines Basis-Datentyps.
❑
Auf die einzelnen Werte des Basis-Datentyps
wird durch einen Index zugegriffen.
❑
Erste Wert eines Arrays: Index = 0
letzter Wert: Index = (Anzahl - 1)
❑
Die Sprache C überprüft nicht die
Bereichsgrenzen. (i. Ggs. zu z.B. Java, Pascal)
M. Zeller
Programmieren
35
Datentyp Array (3)
❑
Variable vom Typ Array können nicht als
Ganzes zugewiesen oder verglichen werden
❑
Zuweisung/Vergleich der einzelnen Werte in
einer Schleife
❑
Die Länge eines Array kann i.Allg. nicht aus
der Array-Variable bestimmt werden.
❑
Die Länge eines Array kann über eine
Konstante oder über eine Ende-Markierung
bestimmt werden.
M. Zeller
Programmieren
36
Datentyp Array (4)
Adresse von numbers[i]:
Anfangsadresse + i * Größe eines Elements
M. Zeller
Programmieren
37
Array als String (1)
int main(void){
const int length = 10;
char inString[length]; int i; printf("\n Beliebige Eingabe mit <return> abschliessen:");
scanf("%9s", inString);
for(i = 0; i < length; i = i + 1){
printf(" i = %d, inString[%d] = %c,", i, i, inString[i]);
printf(" als int: inString[%d] = %d\n", i, inString[i]);
} printf(" InputString: %s \n", inString);
return 0;
}
M. Zeller
Programmieren
38
Array als String (2)
Beliebige Eingabe mit <return> abschliessen:ABCDEFGHIJ
i = 0, inString[0] = A, als int: inString[0] = 65
i = 1, inString[1] = B, als int: inString[1] = 66
i = 2, inString[2] = C, als int: inString[2] = 67
i = 3, inString[3] = D, als int: inString[3] = 68
i = 4, inString[4] = E, als int: inString[4] = 69
i = 5, inString[5] = F, als int: inString[5] = 70
i = 6, inString[6] = G, als int: inString[6] = 71
i = 7, inString[7] = H, als int: inString[7] = 72
i = 8, inString[8] = I, als int: inString[8] = 73
i = 9, inString[9] = , als int: inString[9] = 0
InputString: ABCDEFGHI
M. Zeller
Programmieren
39
Array als String (3)
❑
Strings (Wörter bzw. Text) werden in C als
Arrays mit Basis-Datentyp char realisiert.
❑
Das letzte Zeichen jedes Strings sollte/muss
den Wert 0 haben.
(Ende-Markierung von Strings in C)
❑
String-Funktionen ohne Längenbegrenzung
öffnen Sicherheitslücken
(z.B. gets(), strcpy(), . . . )
❑
Neuere Programmiersprachen unterstützen
die Verarbeitung von Strings wesentlich
besser.
M. Zeller
Programmieren
40
Programmieren lernen
M. Zeller
❑
Vorlesung: Programm => Aufgabe
❑
Übung: Aufgabe =>Programm
❑
Bausteine sammeln
❑
Zusammenbau üben
Programmieren
41
Algorithmen
Ein Algorithmus ist eine Verfahrensvorschrift,
die eine Menge von Objekten durch
Operationen aus einem definierten
Anfangszustand in einen definierten
Endzustand bringt.
❑
Kochrezept, Bastelanleitung
❑
Mathematische Definition
(u.a. Turing-Maschine)
M. Zeller
Programmieren
42
Beschreibung von Algorithmen
❑
Programmiersprache: C, Java, Basic, Pascal . . .
❑
Pseudo-Code (nicht standardisiert):
while budget > 0 do
buy more things
endwhile
❑
Nassi-Shneiderman-Diagramm (Struktogramm)
❑
Ablaufdiagramm, Ablaufplan
❑
UML (Unified Modelling Language)
s. Vorlesung Softwareengineering
M. Zeller
Programmieren
43
Ablaufdiagramm
Block bzw. Sequenz
M. Zeller
Programmieren
44
Ablaufdiagramm, Verzweigung
M. Zeller
Programmieren
45
Ablaufdiagramm, Schleife
M. Zeller
Programmieren
46
Mehrfachverzweigung (switch)
M. Zeller
Programmieren
47
Elemente des Entwurfs
Prozedurale Systeme:
Ablaufdiagramme bzw. Pseudocode
❑
Sequenz von Verarbeitungsschritten
❑
Einfach- oder Mehrfachverzweigung
❑
Schleifen
Größere objektorientierte Systeme: UML
M. Zeller
Programmieren
48
Regeln der strukturierten
Programmierung
❑
Ein Strukturblock hat genau einen Eingang und
genau einen Ausgang.
(Mehrere Ausgänge können m.E. sinnvoll sein)
❑
Aneinanderreihen und Schachteln von
Strukturblöcken ergibt wieder einen
Strukturblock.
❑
Jeder Strukturblock muss mindestens einmal
erreicht werden.
❑
Elementare Strukturblöcke sind Zuweisung und
Funktionsaufruf.
M. Zeller
Programmieren
49
Praktikum Programmieren
❑
Überarbeiten heißt i. Allg:
Es sind nicht alle Fehler angestrichen. Bitte
prüfen Sie selbst nochmals die gesamte
Teilaufgabe.
❑
Ablaufdiagramm:
Bitte zeichnen Sie sie Ablaufdiagramme mit
den vorgegebenen Symbolen und sehr
detailliert. Jede Variable muss dargestellt
werden, ebenso Eingaben und Ausgaben.
M. Zeller
Programmieren
50
Größter Gemeinsamer Teiler
Algorithmus: Euklid 300 v. Ch.
Gegeben: Zwei natürliche Zahlen x und y
Gesucht: Größter gemeinsamer Teiler von x, y
Wiederhole bis x gleich y ist:
❑
❑
Wenn x größer als y ist: ziehe y von x ab und
weise das Ergebnis x zu.
❑
Wenn y größer als x ist: ziehe x von y ab und
weise das Ergebnis y zu.
❑
M. Zeller
Der Wert den x und y jetzt haben ist der
größte gemeinsame Teiler.
Programmieren
51
GGT-Algorithmus Ablaufdiagramm
M. Zeller
Programmieren
52
GGT: Ein C-Programm
int main(void){
int zahl1 = 61*31*13*7;
int zahl2 = 61*41*17*7; int current1 = zahl1;
int current2 = zahl2; while (current1 != current2){
if (current1 > current2){
current1 = current1 ­ current2; }else{
current2 = current2 ­ current1;
}
}
printf("\n Der GGT der Zahlen %u und %u", zahl1, zahl2);
printf(" lautet: %u \n\n", current1);
return 0;
}
M. Zeller
Programmieren
53
Funktionen
int ggt(int par1, int par2){
while (par1 != par2){
if (par1 > par2){
par1 = par1 ­ par2; }else{
par2 = par2 – par1; }
}
return par1; }
int main(void){
int num1 = 61*31*13*7;
int num2 = 61*41*17*7; int ggt_result;
ggt_result = ggt(num1, num2); printf("\n Der GGT der Zahlen %d und %d", num1, num2); printf(" lautet: %d \n\n", ggt_result);
return 0;
}
M. Zeller
Programmieren
54
Funktionen
long int pow(unsigned int base, unsigned int exponent){
long int result = 1; int i; printf("\n Aufruf Funktion pow(%d, %u)", base, exponent);
for(i = 0; i < exponent; i = i + 1){
result = result * base; }
return result;
}
int main(void){
unsigned int b = 3; unsigned int result1;
unsigned int result2;
result1 = pow(b, 3);
result2 = pow(result1, 2);
printf("\n result1 = %u, " , result1);
printf(" result2 = %u \n\n", result2);
return 0;
}
M. Zeller
Programmieren
55
Funktionen
❑
Eine Funktion fasst mehrere Anweisungen
zusammen, so dass diese Anweisungen mit
einem Aufruf der Funktion ausgeführt
werden.
❑
Eine Funktion benötigt i.a. Aufrufparameter
und liefert i.a. einen Rückgabewert zurück.
❑
Bibliotheksfunktionen z.B. printf(), scanf()
❑
Eigene Funktionen (s. Übungen)
M. Zeller
Programmieren
56
Prototyp einer Funktion
❑
❑
Eine Funktion ist für den Nutzer durch
folgende Attribute charakterisiert (Prototyp):
❑
Name der Funktion (muss in C im gesamten
Programm eindeutig sein)
❑
Datentypen der Parameter,
die Namen sind optional
❑
Datentyp des Rückgabewerts
Bsp.: char []strncpy(char [], char [], int)
Prototyp der Funktion strncpy().
M. Zeller
Programmieren
57
Signatur einer Funktion
❑
❑
Die Signatur einer Funktion durch folgende
Attribute gegeben:
❑
Name der Funktion
❑
Datentypen der Parameter,
die Namen sind optional
Bsp.: strncpy(char [], char [], int)
Signatur der Funktion strncpy().
Die Begriffe Prototyp und Signatur werden
teilweise auch Synonym verwendet.
M. Zeller
Programmieren
58
Implementierung einer Funktion
❑
Die Implementierung einer Funktion:
❑
Name der Funktion
❑
Namen und Datentypen der Parameter
❑
Datentyp des Rückgabewerts
❑
Ausführbarer Programmcode
Bsp.:
char[] strncpy(char s1[], char s2[], int n){
int i = 0;
while ((s2[i] != 0) && (i < n)){
: }
} M. Zeller
Programmieren
59
Aufruf einer Funktion
1) Die Werte der aktuellen Parameter werden in
die formalen Parameter kopiert.
call-by-value
2) Der Programmcode der Funktion wird
ausgeführt.
3) Der Rückgabewert wird zurückgegeben.
D.h. der Funktionsaufruf wird durch den
Rückgabewert ersetzt (innermost rewriting).
M. Zeller
Programmieren
60
Arrays als Parameter
❑
Die Anfangsadresse des Arrays wird als
Parameter übergeben.
❑
Die Anfangsadresse kann nicht verändert
werden.
❑
Die einzelnen Elemente des Arrays können
verändert werden (Call By Reference).
M. Zeller
Programmieren
61
Sinn und Zweck von Funktionen
❑
Jeder Teil eines Programms soll einen
treffenden Namen erhalten.
❑
Die Komplexität des Programms soll
minimiert werden.
(DRY – don't repeat yourself)
❑
Die Wartbarkeit des Programms wird
verbessert:
❑
Test
❑
Modifikation
❑
Kommentar bzw. Dokumentation
M. Zeller
Programmieren
62
Sinn und Zweck von Funktionen
❑
Funktionen sollten kürzer als ca. 50 Zeilen
sein (ohne Kommentar)
❑
Funktion sollten maximal 5 - 10 Variable
enthalten
❑
Kontrollstrukturen sollten höchstens drei
Stufen tief verschachtelt sein
M. Zeller
Programmieren
63
Deklarationen
❑
Deklaration: Objekt (Funktion oder Variable)
bekannt machen.
❑
Definition: Deklaration und Objekt anlegen.
(Anweisungen ablegen, Speicher reservieren)
❑
Funktionen: Deklaration durch Prototyp
❑
Variablen: Deklaration durch
z.B. extern int globalCount; ❑
Jedes deklarierte Objekt muss irgendwo im
Programm definiert sein.
M. Zeller
Programmieren
extern 64
Funktionen verteilt auf Dateien (1)
/* Datei: ggtmain.c */
#include <stdio.h>
extern int ggt(unsigned int, unsigned int); int main(void){
int length = 5;
int numbers[] = {2310, 14, 35, 70, 210}; int i;
int ggt_result = numbers[0];
for(i = 1; i < length; i = i + 1){
ggt_result = ggt(ggt_result, numbers[i]); }
printf("\n Der GGT der Zahlen ");
for(i = 0; i < length;i = i + 1){
printf(" %u,", numbers[i]);
}
printf(" lautet: %u \n\n", ggt_result);
return 0;
}
M. Zeller
Programmieren
65
Funktionen verteilt auf Dateien (2)
/* Datei: ggtfun.c */ int ggt(int number1, int number2){
while (number1 != number2){
if (number1 > number2){
number1 = number1 ­ number2; }else{
number2 = number2 ­ number1;
}
}
return number1; }
M. Zeller
Programmieren
66
Funktionen verteilt auf Dateien (3)
❑
Eine Funktion kann in einer eigenen Datei
stehen.
❑
Eine Funktion kann nicht über mehrere
Dateien verteilt sein.
❑
In einer Datei kann mehr als eine Funktion
stehen.
❑
Eine Funktion muss dem Compiler erst
bekannt sein, bevor sie verwendet (d.h.
aufgerufen) werden kann. (Definition oder
Deklaration durch Prototyp)
M. Zeller
Programmieren
67
Funktionen verteilt auf Dateien (4)
❑
❑
❑
Header-Datei: name.h
❑
Definition von Datentypen (später)
❑
Deklaration von Funktionen (Prototypen)
Programm-Datei: name.c
❑
include der Header-Datei name.h
❑
Definitionen/Implementierung der Funktionen
Andere Programm-Dateien (xyz.c)
❑
M. Zeller
include der Header-Datei und Aufruf der Funktionen
Programmieren
68
Funktionen verteilt auf Dateien (5)
❑
Modul:
Header-Datei und Implementierungs-Datei
Ziel der Verteilung auf Dateien
❑
Parallele Bearbeitung von Programmteilen
❑
Inkrementelles Compilieren
❑
Abspaltung von Bibliotheken;
ggf. „shared“ (zur Laufzeit)
M. Zeller
Programmieren
69
Sichtbarkeit von Variablen
int foo(int x, int b){
b = 2 * x + b; return b;
}
int main(void){ int b = 1;
int c = 2; int result;
if (b < c){
int b = 3; result = foo(b, c);
}
printf("\n b = %d, result = %d, \n ", b, result);
return 0;
}
M. Zeller
Programmieren
70
Sichtbarkeit von Variablen
❑
Ist in einem Block ein innerer Block
eingeschachtelt, so sind die Variablen des
äußeren Blocks im inneren sichtbar (aber
nicht umgekehrt).
❑
Ist im inneren Block eine Variable definiert,
die den gleichen Namen besitzt, wie eine
Variable des äußeren Blocks, so überdeckt
die innere Variable die äußere.
M. Zeller
Programmieren
71
Sichtbarkeit von Variablen
❑
Ein formaler Parameter einer Funktion erhält
den Wert der beim Aufruf eingesetzten
Variablen bzw. des eingesetzten Ausdrucks (call
by value).
❑
Ein formaler Parameter einer Funktion ist nur
innerhalb der Funktion sichtbar.
❑
Die beim Aufruf eingesetzte Variable wird durch
die Funktion nicht verändert.
(Arrays: Parameter ist die Adresse des Arrays.
Die Adresse kann nicht verändert werden, der
Inhalt der Array-Elemente sehr wohl: call by
reference)
M. Zeller
Programmieren
72
Globale Variablen
/* Datei useFoo.c */
#include <stdio.h>
int global = 0; /* define global variable */
int main(void){
int result = 0;
int i;
for(i = 0; i < 4; i = i + 1){
result = foo(i);
global = global + 1;
printf(" result = %d, \n", result);
}
return 0;
}
M. Zeller
Programmieren
73
Globale Variablen
/* Datei defineFoo.c */
#include <stdio.h>
extern int global; /* declare global variable */
int foo(int x){
x = x * global; /* use global variable */ return x;
}
M. Zeller
Programmieren
74
Überdeckung von Variablen
const int x = 1; void printX(){
printf("\n in printX(): x = %d \n\n", x);
return;
}
int main(void){
int x = 2;
int i;
for(i = 0; i < 2; i = i + 1){
int x = 3;
printf("\n in for(){...}: x = %d", x);
}
printf("\n in main(){...}: x = %d", x);
printX();
return 0;
}
M. Zeller
Programmieren
75
Globale Variablen und
Konstanten
❑
Globale Variable sollten möglichst nicht
verwendet werden, da sie die Wartbarkeit des
Programms verschlechtern.
❑
Konstanten, die in mehr als einer Funktion
verwendet werden, sollten dagegen global
definiert werden (z.B. mathematische Konstanten
oder auch programmspezifische Konstanten).
const double pi = 3.1415 . . . ;
const int lineLength = 128; M. Zeller
Programmieren
76
Globale Variable mit File Scope
static int global = 0;
int foo(int x){
x = x * global; return x;
}
int main(void){
int result = 0;
int i; for(i = 0; i < 4; i = i + 1){
result = foo(i);
global = global + 1; printf(" result = %d, \n", result);
}
return 0;
}
M. Zeller
Programmieren
77
Sichtbarkeit von Variablen
❑
Eine Variable, die nicht innerhalb eines
Blocks deklariert ist, ist eine globale
Variable.
❑
Eine globale Variable, die static deklariert
ist, ist in der gesamten Datei sichtbar.
❑
Eine globale Variable, die nicht static
deklariert ist, ist im gesamten Programm
sichtbar.
M. Zeller
Programmieren
78
Lebensdauer von Variablen
❑
Die Lebensdauer einer Variablen, die nicht
„static“ deklariert wurde, ist auf den Block
begrenzt, in dem sie deklariert wurde. (Wie
Sichtbarkeit)
❑
Die Lebensdauer einer „static“ deklarierten
Variablen ist auf die Programmlaufzeit begrenzt.
D.h. der Wert der Variablen bleibt erhalten, auch
wenn das Programm den Block verlässt, in dem
die Variable deklariert wurde.
M. Zeller
Programmieren
79
Lebensdauer von Variablen
❑
Eine Variable mit Initialisierung, die nicht
„static“ deklariert wurde, wird jedesmal neu
initialisiert, wenn „ihr“ Block erreicht wird.
❑
Eine „static“ deklarierte Variablen mit
Initialisierung wird nur einmal - beim
Programmstart - initialisiert.
M. Zeller
Programmieren
80
Implizite Typ-Konvertierung (1)
❑
Der Datentyp eines Ausdrucks ergibt sich aus
folgenden Regeln:
❑
Variable vom Typ char bzw. short werden in
einem Ausdruck wie folgt konvertiert:
Ausgangs-Typ
Ziel-Typ
(signed) char int unsigned char int (signed) short int unsigned short unsigned int (Falls short = int) unsigned short int (sonst) M. Zeller
Programmieren
81
Implizite Typ-Konvertierung (2)
In C sind die elementaren Datentypen wie folgt
geordnet:
int < unsigned int < long int < unsigned long
unsigned long < float < double < long double
Werden zwei Ausdrücke durch einen
arithmetischen Operator verknüpft, so ist der
Typ des dadurch entstehenden Ausdruck der
'größere' der beiden Typen der beteiligten
Ausdrücke.
M. Zeller
Programmieren
82
Programmier-Stil
Stil: Durch spezifische Auswahl der
lexikalischen und grammatikalischen Mittel
gekennzeichnete Verwendungsweise der
Sprache.
Ziel eines guten Programmier-Stil ist es, leicht
verständliche Programme zu schreiben.
(Stichwort Wartbarkeit)
M. Zeller
Programmieren
83
Programmier-Stil (2)
Leitsatz: Prinzip der geringsten Überraschung
❑
Kommentare
❑
Bezeichner: Namen und Schreibweise von
Variablen und Funktionen
❑
Text-Layout: Einrückungen und Klammern
❑
Schachtelungstiefe von Blöcken
❑
Implizite Typ-Konvertierung
M. Zeller
Programmieren
84
Programmier-Stil (3)
Kommentar: Telegramm-Stil, Aktivsätze
❑
Module, Funktionen:
❑
❑
❑
❑
Sinn und Zweck, wie zu verwenden
Voraussetzungen, Grenzen, Ergebnis
Algorithmen und innerer Aufbau
Blöcken, Zeilen:
❑
❑
Was soll erreicht werden
Ggf.: Wie wird das Ziel erreicht
Nur die nicht offensichtlichen Dinge kommentieren.
M. Zeller
Programmieren
85
Programmier-Stil (4)
Einheitliche Bezeichner für Variable:
❑
❑
Namen muss Inhalt bezeichnen, bevorzugt englisch
Zusammengesetzte Namen:
xx_yy (üblich in C) oder xxYy (üblich in C++, Java)
Einheitliche Bezeichner für Funktion:
❑
Standardname: anweisungObjekt(), z.B. drawLine().
❑
Wenn der Teil 'anweisung' offensichtlich ist, kann er
entfallen (z.B. mean(...) statt computeMean(...) )
❑
Wenn der Teil 'Objekt' offensichtlich ist, kann er
entfallen (z.B. max(int x, int y) statt maxNumber(...) )
M. Zeller
Programmieren
86
Programmier-Stil (5)
Text-Layout: Einrückung, Klammern
Beginn eines Blocks{
Anweisung1;
Beginn eines geschachtelten Blocks{
Beginn eines tiefer geschachtelten Blocks{
Anweisung2;
Anweisung3; }
Anweisung4;
} Anweisung5; }
M. Zeller
Programmieren
87
Programmier-Stil (6)
Schachtelungstiefe
❑
Normale Schachtelungstiefe: 2
❑
Nur in Sonderfällen 3 oder evtl. mehr
Komplexer Blöcke können in Funktionen
aufgespalten werden.
M. Zeller
Programmieren
88
Programmier-Stil (7)
Implizite Typkonvertierung
❑
Nur für Typerweiterungen innerhalb von GanzahlTypen bzw. innerhalb von Gleitkomma-Typen
Alternativen:
❑
Explizite Konvertierung (cast), z.B. x = (float) y / z
❑
Funktionen (und cast), z.B. n = (int) floor(y * z + 0.5)
M. Zeller
Programmieren
89
Programmier-Stil (8)
Sichtbarkeit von Variablen
❑
Variablen sollen nur in dem Block sichtbar sein, in
dem sie benötigt werden (d.h. möglichst lokal).
❑
Variablen sollen so benannt werden, dass sie sich
nicht gegenseitig überdecken.
❑
Verwenden Sie keine globalen Variablen.
M. Zeller
Programmieren
90
Programmier-Stil (9) Funktionen
❑
Eine Funktion soll eine klar abgegrenzte
Aufgabe erfüllen.
(Eine Sache richtig machen.)
❑
Eine Funktionen soll nur ein Ergebnis liefern
und ggf. Aufrufparameter ändern (aber keine
globalen Variablen).
❑
Eine Funktion sollte nicht länger als ca. 50
Zeilen sein (ohne Kommentar).
❑
Eine Funktion sollte maximal 5-10 Variable
verwenden.
M. Zeller
Programmieren
91
Zeiger/Pointer (1)
#include <stdio.h>
int main(void){
float num = 1.234;
float *numPtr; numPtr = # printf(" der Wert von num ist: %7.3f \n", num);
printf(" die Adresse von num ist: %u \n", numPtr); printf(" Wert in Adresse von num: %7.3f \n", *numPtr);
*numPtr = num + 4.321; printf("\n der Wert von num ist: %7.3f \n", num);
printf(" die Adresse von num ist: %u \n", numPtr); printf(" Wert in Adresse von num: %7.3f \n", *numPtr);
return 0;
} M. Zeller
Programmieren
92
Zeiger/Pointer (2)
float size;
“normale“ float-Variable
float *numPtr;
Zeiger (Pointer) auf eine
float-Variable
numPtr = &size;
Der Pointer erhält als Wert die
Adresse der Variablen size.
Der Pointer zeigt nun auf size.
M. Zeller
Programmieren
93
Zeiger/Pointer (3)
int height;
int width;
int *numPtr1;
int *numPtr2 = NULL;
NULL ist eine Konstante
numPtr1 = &height;
*numPtr1 = 42;
Die Variable, auf die numPtr1 zeigt,
erhält den Wert 42 zugewiesen.
width = *numPtr1;
Die Variable erhält den Wert, auf
den numPtr1 zeigt, zugewiesen.
numPtr2 = numPtr1;
Der Pointer numPtr2 zeigt jetzt
ebenfalls auf die Variable height.
M. Zeller
Programmieren
94
Call By Reference
void increment(int *num){
*num = *num + 1; num = num + 1;
return; }
int main(void){
int testNum = 3;
int *testNumPtr = &testNum;
printf("\n Wert testNum: %d ",testNum); increment(testNumPtr);
increment(&testNum);
printf("\n Wert testNum: %d \n",testNum); return 0;
}
M. Zeller
Programmieren
95
Call By Reference
❑
❑
Pointer/Adresse als Übergabe-Parameter an
Funktion
❑
Die Funktion kann das Datenelement verändern
❑
Die Änderungen sind außerhalb der Funktion sichtbar
Den Pointer selbst kann die Funktion zwar
ändern, diese Änderung wirkt sich aber nur
innerhalb der Funktion aus. (Der Pointer wird
bei der Übergabe kopiert.)
M. Zeller
Programmieren
96
Pointer und Strings (1)
int main(void){
char lastName[] = "Mueller­Luedenscheid";
char *anyText; anyText = lastName; printf(" lastName: %s \n", lastName);
printf(" anyText: %s \n", anyText);
anyText[8] = 'M'; printf(" lastName: %s \n", lastName);
lastName[0] = 'L'; printf(" anyText: %s \n", anyText);
anyText = anyText + 8;
printf(" anyText: %s \n", anyText);
printf(" anyText als char: %c \n", *anyText);
return 0;
}
M. Zeller
Programmieren
97
Pointer und Strings (2)
Das Programm erzeugt folgende Ausgabe:
lastName: Mueller­Luedenscheid
anyText: Mueller­Luedenscheid
lastName: Mueller­Muedenscheid
anyText: Lueller­Muedenscheid
anyText: Muedenscheid anyText als char: M
M. Zeller
Programmieren
98
Pointer und Strings (3)
char lastName[]= "Mueller­Luedenscheid";
❑ Definiert einen Pointer auf char
❑
Reserviert Speicherplatz für 21 chars
❑
Vorbelegung mit „Mueller-L ... “
Der Pointer lastName zeigt auf den Anfang des
reservierten Speicherplatzes. Er kann nicht
verändert werden.
char *anyText;
Diese Anweisung definiert einen Pointer auf char.
Es wird kein Speicherplatz reserviert, der Pointer
kann verändert werden.
M. Zeller
Programmieren
99
Pointer und Arrays (1)
<BasisTyp> var[n];
Die Definition eines Arrays definiert einen Pointer
auf den Anfang des Arrays und reserviert Platz für
die einzelnen Elemente. Der Pointer kann nicht
verändert werden.
<BasisTyp> *var; Die Definition eines Pointers reserviert keinen
Speicherplatz. Der definierte Pointer kann
verändert werden.
M. Zeller
Programmieren
100
Pointer und Arrays(2)
int main(void){
int numbers[] = {0, 1, 2, 3, 4, 5, 6, 7};
int *numPtr; numPtr = numbers; /* gueltige Zuweisung */
printf(" numPtr ist: %u \n", numPtr); printf(" *numPtr ist: %d \n", *numPtr);
numPtr = numPtr + 1; /* plus 4 (oder 8) */
printf(" numPtr ist: %u \n", numPtr); printf(" *numPtr ist: %d \n", *numPtr);
numPtr = numPtr + 3; /* plus 12 (oder 24) */
printf(" numPtr ist: %u \n", numPtr); printf(" *numPtr ist: %d \n", *numPtr);
return 0;
}
M. Zeller
Programmieren
101
Pointer und Arrays (3)
<BasisTyp> *varPtr; Ein Pointer kann inkrementiert und dekrementiert werden:
varPtr = varPtr + 1; varPtr = varPtr ­ 1; Belegt ein Element des BasisTyps im Speicher n Bytes, so
werden tatsächlich folgende Operationen ausgeführt:
varPtr = varPtr + 1; ⇒ varPtr = varPtr + n
varPtr = varPtr ­ 1; ⇒ varPtr = varPtr – n
Der Pointer wird um n erhöht bzw. vermindert.
M. Zeller
Programmieren
102
Pointer und Arrays (4)
<BasisTyp> *varPtr;
Zu einem Pointer kann eine ganze Zahl addiert werden;
ebenso kann von einem Pointer eine ganze Zahl abgezogen
werden.
varPtr = varPtr + 3;
varPtr = varPtr ­ 5;
Belegt ein Element des BasisTyps im Speicher n Bytes, so
werden tatsächlich folgende Operationen ausgeführt:
varPtr = varPtr + k; ⇒ varPtr = varPtr + n*k
varPtr = varPtr ­ k; ⇒ varPtr = varPtr – n*k
Der Pointer wird um n*k erhöht bzw. vermindert.
M. Zeller
Programmieren
103
Pointer und Arrays (5)
❑
❑
Ein Pointer kann durch die Operationen ++,
--, +, - dazu gebracht werden, auf
verschiedene Elemente eines Arrays zu
zeigen.
❑
Schneller als über Index (vielleicht)
❑
Weniger leicht lesbar
❑
Fehleranfälliger
Empfehlung:
Verzichten Sie auf Pointer-Arithmetik!
M. Zeller
Programmieren
104
Strukturierte Datentypen (1)
int main(void){
struct personId{
char firstName[20];
char lastName[30];
short yearOfBirth; };
struct personId person1 = {"James", "Cook", 1728};
struct personId person2;
person2 = person1; person2.yearOfBirth = 1699;
printf(" pers1 lastName: %s \n", person1.lastName); printf(" pers2 firstName: %s\n", person2.firstName); printf(" pers2 borne in %4i\n", person2.yearOfBirth); return 0;
}
M. Zeller
Programmieren
105
Strukturierte Datentypen (2)
Definition einer Struktur mit drei Komponenten.
struct personId{
char firstName[20];
char lastName[30];
short yearOfBirth;
}; Definition einer Variablen vom Typ struct personId
struct personId person1;
Der Compiler reserviert Speicherplatz, aber die
Komponenten der Struktur werden nicht
initialisiert.
M. Zeller
Programmieren
106
Strukturierte Datentypen (3)
Definition einer Variablen vom Typ struct
personId
struct personId person1 = {"James", "Cook", 1728};
Der Compiler reserviert Speicherplatz, und
initialisiert die Komponenten der Struktur.
M. Zeller
Programmieren
107
Strukturierte Datentypen (4)
Definition einer Variablen vom Typ struct personId
struct personId person1 = {"James", "Cook", 1728};
Auf eine Komponenten der Struktur wird durch
den Namen der Komponente zugegriffen:
person1.yearOfBirth = 1983; /* schreibend */ if(person1.yearOfBirth < 2000){... /* lesend */
M. Zeller
Programmieren
108
Strukturen vs. Arrays
❑
Strukturen fassen mehrere Datenelemente
zusammen, die unterschiedliche Bedeutung
tragen bzw. die unterschiedlich verwendet
werden. Die Elemente können
unterschiedliche Datentypen besitzen.
❑
Arrays fassen mehrere Datenelemente
zusammen, die die gleiche Bedeutung tragen
bzw. die einheitlich verwendet werden. Die
Elemente besitzen alle denselben Datentyp.
M. Zeller
Programmieren
109
Pointer auf Strukturen
Definition einer Struktur mit drei Komponenten.
struct personId{
char firstName[20];
char lastName[30];
short yearOfBirth; }; Variable vom Typ struct personId
struct personId person1;
Variable vom Typ Pointer auf struct personId
struct personId *personPtr;
M. Zeller
Programmieren
110
Pointer auf Strukturen
Zuweisung
personPtr = &person1;
Zwei Möglichkeiten der Dereferenzierung
(*personPtr).yearOfBirth
personPtr­>yearOfBirth
Beide Anweisungen greifen auf die Komponente
yearOfBirth der Variablen zu, auf die personPtr zeigt.
Die zweite Schreibweise ( -> ) ist zu bevorzugen.
M. Zeller
Programmieren
111
Strukturierte Datentypen
Eine Struktur kann einen oder mehrere Pointer
auf den eigenen Typ als Komponente enthalten.
struct personId{
char firstName[20];
char lastName[30];
short yearOfBirth;
struct personId *next;
}; Die Komponente next der Struktur kann einen
Pointer auf eine Variable vom Typ struct personId
aufnehmen.
M. Zeller
Programmieren
112
Pointer auf Strukturierte Datentypen
struct personId person1 = {"James", "Cook", 1728, NULL};
struct personId person2 = {"Captain", "Kirk", 8307, NULL};
person1.next = &person2; person1.next ≙ &person2
*person1.next ≙ person2 person1.next­> ≙ person2.
person1.next­>lastname ≙ person2.lastName M. Zeller
Programmieren
113
Strukturierte Datentypen
(Ausblick)
Aus Strukturen, die ggf. mehrere Pointer auf den
eigenen Typ als Komponente enthalten, können
Listen, Bäume bzw, allg. Graphen aufgebaut
werden.
M. Zeller
Programmieren
114
Verschachtelte Strukturen
struct persIdentity{
char firstName[20];
char lastName[30];
short yearOfBirth; }; struct persAddress{
char street[30];
int zipCode;
char city[30];
}; struct person{
struct persIdentity identity;
struct persAddress address;
struct person *next; };
struct person person1;
M. Zeller
Programmieren
115
Verschachtelte Strukturen
struct person person1;
struct persIdentity currentId;
currentId = person1.identity;
currentId.yearOfBirth = 1699;
strncpy(person1.address.street, "Rosenweg 3", 29);
Die Funktion strncpy(char[], char[], int) finden Sie in
der Bibliothek string.h
Die Funktion kopiert den Inhalt des zweiten Arrays
in das erste Array. Der letzte Parameter gibt an,
wieviele Buchstaben maximal kopiert werden.
M. Zeller
Programmieren
116
Verschachtelte Strukturen
Große Strukturen können/sollen durch Definition von
Teilstrukturen unterteilt werden (wie Funktionen).
❑
Bessere Lesbarkeit
❑
Einfachere Handhabung
Prinzipien
❑
„teile und herrsche“
❑
Trennung der Belange (separation of concerns)
M. Zeller
Programmieren
117
Typdefinitionen
struct persAddress{
char street[30];
int zipCode;
char city[30]; }; struct persIdentity{
char firstName[20];
char lastName[30];
short yearOfBirth; }; typedef struct persIdentity PERS_ID;
typedef struct persAddress PERS_ADDRESS;
typedef struct person{ /* Definition der Struktur */
PERS_ID identity; /* und Umbenennung in einem. */
PERS_ADDRESS address; struct person *next; } PERSON; PERSON person1;
M. Zeller
Programmieren
118
Typdefinitionen
struct persIdentity{
... }; Definiert den Datentyp
struct persIdentity typedef struct persIdentity{
... } PERS_ID; Definiert den Datentyp struct persIdentity und
den identischen Datentyp PERS_ID.
Durch typedef wird ein zweiter Name für
einen bestehenden Datentyp definiert.
M. Zeller
Programmieren
119
Typdefinitionen
Typdefinitionen können die Lesbarkeit eines
Programms erheblich erhöhen.
Schwierig zu lesende Definition:
struct order *orders[10];
Klarere Definition:
M. Zeller
typedef struct order ORDER;
typedef ORDER *ORDER_PTR; ORDER_PTR orders[10]; Programmieren
120
Strukturen als Parameter
❑
Parameter vom Typ struct xy
werden by-value übergeben
❑
Auch eingebettete Arrays werden kopiert
❑
Auch eingebettete Pointer werden vom
aktuellen in den formalen Parameter
kopiert. Ein Pointer zeigt dann auf dieselbe
Variable wie im aktuellen Parameter.
M. Zeller
Programmieren
121
Pointer in Strukturen
struct point{
float x; float y; };
typedef struct point POINT;
typedef POINT *POINT_PTR;
struct rectangle{
POINT_PTR leftUpper;
POINT_PTR rightLower; };
typedef struct rectangle RECTANGLE; float areaRectangle(RECTANGLE recta){
float height = recta.leftUpper­>y ­ . . . float width = recta.rightLower­>x ­ . . . M. Zeller
Programmieren
122
Dynamische Speicherverwaltung
Bisher:
❑
❑
Alle verwendeten Variablen müssen zur CompileZeit deklariert sein.
Neu:
❑
❑
Variable können auch zur Laufzeit neu angelegt
und wieder gelöscht werden.
❑
Diese Variablen können nicht über einen Namen,
sondern nur über einen Pointer angesprochen
werden.
M. Zeller
Programmieren
123
Dynamische Speicherverwaltung
Ziel der dynamischen Speicherverwaltung
❑
Das Programm belegt nur soviel Speicher
wie nötig
❑
Der Speicher wird nur so lange wie nötig
belegt
M. Zeller
Programmieren
124
Dynamische Speicherverwaltung
Lebensdauer und Sichtbarkeit von dynamischen
Variablen
❑
Lebensdauer:
Dynamische Variable existieren bis sie explizit
gelöscht werden, bzw. bis zum Ende des
Programmlaufs.
❑
Sichtbarkeit:
Das Konzept Sichtbarkeit greift nicht bei
dynamischen Variablen, da sie nicht über
Namen, sondern nur über Pointer angesprochen
werden.
M. Zeller
Programmieren
125
Dynamische Speicherverwaltung
Bibliotheksfunktionen für dynamische
Speicherverwaltung:
#include <stdlib.h>
void *malloc(size_t size); void free(void * ptr);
(Der Datentyp size_t entspricht im allg. dem
Datentyp unsigend int oder unsigend long)
M. Zeller
Programmieren
126
Speicherverwaltung: malloc()
Die Bibliotheksfunktion
malloc() Aufruf-Parameter:
❑
Größe des Speicherbereichs, den die neue
Variable benötigt.
Rückgabewert:
❑
M. Zeller
Void-Pointer, der auf die neue, anonyme
Variable zeigt.
Programmieren
127
Speicherverwaltung: malloc()
Die Bibliotheksfunktion
malloc() Die Größe des Speicherbereichs, den die neue
Variable benötigt, kann mit dem Operator sizeof bestimmt werden.
Der zurückgegebene Pointer muss einer PointerVariablen zugewiesen werden.
Der Typ des Pointers muss im Allgemeinen nicht
explizit konvertiert werden. Manche Compiler
verlangen dies aber.
M. Zeller
Programmieren
128
malloc() und free()
int main(void){
int *intPtr = NULL; int i; int iterations; printf("\n Wieviele Zyklen? ");
scanf(" %d", &iterations);
for(i = 0; i < iterations; i++){
intPtr = malloc(sizeof (int));
*intPtr = i*i; printf("\n intPtr zeigt auf: %d", *intPtr);
free(intPtr);
}
return 0;
}
M. Zeller
Programmieren
129
Speicherverwaltung: free()
Dynamische Variable, die nicht explizit freigebenen
werden, existieren weiter, auch wenn auf sie nicht
mehr zugegriffen werden kann (Memory-Leak).
Eine dynamische Variable muss daher freigegeben
werden, bevor die letzte Zugriffsmöglichkeit
verloren geht. Sie darf nur genau einmal
freigegeben werden.
Aufruf z.B.:
free(varPtr); Wobei varPtr auf die freizugebende Variable zeigt.
M. Zeller
Programmieren
130
Speicherverwaltung: free()
❑
Nachdem eine dynamische Variable mit free()
freigegeben wurde, sollten (müssen) alle
Zeiger, die auf diese Variable zeigen mit
einem anderen Wert belegt werden.
❑
Der Zugriff auf die freigegebene Variable kann
zu beliebige Ergebnisse führen. (Wie der
Zugriff auf eine nicht initialisierte Variable)
free(varPtr);
x = *varPtr; /* Ergebnis ist quasi zufällig */ M. Zeller
Programmieren
131
Verkettete Liste/Stack
Verkettet Liste
❑
❑
Menge von dynamischen Variablen linear
verkettet
❑
Einfügen und Entfernen von Elementen an
beliebiger Stelle
Stack
❑
❑
Menge von Variablen
❑
Operationen push() und pop()
❑
Implementierung z.B. verkettete Liste
M. Zeller
Programmieren
132
Abstrakter Datentyp ADT
❑
Auf die Werte, die in einem ADT enthalten
sind, kann nur über Zugriffsfunktionen
zugegriffen werden. Z.B. Einfügen, Löschen
❑
Die Implementierung ist in einem Teil des
Programms gekapselt.
❑
Definition der Datenstruktur
❑
Implementierung der Zugriffsfunktionen
M. Zeller
Programmieren
133
Stack, nicht abstrakt
❑
Globale Variable, die auf die Spitze des Stacks
zeigt, z.B. stackTop
❑
Globale Datenstruktur mit Verkettungs-Pointer
❑
Operationen push() und pop()
Nachteil:
❑
M. Zeller
Die Datenstruktur kann an jeder Stelle des
Programms geändert also auch gestört
werden.
Programmieren
134
Abstrakter Datentyp Stack
Die interne Struktur des Stacks ist nur innerhalb
der Implementierungs-Datei sichtbar.
Im übrigen Programm sind nur Zugriffsfunktionen
sichtbar:
❑
push()
❑
pop()
❑
isEmpty()
❑
...
M. Zeller
Programmieren
135
Abstrakter Datentyp Stack
Header-Datei für den Stack:
struct order{ unsigned int orderCode;
unsigned int price; };
typedef struct order ORDER;
int isEmpty(); void push(ORDER);
ORDER pop();
M. Zeller
Programmieren
136
Abstrakter Datentyp Stack
Vorteil:
❑
Die Datenstruktur kann nur innerhalb der
Implementierungs-Datei geändert werden.
❑
Speicherverwaltung (malloc(), free())
nur innerhalb der Implementierungs-Datei
Ziel:
❑
Minimierung der Abhängigkeiten zwischen
verschiedenen Programmteilen
M. Zeller
Programmieren
137
Dauerhafte Speicherung von
Daten
❑
Variable eines Programms verlieren ihren
Wert spätestens, wenn das Programm
beendet wird
❑
Um Daten dauerhaft zu speichern, können
sie in Dateien abgelegt werden
❑
Die Bibliothek stdio.h stellt Datei-Funktionen
zur Verfügung
M. Zeller
Programmieren
138
Streams
❑
In C stellen „Streams“ die Verbindung zu einer
Datei her (Bibliothek stdio.h).
❑
Ein Programm kann von einem Stream lesen
und auf einen Stream schreiben.
❑
Das „andere Ende“ eines Streams ist i.allg. eine
Datei, kann aber auch die Tastatur bzw. der
Bildschirm sein (oder ein anderes I/O-Gerät).
❑
Der Datentyp eines Streams ist: FILE *
z.B. FILE *varStream; M. Zeller
Programmieren
139
Streams und Dateien
int main(void){ FILE *inFilePtr; char line[100];
char *getResult; inFilePtr = fopen("testFile.txt", "rt");
if(inFilePtr == NULL){
exit(1);
}
while( !feof(inFilePtr) ){
getResult = fgets(line, 100, inFilePtr); if (getResult != NULL){
printf(" Zeile gelesen: %s ", line);
} }
fclose(inFilePtr);
return 0; } M. Zeller
Programmieren
140
Dateizugriff
❑
Variable für Stream vereinbaren.
❑
Datei öffnen und mit einem Stream
verknüpfen.
❑
Von Stream lesen bzw. auf Stream schreiben.
❑
Datei schließen.
❑
Nach jedem Aufruf einer Bibliotheksfunktion
sollte (muss) geprüft werden, ob der Aufruf
erfolgreich war (s. malloc()).
M. Zeller
Programmieren
141
Dateizugriff - fopen()
fopen(Dateinamen, Modus)
Der Modus besteht aus ein bis drei Buchstaben.
❑
Erster Buchstabe (notwendig): 'r', 'w' oder 'a'
für read, write bzw. append
❑
Als zweiter bzw. dritter Buchstabe kann '+'
und einer der Buchstaben 'b' oder 't' stehen.
Bsp.: „r+b“, „w“, „a+“, „w+t“
Dabei steht 'b' für binär-Modus und 't' für textModus. Das '+' bedeuted, dass die Datei sowohl
gelesen als auch beschrieben werden kann.
M. Zeller
Programmieren
142
Dateizugriff – fclose()
fclose(FILE * myStream); Der Aufruf fclose(myStream) schließt den
Stream. Datenstrukturen im Prozess und im
Betriebssystem werden freigegeben.
❑
von myStream nicht mehr gelesen werden
❑
auf myStream kann nicht mehr geschrieben
werden
M. Zeller
Programmieren
143
Dateizugriff – fopen()
Das Öffnen einer Datei erzeugt keine Sperre.
Eine Datei kann von mehreren Prozessen
gleichzeitig geöffnet sein.
Wenn mehrere Prozesse auf eine Datei
schreiben, müssen sie sich synchronisieren.
M. Zeller
Programmieren
144
Streams: Lese-/Schreib-Funktionen
❑
fscanf(), fprintf()
wie scanf() bzw. printf() für beliebige streams
❑
getc(), putc() bzw. fgetc(), fputc()
Lesen bzw. schreiben einzelner Bytes
❑
fgets(), fputs()
Lesen bzw. schreiben von (Text-)Zeilen
❑
fread(), fwrite()
Lesen bzw. schreiben von Datenblöcken
Die genaue Funktionsweise entnehmen Sie bitte
den Arbeitsblättern bzw. der Literatur.
M. Zeller
Programmieren
145
Dateizugriff – Positionszeiger
Zu jeder geöffneten Datei gehört ein Positionszeiger.
Jede Lese- oder Schreib-Operation findet an der Stelle
statt, auf die der Positionszeiger zeigt.
Wert ermitteln:
long ftell(FILE *stream)
Der Positionszeiger wird durch Funktionsaufrufe
verändert:
❑
Lese- oder Schreib-Operationen
❑
fseek(FILE* stream, long offset, int posId)
(posId: SEEK_SET, SEEK_CUR oder SEEK_END)
❑
rewind(FILE* stream)
M. Zeller
Programmieren
146
Dateizugriff – Positionszeiger
M. Zeller
Programmieren
147
Unterstützung
❑
Tutoren im Praktikum
❑
Laborsprechstunde
Herr Drotleff, T007
Herr Bernhard, T109
❑
Zusatzkurs der Fachschaft
M. Zeller
Programmieren
148
Beliebte Missverständnisse
❑
Null-Byte bei Strings: '\0' oder 0 nicht NULL
❑
Allgemeine Arrays besitzen i. Allg keine
Endemarkierung
❑
Initialisierung und Vergleich bei Pointer
=NULL nicht 0 oder '\0'
❑
Zugriff auf Komponenten einer Struktur:
var.comp wenn var kein Pointer ist.
var­>comp wenn var ein Pointer ist. Der
Datentyp von comp spielt keine Rolle.
comp ist der Name der Komponente, nicht
der Datentyp.
M. Zeller
Programmieren
149
Beliebte Missverständnisse
❑
File *stream ≠ Positionszeiger
❑
Parameterübergabe
❑
alle Datentypen außer Arrays: „by value“
(auch Strukturen ggf. mit Arrays)
❑
alle Datentypen können „by reference“
übergeben werden (formaler Parameter: Pointer
auf . . . )
M. Zeller
Programmieren
150
Fakultät
double fakultaet(unsigned short number){
double result;
if (number == 0){
return 1;
}
result = number * fakultaet(number – 1);
return result;
}
int main(void){
unsigned short num;
double fak;
printf("\n Argument fuer Fakultaet eingeben: ");
scanf("%u", &num);
fak = fakultaet(num);
printf("Die Fakultaet von %u ist %e \n", num, fak); return 0; } M. Zeller
Programmieren
151
Rekursive Funktionen
❑
Eine Funktion, die sich selbst aufruft, wird
rekursiv genannt.
❑
Prinzipiell kann jede rekursive Funktion in
eine iterative Funktion umgewandelt werden.
❑
Manche Algorithmen lassen sich rekursiv
eleganter realisieren als iterativ.
M. Zeller
Programmieren
152
Rekursive Funktionen
❑
Eine rekursive Funktion muss mindestens
einen nicht-rekursiven Ausgang besitzen.
❑
Vor dem rekursiven Aufruf muss geprüft
werden, ob das Ende der Rekursion erreicht
ist (dann den nicht-rekursiven Ausgang
wählen).
❑
Anwendungen für rekursive Funktionen sind
z.B. Sortieren und rekursive Datenstrukturen
wie Listen und Bäume.
M. Zeller
Programmieren
153
Enumerations/Aufzählungstypen
enum apple {cox, elstar, ontario, golden, klar};
enum pear {william, helene, most};
typedef enum apple APPLE;
int main(void){
APPLE myApple = elstar;
enum pear myPear = william;
printf("\n\n meine Birne: %d \n", myPear);
if (myApple == ontario){
printf("\n meine Apfel ist ein Ontario\n");
}
else{
printf("\n meine Apfel ist ein %d \n", myApple);
}
:
M. Zeller
Programmieren
154
Enumerations/Aufzählungstypen
Jeder Wert eines Aufzählungstyps entspricht
einer ganzen Zahl.
Die Anweisung
enum color {red, green, blue};
definiert die drei Werte:
red = 0, green = 1 und blue = 2.
Die Werte können gesetzt werden:
enum color {red, green = 4, blue};
red = 0, green = 4 und blue = 5.
M. Zeller
Programmieren
155
Enumerations/Aufzählungstypen
Die Anweisung
enum color {red, green, blue};
definiert den Datentyp enum color
Werten: red, green und blue.
mit drei
Die Anweisung
enum color myColor; definiert eine Variable
vom Typ enum color.
M. Zeller
myColor
Programmieren
156
Enumerations/Aufzählungstypen
Variable eines Aufzählungstyps können für
einen ganzahligen Wert/Ausdruck eingesetzt
werden.
enum color {red, green, blue};
enum color myColor = green; float test[3];
test[myColor] = 4.2; switch(myColor){
case red: . . . case green: . . .
. . . } M. Zeller
Programmieren
157
Enumerations/Aufzählungstypen
Variable verschiedener Aufzählungstypen
können in einem Ausdruck untereinander und
mit Zahlen gemischt werden: schlechter Stil!
enum color {red, green, blue};
enum direction {north, west, east = 6, south};
enum color myColor = green; enum direction myDir = south; int nonsense = (myDir – myColor) * 3;
if (myColor > myDir){
. . .
}
M. Zeller
Programmieren
158
Enumerations
Sinnvoller Einsatz:
z.B. Darstellung eines Zustands
❑
❑
Vergleich auf Gleichheit
❑
if (myColor == blue) . . .
❑
switch (myColor){ case blue : . . . evtl. Indizierung von Arrays:
❑
colorCode = codeTab[blue] M. Zeller
Programmieren
159
Der Präprozessor
Übersetzungsschritte eines C-Programms:
(1)Präprozessor (Textverarbeitung):
Kopiert und ersetzt Programmtext
(2)Compiler: Analysiert Programmstruktur und
erzeugt “Objekt-Code“
(3)Linker: Fügt Objekt-Code von
Bibliotheksfunktionen ein und setzt Adressen ein.
M. Zeller
Programmieren
160
Präprozessor-Anweisungn
❑
Zeilenorientiert:
Eine Anweisung endet am Ende der Zeile.
Ausnahme:
Wenn der letzte Buchstabe der Zeile ein \ ist.
❑
Erstes (druckbare) Zeichen muss ein # sein
❑
Beispiele:
#include . . .
#define . . . #ifdef . . .
M. Zeller
Programmieren
161
Präprozessor-Anweisungen
#include <name.h> Sucht im „include-Pfad“ nach der Datei name.h
und ersetzt die include-Anweisung durch den
Inhalt der Datei name.h
#include “name.h“ Sucht im aktuellen Verzeichnis nach der Datei
name.h
Schutz gegen mehrfachen Import:
#ifndef NAME_H
#define NAME_H
Inhalt der Datei name.h #endif M. Zeller
Programmieren
162
Defines
Der Präprozessor ersetzt Konstanten im Programm.
#define LENGTH 64 LENGTH ist eine (Text-)Konstante
Sie wird im Programm ersetzt:
int numbers[LENGTH]; :
for(i = 0; i < LENGTH; i++){ ... Wird zu:
int numbers[64];
:
for(i = 0; i < 64; i++){ ... M. Zeller
Programmieren
163
Defines
Sie sollten keine Zahlen (Magic Numbers) in
Ihrem Quellcode verwenden.
Besser:
❑
Präprozessor-Konstante:
#define SIZE 5
Noch besser, aber nicht immer möglich:
❑
M. Zeller
Compiler-Konstante: const int size = 5;
(in der Header-Datei als extern deklarieren)
Programmieren
164
Makros
Der Präprozessor ersetzt „Makros“ im Programm.
#define PROD(A, B) (A)*(B) PROD ist ein Makro; es wird im Programm ersetzt:
int size = 3; int value = 6; int result;
result = PROD(size, value); wird zu:
result = (size) * (value); M. Zeller
Programmieren
165
Makros
❑
Makros sind etwas schneller als
Funktionsaufrufe
❑
Makros können zu schwer erkennbaren
Fehlern führen
❑
Makros können meist ersetzt werden durch
inline-Funktionen
Empfehlung:
❑
Keine Makros einsetzten
❑
inline-Funktionen nur wenn nötig
M. Zeller
Programmieren
166
Präprozessor
Hauptanwendungen:
❑
Definition von Konstanten
❑
Mehrere Programmvarianten aus einem
Quelltext (statt if-Anweisungen)
❑
geringere Größe und höhere Geschwindigkeit des
ausführbaren Programms
❑
schlechtere Lesbarkeit des Quelltexts
M. Zeller
Programmieren
167
Herunterladen