• Keine Ergebnisse gefunden

Vorlesungsskript PG1 (I1T)

N/A
N/A
Protected

Academic year: 2021

Aktie "Vorlesungsskript PG1 (I1T)"

Copied!
225
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Prof. R Zavodnik 21. Januar 2003

(2)

Inhaltsverzeichnis

1 Einf¨uhrung 1

1.1 Computerorientierte Probleml¨osung . . . 1

1.1.1 Was ist ein Computer? . . . 1

1.1.2 Wie schreibt man ein Computerprogramm? . . . 2

1.1.3 Programm und Rechner . . . 2

1.2 Schritte zum Schreiben und Ausf¨uhrung eines C - Programms . . . 4

1.3 Schritte zum Compilieren eines Computerprogramms . . . 5

2 Eine ¨Ubersicht 7 2.1 Das erste C-Programm . . . 7

2.1.1 Das minimale Programm . . . 7

2.1.2 Das ber¨uhmte “hello”- Programm . . . 8

2.2 Konstante, Variablen und (arithmetische) Ausdr¨ucke . . . 9

2.3 Kontrollstrukturen . . . 13

2.3.1 Bedingte Ausf¨uhrung . . . 19

2.3.2 Die Switch - Anweisung . . . 22

2.4 Backus-Naur-Form . . . 26

2.4.1 Definition von BNF . . . 26

2.4.2 Kontrollstrukturen in EBNF . . . 27

2.5 Aufz¨ahlungen und enum . . . 28

2.6 Reihungen . . . 33

2.7 Funktionen in C . . . 34

2.8 Zeichenketten in C . . . 38

2.8.1 Definition und Beispiele . . . 38

3 Daten im Programm 41 3.1 Speicherorganisation . . . 42

3.2 Ganze Zahlen in C . . . 45

3.2.1 Das 2er - Komplement Dualsystem . . . 46

3.2.2 Die Fließkommazahlen (floatund double) . . . 49

3.3 Konstanten in C . . . 52 i

(3)

3.3.1 Ganzzahlige Konstanten . . . 52

3.3.2 Gleitkomma Konstanten . . . 53

3.3.3 Zeichenkonstanten . . . 53

3.4 Typumwandlungen (casts) . . . 54

3.5 Die C - Speicherklassen . . . 54

3.5.1 Die Speicherklasse auto . . . 54

3.5.2 Globale Variablen . . . 56

3.5.3 Die Speicherklasse static . . . 58

3.5.4 Die Speicherklasse register . . . 60

3.6 Initialisierung globaler Variablen . . . 60

4 Funktionen 63 4.1 Rekursion . . . 63

4.2 Parameter¨ubergabe in C . . . 66

4.3 Funktionen als Parameter . . . 68

4.4 Schnelle Funktionen - Macros . . . 71

5 Reihungen in C 73 5.1 Zeiger in C . . . 74

5.1.1 Zeigerarithmetik . . . 76

5.2 Reihung - Initialisierung . . . 77

5.3 Dynamische Erzeugung einer Reihung . . . 78

5.4 Zeichenketten . . . 80

5.4.1 Speicherzuteilung von Zeichenketten . . . 81

5.5 Merhdimensionale Reihungen . . . 84

5.5.1 Flatterreihungen / ragged - arrays . . . 86

5.6 Reihungen als Funktionsparameter . . . 89

5.7 Konstanten, Zeiger und Fl¨uchtige Daten . . . 90

5.8 Beispiel: Das Sieb von Eratosthenes . . . 91

6 Strukturen und Unions 95 6.1 Definition . . . 95

6.2 Zugriff auf Strukturen und Komponenten von Strukturen . . . 97

6.3 Operatorpr¨azedenz . . . 98

6.4 Strukturen in Funktionen . . . 100

6.5 Initialisierungen von Strukturen . . . 106

6.6 Unionen . . . 106

6.7 Bit-Ausdr¨ucke, Bit-Operatoren und Bit-Felder . . . 108

6.7.1 Die Logischen Operatoren . . . 109

6.7.2 Die Shift Operatoren . . . 109

6.7.3 Bit - Masken . . . 110

6.7.4 Bit-Felder: . . . 114

(4)

7 Dateien 117

7.1 Elementare Operationen mit Dateien . . . 118

7.1.1 Datei ¨Offnen . . . 118

7.1.2 Datei Schließen . . . 118

7.1.3 Datei Lesen . . . 119

7.1.4 Datei Schreiben . . . 119

7.1.5 Fehlererkennung: . . . 119

7.2 Random Access (Direktzugriff) . . . 120

8 Die Klassischen Datenstrukturen 125 8.1 Was sind Datenstrukturen? . . . 125

8.2 Rekursive Datenstrukturen . . . 125

8.3 Listenstrukturen . . . 128

8.3.1 Verkettete Listen . . . 128

8.3.2 Queues (Schlangen) . . . 129

8.3.3 Stacks . . . 131

8.3.4 Doppelverkettete Listen . . . 137

8.4 B¨aume . . . 139

8.4.1 Definitionen . . . 139

8.4.2 Programmdarstellung von B¨aumen . . . 141

8.4.3 Durchlauf eines Bin¨arbaums . . . 143

9 Objektorientierung in C++ / Eine ¨Ubersicht 157 9.1 Teil 1: Klassen . . . 157

9.1.1 Unterschied zu Strukturen . . . 157

9.1.2 Ein - Ausgabe in C++ . . . 160

9.1.3 Initialisierung und Konstruktoren . . . 161

9.1.4 Kopierkonstuktoren . . . 163

9.1.5 Destruktoren . . . 167

9.1.6 Konstante Objekte . . . 167

9.1.7 friend- Funktionen . . . 169

9.1.8 Inline - Funktionen . . . 170

9.1.9 Reihungen und Konstruktoren . . . 173

9.1.10 Zeiger auf Objekte . . . 174

9.1.11 Dynamische Datenobjekte . . . 176

9.1.12 ¨Uberladen von Operatoren . . . 178

9.1.13 friend- Klassen . . . 184

9.1.14 Beispiel Objektorientierung: Das 8 K¨oniginnenproblem . . 185

9.2 Teil 2: template- Klassen . . . 191

9.2.1 template- Klassendefinition . . . 191

9.2.2 Methodenfunktionen undtemplate- Klassen . . . 193

9.2.3 Die Standard Template Library . . . 200

(5)

9.3 Teil3: Objektorientierung . . . 209 9.3.1 Vererbung . . . 209 9.3.2 Polymorphismus: virtuelle Funktionen . . . 217

(6)

Einf¨ uhrung

1.1 Computerorientierte Probleml¨ osung

1.1.1 Was ist ein Computer?

Der Computer (Rechner) ist eine elektronische Maschine, die intern gespeicherte Anweisungen in einer bestimmten Reihenfolge ausf¨uhrt. Diese Anweisungen (oder Operationen) werden auf gespeicherte Daten angewandt (d.h. lesen) und die be- rechneten Ergebnisse werden ebenfalls gespeichert (d.h. schreiben). Die ben¨otigte Folge von Anweisungen, um bestimmte Ergebnisse zu berechnen, heißt Computer- programm.

Rechnerkomponente

1. CPU oder Zentraleinheit:F¨uhrt Anweisungen aus 2. Speicher: Enth¨alt Daten

Abbildung 1.1: Funktionalit¨at eines Rechners 1

(7)

Abbildung 1.2: Komponente des Computers (a) Hauptspeicher (intern)

(b) Externer Speicher (Diskette / Festplatte) 3. Ein / Ausgabeger¨at

Aus der Sicht eines Programmierers ist der Speicher die wichtigste Komponente des Computers.

1.1.2 Wie schreibt man ein Computerprogramm?

1. Man spezifiziere die vom Computer auszuf¨uhrende Arbeit (immer bez¨uglich Eingabe)

2. Man entwickle einen Algorithmus( d.h. eine feste Folge von Regeln, deren Befolgung das gew¨unschte Problem l¨ost) oder ein Rezept, um eine L¨osung zu finden. (Dieser Schritt ist sprachen - und computerunabh¨angig)

3. Man ¨ubersetzeden Algorithmus in die jeweilige Computersprache 1.1.3 Programm und Rechner

Wie l¨auft ein Programm auf einem Computer? Die obenerw¨ahnte Computerspra- che ist meist von Menschen aber nicht vom Computer lesbar. Das Programm wird gew¨ohnlicherweise mit einemTexteditor(auch m¨oglich: Lochkarten, Streifen, usw.) geschrieben Folge: Das Programm muß in die Maschinensprache mittels des Computers ¨ubersetzt werden. Jede Computersprache besitzt mindestens einen Compiler, z.B. C wurde von Kernighan/Ritchie, Microsoft, Borland, Metaware, usw. implementiert.

(8)

Abbildung 1.3: Ablauf des Compilierungsvorgangs

Wichtig: Das ¨ubersetzte Programm ist zum urspr¨unglichen Computerprogramm v¨ollig ¨aquivalent!

DerCompiler ist ein Computerprogramm, das einen Quelltext (Computerpro- gramm, das aus reinem Text besteht) in die Maschinensprache ¨ubersetzt.

Der Ablauf ist in Abb. 1.3 zu sehen. Zur Compilezeit wird das Programm ubersetzt; zur¨ Laufzeitwird das Programm ausgef¨uhrt.

Kommt ein Fehler w¨ahrend des Programm - Schreibens vor (sog. Syntaxfeh- ler), handelt es sich um einen Compilezeitfehler. Kommt dagegen ein Fehler beim Programmablauf vor, (z.B. Teilung durch 0), so handelt es sich um einenLaufzeit- fehleroder bug. Laufzeitfehlersuche heißt Debugging

Was war zu erst da? Programm oder Compiler? Da der Compiler eben- falls ein Programm ist, braucht man einen Compiler, um den Compiler zu ¨uberset- zen. Das Problem wird durch folgendes Bootstrappingverfahren gel¨ost: Man ¨uber- setze einen kleinen Teil der zu ¨ubersetzenden Computersprache per Hand (d.h. ein Teil der Sprache wird direkt im Maschinencode geschrieben). Danach werden suk- zessiv immer gr¨oßere Sprachmengen ¨ubersetzt, bis die volle Sprachmenge erreicht worden ist.

(9)

Abbildung 1.4: Ablauf des Programms

1.2 Schritte zum Schreiben und Ausf¨ uhrung eines C - Programms

• Geben Sie den Programmtext mit einem Texteditor (vi) ein. Das Ergebnis ist eine Datei programm.c

• Compilieren Sie die o.e. Textdatei. Das Ergebnis ist eine Ausf¨uhrbare Datei a.out

Merke: Die ausf¨uhrbare Datei ist keine Textdatei! Die ausf¨uhrbare Datei ist eine bin¨are Datei, d.h. eine Folge von 0 und 1.

• F¨uhren Sie die oben entstandene bin¨are Datei durch einen direkten Aufruf aus. Die Eingabe wird normalerweise w¨ahrend der Programmlaufzeit von der Tastatur oder einer anderen Dateigelesen. Die Ausgabe wird gew¨ohnlich auf

– den Bildschirm – den Drucker – die Datei

w¨ahrend der Laufzeitgeschrieben.

Hinweis: C hat keine Ein- und Ausgabe Das bedeutet: Die Sprache “C”

selbst besitzt keine Ausgabe oder Eingabebefehle. Diese werden durch Standard- biblotheken implementiert (mittels #include: siehe unten). Dies hat zur Folge, daß Ausgabe / Eingabeoperationen vom Programmierer selbst geschrieben wer- den k¨onnen. Diese m¨ussen dann auch in Bibilotheken abgelegt werden und der Programmierer kann sie anstatt der “Standardbibliothek” verwenden.

(10)

1.3 Schritte zum Compilieren eines Computerpro- gramms

• Bestimmte Texte, die das Programm ben¨otigen k¨onnte (z.B. Standardtex- te mit n¨utzlichen Konstruktionen, die allen Programmierern zur Verf¨ugung gestellt worden sind), werden imQuellprogrammmittels dem sog. Pr¨aprozes- sor (cpp) im Programm eingef¨ugt (maschinenunabh¨angig). Genauer gesagt:

Eine bestimmte Datei wird im Programmtext an der von Ihnen bestimmten Stelle inkludiert (eingef¨ugt). Das Ergebnis ist eine schwer lesbare Textdatei, die aber vom Compiler leicht zu ¨ubersetzen ist. Der Pr¨aprozessor entfernt außerdem noch alle Kommentare aus dem Programm.

• Ergebnis: programm.i (programm.c wird in programm.i umgewandelt) Der Programmtext in Schritt 1 wird aus der C++ - Sprache in eine Sprache ¨uber- setzt, die der Kommandostruktur der jeweiligen Zielmaschine verwandt ist.

Obwohl diese Sprache maschinenabh¨angig ist, handelt es sich um eine Text- datei, die sp¨ater einer weiteren ¨Ubersetzung bedarf (z.B. Assemblersprache).

Ergebnis:

• Das in Schritt 2 ¨ubersetzte Modul wird assembliert, d.h. Kommando f¨ur Kommando in die Assemblersprache ¨ubersetzt. Das Ergebnis ist ein bin¨ares Modul ohne Text.

Dieses Modul ist auf dem Computer immer noch nicht lauff¨ahig. Grund:

Das Modul kann andere Module verwenden oder aufrufen (z.B. um die Wur- zel einer Zahl aus Ihrem Programm zu berechnen). Solche Module wurden bereits ¨ubersetzt und als Bin¨armodule im Rechner einer sogenannten Biblio- thek oder Archiv gespeichert.

• Alle in ihrem Urspungsprogramm referenzierten Module (d.h. *.e - Dateien) werden in ein ausf¨uhrbares Programm (bzw. in ein bin¨ares Modul, das auf dem Computer lauff¨ahig ist) gelinkt ( zusammengebunden ). Im Programm wird nicht nur programm.e verwendet, sondern auch externe Module. Das Ergebnis ist eine Bin¨ardatei, die auf dem Computer lauff¨ahig ist.

Der gesammte Prozeß nochmal in der ¨Ubersicht in Abb. 1.5:

(11)

Abbildung 1.5: Ablauf des Compilierungsvorgangs

(12)

Eine ¨ Ubersicht

2.1 Das erste C-Programm

2.1.1 Das minimale Programm

Dem kleinstm¨oglichen C - Programm geben wir den Dateiname: min.c:

void main() {

}

Bemerkungen zum Programm

1. “main” bedeutet: Programm, fange hier an!

2. In jedem C-Programm muß das Wort main genau einmal vorkommen.

3. Leerzeichen spielen beim Programmieren (ausser zwischen den W¨ortern) kei- ne Rolle. Falls anstatt “main” etwas anderes verwendet wird, bleibt der Compiliervorgang beim “Linker” stehen

4. Vor dem Namen der Funktion main wird deklariert, von welchem Typ das berechnete Ergebnis sein soll.

5. void bedeutet: Kein R¨uckgabewert wird berechnet.

6. Hinter main stehen in Klammern () die Werte, die der Funktion ¨ubergeben werden. Hier werden keine Werte ¨ubergeben. In ANSI - C wird das so ausgedr¨uckt: main(void) statt nurmain(). ‘Die Werte, die das Programm bekommen soll, werden in der Kommandozeile ¨ubergeben. Dies wird sp¨ater erkl¨art.

7

(13)

7. Die geschweiften Klammern “{ }” umfassen die auszuf¨uhrenden Program- manweisungen der Funktion main . Die hier verwendete Anweisung ist die sog. leere Anweisung. Daraus folgt: Das Programm tut gar nichts!

2.1.2 Das ber¨uhmte “hello”- Programm

#include<stdio.h>

void main() {

printf("Hallo\n"); /*Gruesse die Welt*/

}

Erkl¨arung:

1. Die Compiler - Direktive#include <stdio.h>bewirkt, daß an dieser Text- stelle vor der C- ¨Ubersetzung eine Textdatei (ASCII) namens stdio.h ein- gef¨ugt wird. Die Datei enth¨alt Informationen dar¨uber, was printf bedeu- tet. Etwas genauer, stdio.h enth¨alt Informationen, die es dem C-Compiler erm¨oglichen, die korrekte Verwendung der sog. “Funktion” printf() im Programm zu pr¨ufen.

2. Include - Anweisungen sollten ganz am Anfang des Programmquelltextes stehen.

3. Die eckigen Klammern<> bedeuten, daß diese Textdatei an einer vereinbar- ten Stelle im UNIX Betriebssystem (sog. Pfadname) zu finden ist.

4. Die C-Standardfunktion printfist nicht Bestandteil der C - Sprache. Die vordefinierte Funktion printf() wird lediglich aufgerufen. Was printf() genau bedeutet, steht in der Datei stdio.h. Folge: Es ist auch m¨oglich, selbstdefinierteprintf’s zu schreiben.

5. Die Kommentare /*Text*/ werden nicht ¨ubersetzt, sind aber sehr w¨unschenswert, denn sie helfen dem Leser das Programm zu verstehen. Kom- mentare “/* */” k¨onnen ¨uber mehrere Zeilen gehen. Wann sind Kommen- tare zu schreiben? Faustregel: Kommentare schreiben, wo eine Erkl¨arung n¨otig ist.

6. Das Semikolon “;”wird verwendet, um jede Anweisung zu beenden. Daher ist folgendes m¨oglich

void main() {

;;;;

}

(14)

Abbildung 2.1: Compilierung vonhello.c Das obige Programm w¨urde 4x nichts machen.

7. Alles, was zwischen den Klammern “(” und “)” bei printf() vorkommt, wird auf den Bildschirm ausgegeben. Dabei ist

neinSteuerzeichenf¨ur den Zeilenumbruch. Steuerzeichen werden zwar nicht direkt ausgegeben, steuern aber wie weiter ausgegeben wird.

Der gesamte ¨Ubersetzungsvorgang wird in Abb. 2.1 zusammengefasst. Das Pro- gramm hello.cwird mit dem C - Compilierungsbefehl

cc -o hello hello.c

uberstezt. Die Compileroption¨ -o bewirkt, daß die ausf¨uhrbare Datei “hello”

heißt. Ohne diese Option heißt sie “a.out”.

2.2 Konstante, Variablen und (arithmetische) Aus- dr¨ ucke

Jetzt wird ein Programm geschrieben, das Daten aus seinem Speicherbereich liest und schreibt. Hilfreiche Programmkonstanten werden auch dabei verwendet. Den Speicher kann man sich als ein System beschrifteter Schubladen in einem Schub- ladenkasten vorstellen. In den Schubladen befinden sich die genannten Werte.

Die Schubladen oder Speicherzellen werden durch Variablennamen benannt.

Zum Beispiel in Abb. 2.2 ist S1 = die Adresse der ersten Speicherzelle. Die einzelnen Speicherbereiche werden durch Namen oder Bezeichner angesprochen Man betrachte nachfolgendes Programmumfang.c:

(15)

Abbildung 2.2: Speicherzellen als Schubladenkastensystem

#include <stdio.h>

int main(void) {

/*Lese einen Radiuswert ein und berechne eine Fl"ache und den Umfang.

Gib das Ergebnis auf den Bilschirm aus.*/

float Radius, Flaeche, Umfang; /*Datenerklaerung*/

const float pi=3.14159;

printf("Bitte Radius eingeben\n");

scanf("%f", &Radius); /*lese den Radius ein */

Flaeche = pi * Radius * Radius; /*Flaeche berechnen*/

Umfang = 2 * pi * Radius; /*Umfang berechnen*/

printf("\n Umfang = %f und Flaeche =%f", Umfang, Flaeche);

return 0;

}

Erkl¨arung:

• Anstatt void wird hier eine ganze Zahl int als R¨uckgabewert von main verwendet. intbedingt einereturn- Anweisung, da hier ein R¨uckgabewert erzwungen wurde.

• float Radius, Flaeche, Umfangist eineDeklarationvon Programmvaria- blen, d.h. Speicherplatz mit Namen (genauer: Adressen) wird bereitgestellt.

Mit der Angabe float sind Fließkommazahlen in diesen 3 Speicherpl¨atzein zu speichern. Man sagt: derTypdieser Speicherpl¨atze istfloat. Am Anfang des Programms sind diese 3 Speicherpl¨atze leer; konkrete Fließkommawerte m¨ussen im Programm zugewiesenwerden.

• const float pi = 3.1415926;ist die Deklaration einerProgrammkonstan-

(16)

Abbildung 2.3: Einlesen von Daten mittels scanf()

tenmit dem Namenpi. Der Wert 3.1415926 dieser Konstante istunver¨ander- lich und das sog. Schl¨usselwort consterkl¨art diesen Namen als Konstante.

• printfbedeutet, wie im hello - Programm Bildschirmausgabe des Argumen- tes.

• scanf("%f", &Radius)ist eine Standardbibliotheksfunktion, die einen Wert von der Tastatur einliest, diesen Wert als floatinterpretiert und ihn in der Variablen Radius abspeichert. In diesem Fall heißt &Radius die Speicher- adresseder VariablenRadius. Diese Adresse ist eineParameterder Funktion scanf(). Siehe Abb.2.3 f¨ur eine Interpretation.

%fist noch ein Steuerzeichen mit der Bedeutung: Interpretiere den Inhalt der Speicherzelle “Radius” als float, d.h. speichere dort eine Fließkommazahl ab. Wird bei printf() %f verwendet, werden bei der Ausgabe anstelle des %f die hinter den G¨ansef¨ußen angegebenen Variablen der Reihe nach ausgegeben.

• Beim Programmieren sind keine Umlaute zu verwenden.

• Die Zuweisung:

Flaeche = pi * Radius * Radius;

Der Zuweisungsoperator =wird verwendet, um Variablen einen berechneten Wert zu geben. Die AnweisungRadius = 1.0;bedeutet: Der Wert 1.0 wird der Speicherzelle Radius zugewiesen. Folge: Der Ausdruck auf der rechten Seite der Zuweisung wird von der Maschine berechnet (hier: 3.14159*1.0*1.0

= 3.14159) und in die Speicherzelle Flaechegeschrieben.

• Variablen k¨onnen auch initialisiert werden, z.B. statt {

float Radius;

Radius = 1.0; /* Wird extra waehrend der Laufzeit belegt*/

. . . }

(17)

kann man auch eine sog. Initialisierung verwenden.

{

float Radius=1.0;

/*Wird vor der Ausfuehrung der Anweisungen belegt*/

. . . }

Immer verwendete Variablen vorbelegen!

• Der Zuweisungsoperator = wird haupts¨achlich verwendet, um den Wert eines arithmetischen Ausdrucks zu berechnen und dann erst diesen Wert in eine Speicherzelle zu plazieren.

Beispiel: Annahme: Variablena,b,c sind vom Typ

”Ganze Zahl“. Dann ist der Programmabschnitt

int main () {

int a,b,c, a=1;

b=2;

c=a+b; /*Zuweisung*/

}

gleichbedeutend mit int main () {

int a=1, b=2,c; /*Initialisierungen*/

c=a+b; /*Zuweisung*/

}

Die Werte von a und b, d.h. 1 und 2 werden von Computer addiert. Das Ergebnis 3 wird in einer tempor¨aren Speicherzelle gespeichert. Dieses Er- gebnis 3 wird dann von der tempor¨aren Speicherzelle zur Speicherzelle mit der Adresse cbewegt. Variablenaund bwerden dabei nur verwendet(sog.

r-Werte – “r” f¨ur rechte Seite). Die Variable cwird dabei modifiziert (sog.

l-Wert – “l” f¨ur linke Seite). Also, bei Zuweisungsvariablen heißt r-Wert

“Rechte Seite” und die Werte f¨ur a und b werden dabei nicht ver¨andert w¨ahrend l-Wert “Linke Seite”heißt. Der Wert c wird modifiziert. Mit der Anweisung

(18)

Abbildung 2.4: Speicherzellen und Zuweisungen Umfang = 2 * pi * Radius;

wird der Wert der SpeicherzelleRadiusmit dem Produkt 2 * 3.14159 multi- pliziert und das Ergebnis der SpeicherzelleUmfangzugewiesen, wie Abb. 2.4 zeigt.

2.3 Kontrollstrukturen

Wenn in einem Programm bestimmte Teile wiederholt werden m¨ussen, geschieht dies durch Schleifen. Durch bestimmte Bedingungen gesteuert bricht die Wieder- hohlung w¨ahrend des Programmablaufs ab.

Beispiel: Umwandlung von Celsius in Fahrenheit Der Umrechnungsalgo- rithmus erfolgt durch die Formel

F = 9/5C+ 32 Der Gesammtalgorithmus l¨auft wie folgt ab:

Eingabe: Varible Celsius Ausgabe: Variable Fahrenheit lower ← -40

upper ← 160 step ← 20

so lange Celsius ≤upper {

Fahrenheit = 95 ×Celsius+ 32 Celsius =Celsius +step }

Dieser Algorithmus wird nun systematisch in ein Programm umgewandelt. Im folgenden Programm wird die Wiederholung in einer while- Schleife realisiert:

(19)

# include <stdio.h>

int main() {

const int lower = -40, upper = 160, step = 20;

int Celsius = lower, Fahrenheit;

while (Celsius <= upper) {

Fahrenheit = (9*Celsius)/5 + 32; /* Integer - Teilung*/

printf("%d &%d\n", Celsius, Fahrenheit);

Celsius = Celsius + step;

} return 0;

}

Erkl¨arung:

• Die Konstanteninitialisierungen

const int lower = -40, upper = 160, step = 20;

erkl¨aren die Konstanten lower, upperund step und definieren sie mit -40, 160 und 20. Diese sind keine Variablen!! (Sie d¨urfen zum Beispiel nicht auf der linken Seite einer Zuweisung stehen). Eine Anweisunglower = 10;f¨uhrt zu einer Compilerfehlermeldung, d.h. diese Anweisung ist nicht ¨ubersetzbar.

Folge: Konstanten werden in Programmanweisungen nur verwendet, nicht neu definiert.

• Die Variableninitialisierungint Celsius = lower;weist der Variable (d.h.

Speicherzelle)Celsiusden Wertlower(d.h. -40) zu. Hier ist die Zuweisung Celsius = 50 durchaus erlaubt.

• Konstanten m¨ussen immer initialisiert werden!!

• Eine while- Schleife besitzt die Form (oder Syntax) while (Bedingung)

{

Anweisung }

Auf Deutsch: Eine (oder mehrere) Anweisung(en) (sog. Rumpf) wird (wer- den) solange ausgef¨uhrt bis der Wert vonBedingung0 (d.h. falsch) wird. Da- nach wird die Programmausf¨uhrung bei der nachfolgenden Anweisung fort- gesetzt. In diesem Fall ist die BedingungCelsius <= upperund sie wird so

(20)

behandelt: Ist der Wert dann von der VariablenCelsiuskleiner oder gleich der Konstanteupperd.h. 160, so ist der int- Wert der Bedingung ungleich null, sonst ist der Wert null.

• Mit der Anweisung Celsius = Celsius + step;, oder k¨urzer:

Celsius+=step; wird die Schleifenvariable Celsius aktualisiert, d.h.

mit 20 implementiert. Wichtig, weil der Wert von Celsius<= upper irgend- wann 0 (d.h. falsch) werden muss. Der Rumpf der While - Schleife besteht in diesem Fall aus einem Block. EinBlockist eine Gruppe von Anweisungen, die durch ‘‘{’’ und‘‘ }’’ abgegrenzt wird. Im Programmabschnitt {

Fahrenheit = (9*Celsius)/5 + 32;

printf(...);

Celsius = Celsius + step;

}

besteht der Block aus 3 Anweisungen. Bei jeder Auswertung der Bedingung mit dem Ergebnis 6= 0 wird diese Gruppe oder Block von 3 Anweisungen ausgef¨uhrt. Am Anfang der letzten Iteration hat Celsius den Wert 160.

Beim UpdateFahrenheit+=step;hatCelsiusden Wert 180 (der alte Wert 160 wird damit ¨uberschrieben und die Schleife wird abgebrochen).

• Die Auswertungsreihenfolge von Ausdr¨ucken

Bei gleichrangigen Operatoren wie + und - oder * und / wird der Ausdruck von links nach rechts ausgewertet. Da die Teilung ganzzahlig (integer) ist, wird die Reihenfolge mit Klammern ge¨andert, z.B. a+b*c bedeutet:

1. b*c berechnen

2. Ergebnis mit aaddieren Dagegen bedeutet (a+b)*c

1. a+b berechnen

2. Ergebnis mit cmultiplizieren

• Das sogenannte Flußdiagramm f¨ur die while - Schleife ist aus Abb.2.5 zu entnehmen.

• Parameter bei printf(): Das erste Argument ist eine sog. Zeichenkette (d.h. mehrere Zeichen zwischen‘‘und’’eingeschlossen - eine sog. String), die das allgemeine Format der Ausgabe bestimmt. Die Formatelemente, die mit “%” beginnen, bestimmen wo in dieser Zeichenkette die in printf

(21)

Abbildung 2.5: Flußdiagramm f¨urwhile

nachfolgenden Argumente eingef¨ugt werden sollten und als welcher Typ sie zu interpretieren sind. Zum Beispiel%d heißt intund %f heißt float Falls Celsius den Wert 0 und Fahrenheit den Wert 32 haben, so ist die Ausgabe von printf("%d %d\n",Celsius,Fahrenheit);

0t32, wobei ‘t’ das Leerzeichen bezeichnet.

Nun wird das gleiche Programm mit einer for- Schleife realisiert.

#include <stdio.h>

#define lower -40

#define upper 160

#define step 20 void main (void) {

int Celsius;

printf("Celsius\tFahrenheit\n");

for (Celsius = lower;Celsius<=upper;Celsius+=step) printf("%3d\t%5.1f\n",Celsius, 1.8*Celsius+32.0);

}

Erkl¨arung:

• Die Pr¨aprozessoranweisungen #define lower -40 usw. sind symbolische Namen, die von cpp durch den entsprechenden Ersatztext (hier:-40) ersetzt werden. Diese Substitution ist eine reine textliche Substitution, d.h. die Variable bekommt hier keine Speicherzelle, sondern nur den Wert, der ihr zugewiesen wurde. Diese Konstanten sind keine Variablen. Es ist immer von

(22)

Vorteil solche Programmzahlen mit Hilfe von Pr¨aprozessornamen zu definie- ren. Dann brauchen sie nur noch in einer einzigen Programmzeile ver¨andert zu werden, falls ein neuer Wert benutzt werden soll.

• Die for- Schleife ist derwhile - Schleife sehr ¨ahnlich.

– Celsius = lower ist dieSchleifeninitialisierung. Sie muss i.A. minde- stens 1 Zuweisung enthalten.

– Celsius <= upper ist wie oben.

– Celsius += step ist die Schleifeninkrementierung, d.h. am Ende der Schleife wird Celsius zumstep addiert.

Beispiel:

int n=7, summe = 0 , i;

for (i=1; i<n; i+=1) /*oder i++*/

summe += i;

am Ende istsumme = 21

– Die formatierte Ausgabe mit printf():

printf("%3d\t%5.1f\n",Celsius,1.8*Celsius+32);

%3d bedeutet die int - Ausgabe ist 3 Zeichen breit. F¨uhrende 0-er werden unterdr¨uckt. Die Ausgabe ist (wenn nicht anders angegeben) rechtsb¨undig.

%5.1f bedeutet diefloat - Ausgabe betr¨agt eine gesamte Ausgaben- breite (inklusive Kommastellen) 5 Zeichen mit einem Zeichen nach dem Komma. Diese Ausgabe ist ebenfalls rechtsb¨undig. Das Zeichen \t ist auch ein Steuerzeichen, das den Ausgabecursor auf die n¨achste Tabula- torstelle bringt.

Kontrollzeichen

1. Zeichenkonstanten

Um die Ausgabe zu steuern, stellt C spezielle Zeichen zur Verf¨ugung. Da diese nicht direkt mit der Tastatur eingetippt werden k¨onnen, werden sie durch den Gegenschr¨agstrich ’\’ gekennzeichnet. Sie k¨onnen ¨uberall dort verwendet werden, wo Normalzeichen erscheinen d¨urfen.

(23)

Zeichen Bedeutung

\n Neue Zeile (LF) und Zeilenanfang (CR)

\t Tabulator (einstellbar)

\v Vertikal-Tabulator

\b R¨ucktaste

\r Nur Zeilenanfang (CR)

\f Seitenvorschub (LF)

\a Computer-Klingel (Bell)

\\ Gegenschr¨agstrich ’\’ als Zeichen

\? Fragezeichen ’ ?’ als Zeichen

\’ Anf¨uhrungszeichen ”’ als Zeichen

\’’ G¨ansef¨ußchen ”” als Zeichen

\0 Bin¨ar Null, d.h. 0 (als Wert)

\xhh Hexadezimal hh (als Wert)

\ooo Octalzahl oo (als Wert)

2. Formatzeichen Um Daten als C-Grundtyp (z.B. float oder char) ein- oder auszugeben, k¨onnen diese durch ’%C’, wobei ’C’ ein Zeichen aus der folgenden Tabelle bezeichnet, angegeben werden:

Zeichen Bedeutung

d(bzw. i) Ganzzahlig d.h. Dezimal (mit ’.’ und L¨ange)

o Oktal

x(bzw. X) Hexadezimal

u Ganzzahlig ohne Vorzeichen c Einzelnes Zeichen

s Zeichenkette

f float(Gleitpunkt)

e(bzw. E) Wissentschaftliche Notation g(bzw. G) eoder f(je nachdem)

p Zeigerwert

n Zeiger auf int

% Nur ’%’ ausgeben/einlesen

Außerdem sind folgende Konventionen f¨ur die formatierte Ausgabe von Daten von großer Bedeutung.

Zus¨atzliche Formatierungsinformation 1. Integerausgabezeichen

(24)

%[-]m.nd(oderi): Integerausgabe

’-’ heißt links ausrichten

m= minimum Feldbreite mit linker Ausrichtung (oder rechter bei Verwendung von ’-’).

n= minimale Anzahl von Ausgabeziffern 2. Gleitkommaausgabezeichen

%[-]m.nf(oder odere(bzw. E) oder g(bzw G)): Gleitpunktausgabe

’-’ wie oben mwie oben

n= Genauigkeit, d.h. Anzahl von Ausgabeziffern nach dem Dezimalpunkt

3. Zeichenkettenausgabe

%[-]m.ns:Ausgabe von Zeichenketten

’-’ wie oben mwie oben

n= maximale Anzahl von Zeichen, die auszugeben sind

N.B. Bei m = * und/oder n = * k¨onnen Feldl¨ange und/oder Anzahl der Aus- gabezeichen variabel sein, d.h. in der Argumentliste angegeben werden.

Das Fahrenheitprogramm mit einer do-while - Schleife

#include <stdio.h>

#define lower -40;

Celsius = lower;

do {

printf("%3d\t%5.1f\n",Celsius,1.8*Celsius+32.0);

Celsius+=step;

}

while(Celsius<=upper);

Hier ist nur der Unterschied zur while - Schleife zu erw¨ahnen. Die do-while - Schleife (Rumpf) wird mindestens 1x ausgef¨uhrt, w¨arend eine while - Schleife auch 0x ausgef¨uhrt werden kann.

2.3.1 Bedingte Ausf¨uhrung

Das Flußdiagramm einer konditionellen Anweisung ist in Abb.2.6 gegeben. Die (logische) Bedingung wird ausgewertet. Ist der Wert true (d.h. 6= 0), so wird Anweisung 1 ausgef¨uhrt. Falls der Wert false (d.h. = 0) wird, so wird Anweisung 2 ausgef¨uhrt. Danach geht die Programmausf¨uhrung zur ersten Anweisung nach dieser Konstruktion.

(25)

Abbildung 2.6: Flußdiagramm f¨ur die Bedingte Ausf¨uhrung Syntax

if (Bedingung) Anweisung1;

[else Anweisung2;]

Die eckigen Klammern “[...]” sind als “... ist optional” zu lesen.

Erkl¨arung der Syntax: Der Bedingungsausdruck wird mit 0 verglichen. Ist der Wert 6= 0 (d.h. wahr), so wird Anweisung 1 ausgef¨uhrt, sonst wird Anweisung 2 ausgef¨uhrt, falls die else - Klausel vorhanden ist. Fehlt die else - Klausel, so wird nichts ausgef¨uhrt (wenn der Wert = 0 ist). Danach setzt sich die Program- mausf¨uhrung bei der n¨achsten Anweisung nach der if - Anweisung fort.

Bemerkungen:

• Strichpunkt nach Anweisung 1 beachten!

• If -Anweisungen k¨onnen verschachtelt werden.

1. Beispiel:

int main() {

int m;

scanf("%d",&m);

if (m >= 0) printf("Die Zahl ist positiv\n");

else printf("Zahl ist negativ\n");

printf("Programm fertig"); return 0;

}

2. Beispiel: Zeichen werden direkt von der Tastatur mittels der C- Standardfunktionen getchar() eingelesen. Variablen, die einzelne Zeichen

(26)

enthalten k¨onnen, sind von Typ char. Bsp: ‘a’ , ‘Q’ , ‘\n’ Zeichen werden immer in‘ ’ eingeschlossen.

#include <stdio.h>

int main(void) {

char c;

c = getchar();

if (c >= ’A’) if (c <= ’Z’)

printf("Zeichen war ein Grosser Buchstabe\n");

else if (c < ’a’)

printf("Zeichen war zwischen grossen und kleinen Buchstaben\n");

else if (c <= ’z’)

printf("Zeichen war kleiner Buchstabe\n");

else

printf("Zeichen war ein Buchstabe mit hohem Wert\n");

else

printf("Zeichen war ein Buchstabe mit kleinem Wert\n");

return 0;

} }

Bemerkungen:

• else wird immer mit vorherigem if gepaart.

• statt:

char c;

c = cetchar();

if (c >= ’a’)

kann man auch schreiben:

char c;

if ((c = getchar) >= ’a’)

Da die Buchstaben in einer sog. Asciitabelle, d.h. computerintern gespei- chert sind, ist es logisch, daß diese in 2 Bl¨ocke, geordnet von abis z, bzw.

(27)

von AbisZin dieser Tabelle stehen. Zwischen den großen and kleinen Buch- staben stehen leider andere Zeichen, wie, z. B. ’[’. Im obigen Programm wird diese Logik durch verschachtelte if-Anweisungen realisiert.

• Bei der Auswertng einer logischen Bedingung ist nur das Ergebnis =0 oder Ungleich 0 ausschlaggebend.

• Bedingungen k¨onnen miteinander mittels logischer Operatoren verkn¨upft werden. Sind Bedingung1 und Bedingung2 logische Bedingungen, so sind auch

1. !Bedingung 1 (Negation von Bedingung 1) 2. Bedingung 1 && Bedingung 2 (lese:

”Bedingung 1UNDBedingung 2“‘) 3. Bedingung 1||Bedingung 2 (lese:

”Bedingung 1ODERBedingung 2“) ebenfalls logische Bedingungen. Diese logischen Operatoren sind in der fol- genden Tabelle definiert.

A B !A A&&B A || B

0 0 6= 0 0 0

0 6= 0 6= 0 0 6= 0

6

= 0 0 0 0 6= 0

6

= 0 6= 0 0 6= 0 6= 0

Logische Bedingungen, die aus mehreren verkn¨upften Bedingungen bestehen, werden in C von links nach rechts ausgewertet. Falls der Wert des gesammten logischen Ausdrucks (d.h. eine logische Verkn¨upfung von Programmwerten) bei einem bestimmten Punkt festgestellt werden kann, so wird der Rest des logischen Ausdrucks nicht ausgewertet. Z. B. istA || Bauszuwerten undAhat den logischen Wert 6= 0, damit ist dann der Wert des gesamten Ausdrucks 6= 0. Dieser Prozeß heißt

”short circuit evaluation“.

2.3.2 Die Switch - Anweisung

Es gibt ein kleines Problem mit der if Anweisung: Es gibt nur 2 M¨oglichkeiten zur Auswahl. Mehrere Auswahlm¨oglichkeiten k¨onnen nur mit verschachtelten if- Konstruktionen behandelt werden. Dies hat zur Folge, daß die Programmlogik un¨ubersichtlich wird. Die L¨osung ist dieswitch- Anweisung:

Syntax:

switch (Ausdruck) {

(28)

case konst1: Anweisung 1;

[break;]

case konst2: Anweisung 2;

[break;]

case konstn: Anweisung n;

[break;]

default: Ersatzanweisung;

}

Erkl¨arung:

1. Der Ausdruck wird ausgewertet. Das Ergebnis muss von Typint sein. Das Ergebnis wird mit den Konstanten (m¨ussen alle verschieden von einander sein) verglichen. Wo Gleichheit besteht, wird die zur passenden Konstante zugeh¨orige Anweisung ausgef¨uhrt. breaksorgt daf¨ur, dass aus derswitch- Anweisung gesprungen wird. Falls keine Gleichheit besteht, wird die Ersatz- anweisung (default) ausgef¨uhrt.

2. Fallsbreakin einerwhile-foroderdo-Schleife verwendet wird, verursacht dies, dass aus der Schleife gesprungen wird, d.h. zur ersten Anweisungnach der Schleife.

Beispiel: Numerischer Wert einer eingegebenen r¨omischen Ziffer ausgeben.

#include <stdio.h>

int main() {

char c;

int a = 0;

printf("Roemische Zeichen eingeben: ");

c =getchar();

switch (c) {

case ’I’: a=1; break;

case ’V’: a=5; break;

case ’X’: a=10; break;

case ’L’: a=50; break;

case ’C’: a=100; break;

case ’D’: a=500; break;

case ’M’: a=1000; break;

(29)

}

if (a >0 )

printf("Wert von %c ist %d\n",c,a);

else

printf("Das eingegebene Zeichen war keine roemische Ziffer\n");

}

Um die Funktion der break-Anweisung etwas deutlicher zu sehen, betrachte man folgendes Programm:

#include <stdio.h>

int main(void) {

char c;

c = getchar();

if (((c >= ’A’)&&(c <= ’Z’)) || ((c >= ’a’) && (c <= ’z’))) switch(c){

case ’a’:

case ’A’:

case ’e’:

case ’E’:

case ’i’:

case ’I’:

case ’o’:

case ’O’:

case ’u’:

case ’U’:

printf("Buchstabe war ein Vokal\n");

break;

default:

printf("Buchstabe war ein Konsonant\n");

} else

printf("Zeichen war unzulaessig\n");

printf("Programm vorbei\n");

return 0;

}

1. Das Herausfiltern von zul¨aßigen Buchstaben aus der Eingabe wird mit lo- gischen Verkn¨upfungen realisiert. Dies ist deutlich leichter zu lesen als die Unterscheidung mit verschachtelten if-Anweisungen.

(30)

2. Um festzustellen, daß ein Vokal eingetippt wurde, steht jeder Vokal nach einer case-Anweisung, die allerdings ohne break geschrieben wurde. Nur die letzte case-Answeisung enth¨alt ein break.

Das Formatzeichen %c steht fuer ein einziges Zeichen

Zusammenfassung Folgendes Programm zeigt eine Mischung vonif,forund Zuweisungen.

/* Die ersten PRIMES ganze Zahlen werden geprueft, ob sie Primzahlen sind oder nicht,

d.h. fuer jedes i zwischen 3 und PRIMES wird festgestellt, ob i durch ein j zwischen 3

und i/2 teilbar ist. Wenn nicht, so ist i eine Primzahl */

#include <stdio.h>

#define PRIMES 100 /* PRIMES wird durch 100 vom CPP ersetzt */

int main() {

int i,j,PRIME=1; /* PRIME wird mit 1 initialisiert */

for(i=3; i<PRIMES; i += 2)

/* Ungerades i wird mit 2 inkrementiert */

{

PRIME = 1;

for(j=3; j<i/2; j += 2) /* for innerhalb for! */

{

if(i%j == 0)

{ /* Gibt es Rest 0 nach Teilung i/j */

PRIME = 0; /* Dann ist i keine Primzahl */

break; /* Schleife in j verlassen */

}

} /* end if */

} /* end for j */

if (PRIME) printf("%d ist Primzahl\n",i);

} return 0;

}

(31)

2.4 Backus-Naur-Form

2.4.1 Definition von BNF

Die Syntax (d.h. die Regeln die formal korrekt gebildete S¨atze definieren) einer Programmiersprache wie C wird durch die sog. Metasprache Backus-Naur-Form (BNF) definiert. Die BNF wurde f¨ur die formale Spezifikation von der Program- miersprache ALGOL-60 verwendet und wurde seither bei der Definition von Pro- grammiersprachen verwendet. Eine Definition (oder Regel) in BNF hat 3 Kompo- nenten:

1. Eine linke Seite 2. Das Symbol ::=

3. Eine rechte Seite

Die linke Seite besteht immer aus (einer Bezeichnung) der Konstruktion, die gerade definiert wird. F¨ur sogennante kontext-freie-Sprachenwie u.a. Programmierspra- chen ist diese stets ein einziges abstraktes Symbol, manchmal als

”nicht-terminales Symbol“ bezeichnet. Es handelt sich immer um ein Symbol, das nicht zum Alpha- bet (d.h. die Menge von

”terminalen Symbolen“) der Programmiersprache, die gerade definiert wird, geh¨ort.

Beispiele: <Programm>,<while-Schleife>

Das Symbol unterscheidet sich von den Alphabetsymbolen durch die eckigen Klammern ’<>’ und heißt nicht-terminales Symbol. Die rechte Seite besteht da- gegen aus einer beliebigen Mischung terminaler und nicht-terminaler Symbole.

Beispiele Ein C-Hauptprogramm kann etwas genauer (aber hier nicht zu genau) durch BNF definiert werden:

<Haupt Programm> ::= <Type>main (< Kommand Params>)

<Block>

<Block> ::= {

<Deklaration Teil>;

<Anweisung>;

}

Terminale Symbole werden hier unterstrichen. Um eine Kurzschreibweise f¨ur meh- rere m¨ogliche rechte Seiten f¨ur eine BNF-Regel zu erm¨oglichen, werden diese von- einander durch einen senkrechten Strich ‘|’ getrennt.

(32)

Beispiel Folgende Grammatik f¨ur arithmetische Ausdr¨ucken hat eine kurze Schreibweise:

<A> ::= <A>|<A>+<A>|<A>- <A>

|<A>*<A> |<A>/ <A>

Zwei weitere Vereinabrungen werden in BNF verwendet. Erstens, wenn ein Teil der rechten Seite optional (d.h. nach Belieben) erscheinen darf oder nicht, so so wird er in eckige Klammern gesetzt. Z.B.

<if Anweisung> ::= if (<cond>) <Anweisung>[else<Anweisung>]

Das dr¨uckt aus, daß die else - Klausel f¨ur eine legale if - Anweisung vorhanden sein kann, aber nicht sein muß.

Zweitens, wenn ein Teil der rechten Seite wiederholt auftritt, wird er in ge- schweifte Klammern gesetzt und ‘*’ als Superskript verwendet, um anzugeben, daß 0,1,2, usw. oft wiederholt werden kann, w¨ahrend ‘+’ angibt:

”einmal oder mehr als einmal

”. Das Beispiel

<Bezeichner> ::= <Buchstabe>{ <Buchstabe>|<Zahl>}

besagt, daß ein Bezeichner aus einem Buchstaben, gefolgt von einer beliebigen Mischung von Buchstaben oder Zahlen, besteht. Mit diesen zus¨atzlichen Verein- barungen wird BNF erweiterte BNF(EBNF) genannt.

2.4.2 Kontrollstrukturen in EBNF

Die in dem letzten Abschnitt er¨orterten Kontrollstrukturen werden nun mit Hilfe von EBNF beschrieben.

1. Conditionelle Anweisungen (a) Die if - Anweisung:

<if Anweisung> ::= if (<cond>)<Anweisung>

[else<Anweisung>]

Beschreibung: <Ausdruck>wird ausgewertet und falls ungleich 0, wird die Anweisung nach ifausgef¨uhrt. Ist der Wert des Ausdrucks = 0, so wird die Anweisung nach else, falls vorhanden, ausgef¨uhrt.

(b) Die switch- Anweisung

<switch Anweisung> ::= switch (<Ausdruck>)

<sw Anweisung>

<sw Anweisung> ::= <Anweisung>|

[case<const Ausdruck>

[, <const Ausdruck>] : <Anweisung>]+ | [default : <Anweisung>]

(33)

Beschreibung: Nach Auswertung des <Ausdrucks> wird der Programmab- lauf in dercase - Anweisung, deren<const Ausdr>dem ausgewerteten ent- spricht, fortgesetzt. Alle Anweisungen nach diesercase- Anweisung werden sequentiell ausgef¨uhrt, es sei denn, einebreak- Anweisung taucht auf.

2. Schleifen Hier wird eine weitere, weniger pedantische Schreibweise vorge- nommen:

(a) Die while- Anweisung

while ( <int Ausdruck>)<Anweisung>

Beschreibung: <Anweisung> wird solange wiederholt ausgef¨uhrt, bis

<int Ausdruck>den Wert 0 annimmt. Danach wird der Programmab-

lauf bei der Anweisung, die derwhile- Konstruktion folgt, fortgesetzt.

(b) Die for- Anweisung

for ([<for init>]; [<cond Ausdruck>]; [<inc Ausdruck>])

<Anweisung>

Beschreibung: Schleifenvariablen werden im <for init> Teil initiali- siert. Vor jeder Iteration werden diese Variablen mit <cond Ausdruck>

verglichen. Bei positivem Vergleichsergebnis wird der Rumpf, d.h.

<Anweisung> ausgef¨uhrt und die Schleifenvariablen werden nach

<inc Ausdruck> inkrementiert. Sobald der Vergleich false wird, wird

der Programmablauf am Ende derfor - Konstruktion fortgesetzt.

(c) Die do - Anweisung

do <Anweisung>while (<Ausdruck>);

Beschreibung: Wie bei while, nur wird hier die Auswertung von

<Ausdruck> nach jeder Iteration durchgef¨uhrt.

2.5 Aufz¨ ahlungen und enum

In der Programmierung ist es sinnvoll, Typen zu definieren, deren Elemente pro- blembezogene Namen haben. In C wird dies durch den sogenannten Aufz¨ahlungs- typ enumrealisiert.

Beispiel:

enum Woche {Montag,Dienstag,Mittwoch,Donnesrtag,Freitag,Samstag,Sonntag};

Standardm¨aßig hatMontagden int - Wert 1,Dienstaghat denint- Wert 2, usw.

Woche ist ein selbstgestrickter Typ, weil er vom Programmierer selbst definiert wurde. Dann kann man eine VariableTag diesen Typ’s erkl¨aren, d.h.

enum Woche Tag;

(34)

Bemerkungen

• Das Schl¨usselwortenumist notwendig bei der Deklaration der Variablentag.

• Woche ist der Typ der Variablen.

• Tag ist die Variable, die vom TypWoche ist.

• M¨ogliche Werte f¨ur Tag sind: Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag. Z.B.

– tag = Donnerstag;

– for (tag = Montag;tag =< Sonntag; tag++){...}

Etwas allgemeiner, ist es m¨oglich den Aufz¨ahlungselementen eigene (aufsteigende) int - Werte zu geben. Z. B.

enum Monat{Jan=3,Feb,Mar,April=10,Mai,Juni = 14,Juli,Aug, Sept,Okt,Nov=25,Dez};

Vor dem ANSII Standard hat man Aufz¨ahlungen mittels #define Pr¨aprozessor- konstanten definiert. Also der obige Aufz¨ahlungstyp kann auch so realisiert wer- den:

#define Montag 1

#define Dienstag 2

#define Mittwoch 3

#define Donnerstag 4

#define Freitag 5

#define Samstag 6

#define Sonntag 7

Die folgenden 3 C-Programme zeigen gute und weniger gute Verwendung von Aufz¨ahlungen im Programm. Alle 3 Programme versuchen den Wochenlohn aus 1) dem Stundenlohn, 2) der Anzahl der regul¨aren Arbeitsstunden, 3) der Anzahl der Samstagsarbeitsstunden und 4) der Anzahl am Sonntag abgeleisteter Arbeits- stunden. Am Samstag wird 150% und am Sonntag wird 200% Lohn bezahlt.

Schlechtes Beispiel f¨ur Aufz¨ahlungen in C

#include <stdio.h>

int main() {

float Stunden, Lohn, Stundenlohn;

(35)

int i;

Lohn = 0.0;

printf ("Bitte Stundenlohn eingeben: ");

scanf ("%f", &Stundenlohn);

for (i = 1; i < 8; i++){

printf("Bitte Anzahl Stunden fuer Tag %d eingeben: ",i);

scanf ("%f", &Stunden); printf("\n");

if ((i == 1) || (i == 2) || (i == 3) || (i == 4) || (i == 5)) Lohn = Lohn + Stunden*Stundenlohn;

else if (i == 6)

Lohn = Lohn + 1.5*Stunden*Stundenlohn;

else if (i == 7)

Lohn = Lohn + 2.0*Stunden*Stundenlohn;

else printf("Unmoeglich\n");

} /*for*/

printf("Lohn fuer die Woche ist %8.2f\n", Lohn);

return 0;

}

Bemerkungen

• Dieses Programm ist sehr schwer zu lesen, da die Bedeutung der Variablen inicht klar ist.

• Aufz¨ahlungen werden als bloßeint- Werte realisiert

Eine etwas bessere L¨osung ist der obengenannte Weg mit Pr¨aprozessorkonstanten:

Ein Besseres Beispiel f¨ur Aufz¨ahlungen in C

#include <stdio.h>

#define Montag 1

#define Dienstag 2

#define Mittwoch 3

#define Donnerstag 4

#define Freitag 5

#define Samstag 6

#define Sonntag 7 int main()

{

float Stunden, Lohn, Stundenlohn;

(36)

int Tag;

Lohn = 0.0;

printf ("Bitte Stundenlohn eingeben: ");

scanf ("%f", &Stundenlohn);

for (Tag=Montag; Tag <= Sonntag; Tag++){

printf("Bitte Anzahl Stunden fuer ");

switch (Tag){

case Montag : printf("Montag");

break;

case Dienstag : printf("Dienstag");

break;

case Mittwoch : printf("Mittwoch");

break;

case Donnerstag: printf("Donnerstag");

break;

case Freitag : printf("Freitag");

break;

case Samstag : printf("Samstag");

break;

case Sonntag : printf("Sonntag");

break;

}

printf(" eingeben: ");

scanf ("%f", &Stunden); printf("\n");

switch (Tag){

case Montag: case Dienstag: case Mittwoch: case Donnerstag:

case Freitag : Lohn = Lohn + Stunden*Stundenlohn;

break;

case Samstag : Lohn = Lohn + 1.5*Stunden*Stundenlohn;

break;

case Sonntag : Lohn = Lohn + 2.0*Stunden*Stundenlohn;

break;

}/*switch*/

} /*for*/

printf("Lohn fuer die Woche ist %8.2f\n", Lohn);

return 0;

}

Die beste L¨osung ist Aufz¨ahlungen zu benutzen. Ein TypWochentagenkann durch eine sog. typedefvom Programmierer definiert werden:

typedef enum {Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag}

(37)

Wochentagen;

Diese Typendeklaration besagt, daß der TypWochentagen eigentlich nichts ande- res ist als die obige Aufz¨ahlung. Nun kann man Variablen des TypesWochentagen erkl¨aren.

Bestes Beispiel f¨ur Aufz¨ahlungen

#include <stdio.h>

typedef enum {Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag} Wochentagen;

int main() {

float Stunden, Lohn, Stundenlohn;

Wochentagen Tag;

Lohn = 0.0;

printf ("Bitte Stundenlohn eingeben: ");

scanf ("%f", &Stundenlohn);

for (Tag=Montag; Tag <= Sonntag; Tag++){

printf("Bitte Anzahl Stunden fuer ");

switch (Tag){

case Montag : printf("Montag");

break;

case Dienstag : printf("Dienstag");

break;

case Mittwoch : printf("Mittwoch");

break;

case Donnerstag: printf("Donnerstag");

break;

case Freitag : printf("Freitag");

break;

case Samstag : printf("Samstag");

break;

case Sonntag : printf("Sonntag");

break;

}

printf(" eingeben: ");

scanf ("%f", &Stunden); printf("\n");

switch (Tag){

case Montag: case Dienstag: case Mittwoch: case Donnerstag:

case Freitag : Lohn = Lohn + Stunden*Stundenlohn;

break;

(38)

case Samstag : Lohn = Lohn + 1.5*Stunden*Stundenlohn;

break;

case Sonntag : Lohn = Lohn + 2.0*Stunden*Stundenlohn;

break;

}/*switch*/

} /*for*/

printf("Lohn fuer die Woche ist %8.2f\n", Lohn);

return 0;

}

2.6 Reihungen

Eine Reihung(oderArray) in C ist eine Tabelle von Variablen des gleichen Typs.

So erkl¨art die Anweisung int Tabelle [10];

10 Variablen des Grundtyps int. 10 Variablen, auf die mittels Tabelle [0], ... , Tabelle[9] zugegriffen werden kann.

Bemerkungen

• Der Index einer Reihung ist vom Typeint.

• Die Indizierung f¨angt bei 0 ‘an!!!

• Es ist nicht m¨oglich eine Reihung anders zu indizieren.

Beispiel: Man lese 10 Zahlen ein und gebe die Summe und den Mittelwert von diesen aus.

#include <stdio.h>

#define Anzahl 10 int main()

{

int i,summe=0,Tabelle[Anzahl];

printf("\nBitte %d Zahlen eintippen\n",Anzahl);

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

scanf("%d",&Tabelle[i]);

summe += Tabelle[i];

}

(39)

printf("\n\nDie eingetippten Zahlen waren:");

for(i=0;i<Anzahl;i++)

printf("\nTabelle [%d]=%5d",i,Tabelle[i]);

printf("\n\n%12d %s\n %12.1f %s\n\n", summe,"Ist die Gesammtsumme, ", ((float)summe)/((float) Anzahl),

"ist der Mittelwert.");

return 0;

}

Kommentar:

• %s ist das Formatierungszeichen f¨ur Zeichenketten; Hier f¨ur die Konstanten

“ist die Gesammtsumme” und “ist der Mittelwert”.

• Der Ausdruck ((float)summe)/((float)Anzahl) verwendet den Konver- tierungsoperator (float); z.B. (float)summe wandelt den int- Wert von summe in einen float-Wert um.

• Die Reihung Tabelle wird mit eckigen Klammern erkl¨art. Jede Reihungs- gr¨oße kann mittels der Funktion sizeof abgerufen werden. Die Reihung kann nach Bedarf gr¨oßer als ihre erkl¨arten Grenzen sein.

2.7 Funktionen in C

In der Programmierung kommt es h¨aufig vor, daß bestimmte Programmabschnitte in dem gleichen Programm oder sogar in einem anderen Programm mehrfach ver- wendet werden. Deswegen ist es sinnvoll diese Abschnitte vom Hauptprogramm (d.h. main) herauszunehmen und als eigenst¨andige Unterprogramme zu schreiben.

Unterprogramme haben wie das Hauptprogramm eine Eingabe (Parameterliste) und eine Ausgabe (Returnwert). In C heißt ein Unterprogramm Funktion. Die Eingabe der Funktion erfolgt ¨uber die Argumentenliste ( oder Parameterliste).

Die Ausgabe ist der R¨uckgabewert.

Syntax:

1. Definition einer Funktion:

<Ergebnistyp> <Funktionsname> (<Parameterliste>) {

<Datendeklaration>;

<Anweisung>;

}

(40)

2. Funktionsaufruf

<Funktionsname>( <Liste von Werten>);

Beispiel Das nachfolgende Programm liest als Eingabe:

• eine float- Zahl x

• eine int- Zahln ein und als Ausgabe:

• die Potenz xn berechnet.

1. #include <stdio.h>

2. float potenz(int n, float x) 3. {

4. int i;

5. float temp=1.0;

6. for (i=1;i=<n;i++)

7. temp = temp*x /* temp *= x */

8. return temp;

9. }

10. int main() 11. {

12. int i;

13. float a;

14. for(i=0;i<=6;i++){

15. a = potenz (i,2.0); /*Aufruf von Potenz*/

16. printf("2.0 hoch %d = %f \n ",i,a);

17. return 0;

18. } Erkl¨arung:

• Die Funktionsdefinition vonpotenzfindet in den Zeilen 2-9 statt

• Zeile 2:

– R¨uckgabewert ist float

(41)

– Funktionsname ist potenz

– Formale Parameterliste istint n, float xSie definiert dieTypenund Anzahlder zu ¨ubergebenden Argumente: Erstes Argument ist von Typ float, zweites Argument von Typ int

• Zeile 3 - 9: Der Funktionsrumpfberechnet aus (x, n) die Potenzxn

• Zeile 8: return x; ist die sogenannte return - Anweisung. Dabei wird der R¨uckgabewert xn berechnet und an das aufrufende Programm main() zur¨uckgegeben. Sie ist immer erforderlich, wo ein Wert zur¨uckgegeben wird.

Einzige Ausnahme: void, d.h. kein Typ (nichtswird zur¨uckgegeben. Allge- meine Form:

return <Ausdruck>;

• Zeile 15. a = potenz(i, 2.0) ist der Aufruf der Funktion potenz. Dabei wird der R¨uckgabewert x= 2.0i der Variablen azugewiesen (¨uber return) Die Liste der ¨ubergebenen Werte ist (i, 2.0). Sie muss mit der formalen Parameterliste(float x, int n)in Typ, Reihenfolge und Anzahl ¨uberein- stimmen. Die Funktion potenz ist ¨uberall in main abrufbar

• Zeile 4-5 und 12-13. Die deklarierten Variablen int i, float temp; sind lokale Variablen. Sie sind nur in der Funktion (Zeilen 3-9) sichtbar, d.h.

uberhaupt definiert.¨ int i aus Zeile 4 und int i aus Zeile 12 haben mit- einander nichts zu tun!

• Eine Funktionsdefinition muss nicht unbedingt dem Programmabschnitt (z.B. main), der die Funktion aufruft, voranstehen. Sie muss nicht einmal in der gleichen Datei (oder Modul) erscheinen, in der der Aufruf erscheint.

Grund: Merhere Programme k¨onnen die Funktion aufrufen wollen. In die- sem Fall ist es notwendig einen sogenannten Funktionsprototypen vor dem eigentlichen Aufruf (normalerweise am Anfang der Datei) zu erkl¨aren. Ein Prototyp ist einer Funktionsdefinition sehr ¨anhlich - nur die formalen Para- meternamen k¨onnen weggelassen werden. In diesem Fall lautet der Funkti- onsprototypfloat potenz(float, int).

Beispiel f¨ur eine Funktion Folgendes Beispiel ruft eine Funktion auf, die in einem anderen Modul oder Datei definiert ist.

/* Modul, in dem die Funktion potenz definiert wird. */

float potenz (int n, float x) {

(42)

int i;

float temp;

temp=1.0;

for (i=1;i<=n;i++) temp *= x;

return temp;

}

/* Hauptmodul in dem potenz aufgerufen wird */

#include <stdio.h>

float potenz (float,int); /* Funktionsprototyp */

void main(void) {

int power,i;

float a;

printf("Bitte Basiszahl eintippen: ");

scanf("%f",&a);

printf("\nUnd nun die gewuenschte hoechste Potenz:");

scanf("%d",&power);

for (i=1;i<=power;i++)

printf("%3.1f^%d = %4.1f\n",a,i,potenz(a,i));

}

Wenn die Funktion potenz in der ¨Ubersetzungseinheit - oder Modul - power.c definiert ist und das aufrufende Programm - hier main() - im Modul callit.c definiert ist, dann ist das ganze Programm mit dem Compilierungskommando cc -o callit callit.c power.c

zu ¨ubersetzen.

Syntax eines Funktionsprototypen

<Rueckgabetyp> <Funktionsname> (<Parameterliste>);

Beispiele:

1. float f(); besitzt leere Parameterliste 2. float f(void); auch leere Parameterliste

(43)

Abbildung 2.7:

3. float f(int,char);besitzt eine Liste von Parametertypen

4. float f(int n,char c); Liste von Parametertypen und Parameternamen

2.8 Zeichenketten in C

2.8.1 Definition und Beispiele

In C werden Zeichenketten in Reihungen vom Typchar abgespeichert. Die einge- gebenen Zeichen werden hintereinander in Reihungen abgespeichert. Am Ende der Reihung kommt das Sonderzeichen\0 (Bin¨ar Null), um das Ende der Zeichenkette zu markieren.

Beispiel:

char Botschaft [10] = "Hello":;

Siehe Abb. 2.7 f¨ur eine Verdeutlichung dieses Speichertyps.

Zeichenkettenkonstanten wie ‘‘hello’’ werden in Anf¨uhrungszeichen ‘‘’’

gesetzt. Diese Zeichen werden im Speicher in einer namenlosen Reihung des Grundtyps char untergebracht (auch mit ‘\0’ am Ende). Wie Abb.2.8 zeigt, passt die Zeichenkette genau in dieser Reihung. Reihungen wie diese sind kon- stant und k¨onnen vom Programierer nicht ge¨andert werden. Genau wie es Varia- blen von Typ char gibt, bei denen man einzelne Zeichen speichern kann, gibt es eine Art Variable (d.h. Reihung), bei denen man Zeichenketten speichern kann.

Der Grundtyp solcher Reihungen bleibt char.

Beispiel: Die Deklaration

char name[ ] = "Herbert Groenemeyer";

erkl¨art einechar- Reihungname[]und initialisiert sie mit der Zeichenkette “Her- bert Groenemeyer”. Die Anzahl von Reihungselementen muss nicht hier spezifi- ziert werden, da der Compiler den Platzbedarf berechnen kann. (hier: 20)

(44)

Abbildung 2.8:

Alternativ:

char *name = "Herbert Groenemeyer";

erkl¨art einechar- Reihung name und initialisiert sie mit der Zeichenkette “Herbert Groenemeyer”. Speicherbedarf wird wieder von Compiler berechnet.

Beispiel 1:

#include <stdio.h>

int main() {

char name[BUFSIZ]; /* BUFSIZ vordefiniert in stdio.h*/

printf("Wie heisst du?");

scanf("%s",name); /* name=Anfangsadresse der Zeichenkette*/

/*besser: scanf("%[a-zA-Z ]",name); */

printf("Hello %s\n",name);

return 0;

}

Das Programm kann auch mit den Standardfunktionen gets() und puts() ge- schrieben werden. Vorteil: Keine extra Sonderzeichenfilter bei der Eingabe not- wendig.

Beispiel 2:

#include <stdio.h>

int main() {

char name [BUFSIZ], target[BUFSIZ];

printf("Wie heisst du?");

gets(name);

(45)

puts("Hello"); /*fuegt immer ’\n’ ein*/

puts(name);

return 0;

}

Bemerkung: Statt char Botschaft[50];

Botschaft="Hello\n";

schreibe man

char Botschaft[50]="Hello\n";

da eine Initialisierung immer zu bevorzugen ist. Folgende Variante zeigt eine Zeichenkettenmanipulation.

#include <stdio.h>

void main(void) {

int i;

char name[BUFZIZ], target [BUFSIZ];

puts("Wie heisst du?");

gets(name);

for(i=0;i<BUFSIZ;i++) if(name[i] != ’\0’)

target[i] = name[i] + 1;

else

{target[i] = ’\0’;

break;

}

puts("Hallo");

puts(name);

puts("oder heisst du");

puts(target);

}

(46)

Daten im Programm

Wie in I.2. bereits erkl¨art, ist eine Variable ein Bezeichner, der auf einen Spei- cherplatz (oder Speicherzelle) zeigt. Um Variablen und ihre dazugeh¨origen Daten im Programm zu verwenden, m¨ussen sie erkl¨art werden, z.B. in C nach ‘{’ und vor ausf¨uhrbaren Anweisungen. Bei jeder Vereinbarung muß ihr Typ auch mitan- gegeben werden, d.h. die Art der Speicherklasse, z.B. floatoder int.

Folgende Tabelle gibt die in C vordefinierten elementaren Datentypen an. “Ele- mentare” heißt: “wird nicht bez¨uglich eines anderen Datentyps definiert.”

Die elementaren Datentypen

char sehr kleiner Integerwert oder ASCII Codewert (meistens zwischen -128 und 128)

short “kleiner” Integerwert

int Integer oder ganzzahliger Wert

(normalerweise zwischen -32768 und 32767)

long “grosser” Integerwert (z.B. zwischen -2.147.483.648 und 2.147.483.647)

float Reeller oder Gleitpunktwert

double Reeller oder Gleitpunktwert mit gr¨oßerer Genauigkeit und/oder gr¨oßerem Wert

unsigned char char ohne Vorzeichen (meistens zwischen 0 und 256) unsigned short short ohne Vorzeichen

unsigned int int ohne Vorzeichen (meistens zwischen 0 und 65.535)

unsigned long long ohne Vorzeichen (meistens zwischen 0 und 4.294.967.295) char * Zeichenkette

void “leerer” Datentyp, d.h. verwendet dort, wo genaue Datentypspezifikation nicht gewollt ist.

41

(47)

Hinweise:

• Die eigentliche Gr¨oße der fundamentalen C-Datentypen sind maschinen- abh¨angig

• Machen Sie nie Voraussetzungen ¨uber die Gr¨oße Ihrer Datentypen.

3.1 Speicherorganisation

Alle Daten in C werden intern im Speicher als Folgen von bin¨aren Zahlen gespei- chert. Eine bin¨are Stelle (0 oder 1) heisst ein Bit. Bits sind im allgemeinennicht adressierbar. Bits werden in Bytes gruppiert. Zum Beispiel, 1 Byte = 8 Bit (bei sog. ASCII - Codes), bei IBM Großrechnern 1 Byte = 16 Bits. Ein Byte ist die kleinste adressierbare Einheit im Computerspeicher. Bytes werden weiter grup- piert zu W¨ortern und langen W¨ortern. Die eigentliche Anzahl der Bytes, die ein Wort bilden, ist maschinenabh¨angig, z.B.

IBM 8086 1 Wort = 1 Byte ( 8 Bit) IBM 80286 1 Wort = 2 Byte ( 16 Bit) IBM 80386 1 Wort = 4 Byte ( 32 Bit) CDC 6600 1 Wort = 60 Bit

MARS-432 1 Wort = 128 Bit

Ein Wort ist die Anzahl der Bits, die auf einmal von/nach Speicher nach/von Zentraleinheit transportiert werden k¨onnen.

Bin¨ardaten Wieviele verschiedene Bitfolgen (d.h. Computerdaten) k¨onnen in einem Byte bzw. einem Wort dargestellt werden?

Anzahl der Bitfolgen mit 1 Bin¨arziffer = 2 (0 und 1) Anzahl der Bitfolgen mit 2 Bin¨arziffer = 2 (00,01,10,11) Anzahl der Bitfolgen mit 3 Bin¨arziffer = 2 (000, 001,010,011

100,110,101,111)

Im allgemeinen k¨onnen aus n Bin¨arziffern 2n verschiedene Bitfolgen dargestellt werden.

Beispiel: In einem Byte k¨onnen 28 = 256 verschiedene Bitfolgen darge- stellt werden. Es gibt eine Abhilfe bei der Darstellung von Bytes: Statt ei- ne Folge von 8 Bin¨arbits zu betrachten, gruppiert man diese Bits in 2 Bit- folgen der L¨ange 4. In einer Bitfolge der L¨ange 4 kann man 16 verschiedene (0,1,2, . . . ,15) Daten darstellen. Diese Bitfolgen werden durch die Hexadezimal Zahlen 0,1,2, . . . ,9, A, B, C, D, E, F dargestellt.

(48)

Bin¨arcode Hexadezimalcode Interpretation

0000 0 0∗23+ 0∗22+ 0∗21+ 0∗20 0001 1 0∗23+ 0∗22+ 0∗21+ 1∗20 0010 2 0∗23+ 0∗22+ 1∗21+ 0∗20 0011 3 0∗23+ 0∗22+ 1∗21+ 1∗20 0100 4 0∗23+ 1∗22+ 0∗21+ 0∗20 0101 5 0∗23+ 1∗22+ 0∗21+ 1∗20 0110 6 0∗23+ 1∗22+ 1∗21+ 0∗20 0111 7 0∗23+ 1∗22+ 1∗21+ 1∗20 1000 8 1∗23+ 0∗22+ 0∗21+ 0∗20 1001 9 1∗23+ 0∗22+ 0∗21+ 1∗20 1010 A 1∗23+ 0∗22+ 1∗21+ 0∗20 1011 B 1∗23+ 0∗22+ 1∗21+ 1∗20 1100 C 1∗23+ 1∗22+ 0∗21+ 0∗20 1101 D 1∗23+ 1∗22+ 0∗21+ 1∗20 1110 E 1∗23+ 1∗22+ 1∗21+ 0∗20 1111 F 1∗23+ 1∗22+ 1∗21+ 1∗20 Beispiel: Die 8-stellige Bin¨arfolge X= 1100

| {z } 0101

| {z }

l¨asst sich alsC5, d.h.

X=C516= 1100·0101

darstellen. Diese Hex - Konstante wird in C 0xC5(= 0XC5) geschrieben. Sie stellt ein interne Bitfolge dar und hat zun¨achst nichts mit dem dargestellten Dezimalwert zu tun. Instdio.hliefert die Standartfunktionsizeof(type)die Gr¨oße des Typs type in Bytes zur¨uck.

Speicherbedarf der Fundamentalen Datentypen Folgendes Programm ver- wendet die sizeof Funktion (eigentlich Makro), um die Gr¨oße der elementaren Datentypen auszugeben:

#include<stdio.h>

int main( ) {

printf("Speicherbedarf der fundamentalen Datentypen:\n");

printf("char : %3d bytes\n", sizeof(char));

printf("short : %3d bytes\n", sizeof(short));

printf("int : %3d bytes\n", sizeof(int));

printf("long : %3d bytes\n", sizeof(long));

printf("float : %3d bytes\n", sizeof(float));

printf("double : %3d bytes\n", sizeof(double));

printf("long double : %3d bytes\n", sizeof(long double));

(49)

return 0;

}

Folgendes Programm gibt die Limitender dargestellten Daten aus. Diese Grenzen werden aus der Systemdatei “limits.h” gelesen:

#include<stdio.h>

#include<limits.h>

int main( ) {

printf("signed char min = %d\n", SCHAR_MIN);

printf("signed char max = %d\n", SCHAR_MAX);

printf("signed short min = %d\n", SHRT_MIN);

printf("signed short max = %d\n", SHRT_MAX);

printf("signed int min = %d\n", INT_MIN);

printf("signed int max = %d\n", INT_MAX);

printf("signed long min = %d\n", LONG_MIN);

printf("signed long max = %d\n", LONG_MAX);

printf("unsigned char max = %u\n", UCHAR_MAX);

printf("unsigned short max = u\n", USHRT_MAX);

printf("unsigned int max = %u\n", UINT_MAX);

printf("unsigned long max = %u\n", ULONG_MAX);

return 0;

}

Zum Beispiel, folgende maschinenabh¨angige Zahlen werden f¨ur einen IBM Penti- um mit dem METAWARE C-Compiler ausgegeben. Zuerst werden die Datengr¨oße angegeben:

char: 1 Bytes

short: 2 Bytes

int: 4 Bytes

long: 4 Bytes

float: 4 Bytes

double: 8 Bytes long double: 10 Bytes

Dann werden die Limiten f¨ur diese Datentypen angezeigt:

(50)

signed char min = -128 signed char max = 127 signed short char min = -32767 signed short max = 32767 signed int main = -32768

signed int max = 32767

signed long min = -2147483648 signed long max = 2147483647 unsigned char max = 255

unsigned short max = 65535 unsigned int max = 65535 unsigned long max = 4294967295

3.2 Ganze Zahlen in C

Um ganze Zahlen im Speicher (Computerspeicher) verwenden zu k¨onnen, m¨ussen diese als Bitfolge interpretiert werden.

Regel:

X =Xn−1Xn−2· · ·X1X0

sei eine Bin¨arfolge der L¨angen. Der (unsigned) Integerwert von X ist durch Wert(X) =

n−1

X

i=0

Xi2i gegeben:

Bsp: F¨urn= 8 stellt X = 10101101 denint- Wert:

1 + 0·2 + 1·22+ 1·23+ 0·24+ 1·25+ 0·26+ 1·27 = 173 dar.

Konsequenz:

1. F¨ur n = 8 sind 28 = 256 int - Werte darstellbar: 0 = 00000000, 1 = 00000001, · · ·255 = 11111111

2. F¨urn= 16 sind 216= 65536 int- Werte darstellbar.

3. F¨urn= 32 sind 232= 2494967296 int- Werte dargestellt.

(51)

Problem: Wie werden negativeint- Werte dargestellt? Eine M¨oglichkeit w¨are ein extra Vorzeichenbit zu f¨uhren. Dann wird die interne Computerarithmetik zu umst¨andlich.

3.2.1 Das 2er - Komplement Dualsystem Man betrachte Bin¨arfolgen der Form

X =Xn−1Xn−2· · ·X1X0

(Xi = 0 oder 1) f¨urnfest,n= 8,16,32.

1. Die positiven ‘signed integers’ (int) werden durch Bitfolgen der Form X= 0Xn−2· · ·X1X0

dargestellt.

Bsp.: n=8

00000000 ←→ 0 00000001 ←→ 1 00000010 ←→ 2

... ...

01111111 ←→ 127

2. Die negativen ‘signed integers’ (int) werden durch Bitfolgen der Form X= 1Xn−2· · ·X1X0

gegeben. Aus obenangegebenen Gr¨unden werden diese Zahlennichtals Vor- zeichen + Betrag interpretiert. Eine Computernahe L¨osung wird nun ge- sucht.

Bsp: (n=8)

10000000 ←→ ? 10000001 ←→ ? 10000010 ←→ ?

... ...

11111111 ←→ ?

Die Korrespondenz zwischen diesen Zahlen und positiven ganzen Zahlen wird durch das sog. 2er-Komplementsystem bestimmt:

(52)

Regel:

X = 0Xn2· · ·X1X0

sei eine positive Zahl. Die negative Zahl−X wird durch die bitweise Summe vom (bitweisen) Komplement ~xvon x und 00· · ·01 (=1) gebildet.

Beispiele: n= 8

1. Die negative Zahl -1 zu 1 = 00000001 ist durch die bin¨are Summe −1 = 11111110 + 1 = 11111111 gegeben.

2. F¨ur 2 = 00000010 ist −2 = 11111101 + 1 = 11111110.

3. Da 127 = 01111111, ist −127 = 10000000 + 1 = 10000001

4. Berechnung von -0: 0 = 00000000 und so −0 = 11111111 + 1 = 00000000 = +0. Es ist bemerkenswert, daß im 2er Komplementsystem es nur eine Null gibt.

Bemerkung: Der ¨Ubertrag wird hier vernachl¨assigt. Ein ¨Ubersicht des 2er Komplementsystem f¨urn= 4 ist in Abb.3.1 zu sehen.

Definition Sindx undy zwei Dualzahlen (der gleichen Bitl¨ange), so wirdx−y

als x+ (−y) definiert. Dabei wird die Summe bitweise durch die folgende Regel

Ubertrag 0:¨ Ubertrag 1:¨

0 + 0 = 0 Ubertrag 0¨ 0 + 0 = 1 Ubertrag 0¨ 0 + 1 = 1 Ubertrag 0¨ 0 + 1 = 0 Ubertrag 1¨ 1 + 0 = 1 Ubertrag 0¨ 1 + 0 = 0 Ubertrag 1¨ 1 + 1 = 0 Ubertrag 1¨ 1 + 1 = 1 Ubertrag 1¨

definiert. Die ¨Ubertr¨age in der ersten Zeile sind Eingangs¨ubertr¨age, w¨ahrend die in Zeilen 2 - 5 Ausgangs¨ubertr¨age sind.

Beispiele:

1. Folgende Operation ist eigentlich eine Subtraktion:

01100111 + 10100110

1) 00001101

Bemerkung: Die ‘1’ vor dem ‘)’ ist der ¨Ubertrag !!

Abbildung

Abbildung 1.2: Komponente des Computers (a) Hauptspeicher (intern)
Abbildung 1.3: Ablauf des Compilierungsvorgangs
Abbildung 1.5: Ablauf des Compilierungsvorgangs
Abbildung 2.1: Compilierung von hello.c Das obige Programm w¨ urde 4x nichts machen.
+7

Referenzen

ÄHNLICHE DOKUMENTE

Nach dem einen Jahr in Bötersheim ging ich nicht nur mit Nach dem einen Jahr in Bötersheim ging ich nicht nur mit Nach dem einen Jahr in Bötersheim ging ich nicht nur mit Nach

Deshalb übernahm Philippe Pétain 1940 in Vichy die Regierung des freien Landes- Philippe Pétain 1940 in Vichy die Regierung des freien Landes- Philippe Pétain 1940 in Vichy

Dabei stellt sich die Frage, inwiefern über- haupt eine Nebentätigkeit ausgeübt werden darf, ob der Arbeitgeber der Hauptanstellung nur infor- miert werden muss oder ob sogar

Wenn aber der Arbeitgeber den Arbeitneh- mer dazu auffordert, die Apotheke komplett zu reinigen, also zum Beispiel die Fenster oder gar die Toilette zu putzen, dann kann man die

Ist aber eine Weiterbeschäftigung für den Gekündigten nicht zumutbar ist, dann kann das Gericht eine Abfin- dung anordnen.. Auch bei einer betriebsbeding- ten Kündigung kann sich

Wir haben Minou Hansen (ADEXA) und Bettina Schwarz (BVpta) für Sie gefragt.. Die Berufsvertretungen beraten und unterstützen ihre Mitglieder bei Problemen am

c) Natürlich ist es super, wenn Leute Spaß verstehen, aber: Wenn ich mich gerade mit ernsten Dingen beschäftige, wie z.B. lernen, über Sorgen und Ängste sprechen oder aufräumen, kann

Und diese Menschen haben nun den – keineswegs unbegründeten – Verdacht, daß sie erneut gegängelt werden sollen, von Funktionären einer Gewerkschaft, die sich nicht