Einführung in die Programmiersprache C Steffen Börm Christian-Albrechts-Universität zu Kiel Sommersemester 2014 S. Börm (CAU Kiel) Einführung C Sommersemester 2014 1 / 68 Übersicht 1 Einleitung 2 Daten 3 Ausdrücke und Funktionen 4 Fallunterscheidungen und Schleifen 5 Zeiger und Arrays 6 Programmbibliotheken 7 Praxis S. Börm (CAU Kiel) Einführung C Sommersemester 2014 2 / 68 Wieso C? Flexibel: Volles Potential des Computers steht zur Verfügung. Effizient: Maschinennah, viele Operationen können durch den Compiler unmittelbar in Maschinenbefehle verwandelt werden. Beherrschbar: Es passiert nur, was wir explizit programmieren. Verbreitet: Eine der am häufigsten genutzten Programmiersprachen, verwandt mit C++, Objective C und Java. Vorsicht: C verzeiht Fehler nicht so leicht wie andere Sprachen. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 3 / 68 Termine Für die C-Einführung werden die Termine der Vorlesungen Algorithmen und Datenstrukturen“ und Organisation und Architektur ” ” von Rechnersystemen“ der ersten Semesterwoche verwendet: Montag, 14. April 2014, CAP3 Hörsaal 2, 10 c.t. (Börm) Montag, 14. April 2014, CAP3 Hörsaal 2, 16 c.t. (von Hanxleden) Dienstag, 15. April 2014, CAP3 Hörsaal 2, 9 s.t. (von Hanxleden) Freitag, 25. April 2014, CAP3 Hörsaal 2, 8 c.t. (Börm) Hinzu kommen praktische Aufgaben und Ergänzungen in den jeweiligen Übungen. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 4 / 68 Literatur Wir können im Rahmen der Vorlesung nur einen kurzen Einblick in die wichtigsten Aspekte der Sprache C geben, deshalb empfehlen wir für eine eingehendere Beschäftigung die folgenden Quellen: D. Ritchie und B. W. Kernighan: Programmieren in C P. Bäumle-Courth, T. Schmidt: Praktische Einführung in C RRZN Hannover: Die Programmiersprache C. Ein Nachschlagewerk. Wikibook C-Programmierung C-Howto.de S. Börm (CAU Kiel) Einführung C Sommersemester 2014 5 / 68 Entwicklungsumgebung Ein C-Programm durchläuft mehrere Stufen, bevor es ausgeführt werden kann: Editor: Wir schreiben das Programm als gewöhnliche Textdatei. Präprozessor: Einzelne Zeichenketten können ersetzt werden, beispielsweise um Abkürzungen zu verwenden. Compiler: Der resultierende Text wird in die Maschinensprache eines Computers übersetzt. Linker: Es entsteht eine für das Betriebssystem ausführbare Datei. Loader: Die ausführbare Datei wird in den Speicher geladen. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 6 / 68 Software Für die Bearbeitung der Übungsaufgaben ist ein Computer mit C-Entwicklungsumgebung erforderlich. Windows: Visual Studio Express Windows: MinGW / MSys (Unix-ähnlich) MacOS: Xcode Linux: GNU Compiler Collection Alternative: Virtuelles Linux-System unter VirtualBox S. Börm (CAU Kiel) Einführung C Sommersemester 2014 7 / 68 Beispiel: GNU Compiler Collection Präprozessor und Compiler: Überführen den Quelltext example.c in eine Objektdatei example.o, die Maschinenbefehle und Linker-Informationen, beispielsweise über Funktionsnamen, enthält. gcc -c example.c Linker: Fügt Objektdateien und Bibliotheken zu einer ausführbaren Datei example zusammen. gcc example.o -o example Programmstart: Unmittelbar per Befehlszeile. ./example S. Börm (CAU Kiel) Einführung C Sommersemester 2014 8 / 68 Übersicht 1 Einleitung 2 Daten 3 Ausdrücke und Funktionen 4 Fallunterscheidungen und Schleifen 5 Zeiger und Arrays 6 Programmbibliotheken 7 Praxis S. Börm (CAU Kiel) Einführung C Sommersemester 2014 9 / 68 Variablen Eine Variable ist in C ein Speicherbereich, der eine gewisse Größe hat und an einer gewissen Adresse beginnt. Variablen werden im Rahmen einer Variablendefinition mit einem Namen versehen, um dem Programmierer die Arbeit zu erleichtern. Variablennamen dürfen aus Buchstaben (a, . . . , z, A, . . . ,Z), Ziffern (1, . . . ,9, 0) und Unterstrichen ( ) bestehen, sie dürfen allerdings nicht mit einer Ziffer beginnen. Um die Zuordnung eines geeigneten Speicherbereichs zu einem Variablennamen kümmern sich Compiler, Linker und Loader. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 10 / 68 Typen Damit der Compiler weiß, wie eine Variable zu interpretieren ist, wird jeder Variablen ein Typ zugeordnet. Typen können mit Namen versehen sein, für die dieselben Regeln wie für Variablennamen gelten. Einige Typen sind vordefiniert, beispielsweise int für ganze Zahlen, float und double für Gleitkommazahlen oder char für Buchstaben. Manche Typen können mit den Schlüsselworten short, long und unsigned modifiziert werden. Mathematik: Ein Typ kann als eine Menge von Werten interpretiert werden, eine Variable dieses Typs entspricht dann einem Element dieser Menge. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 11 / 68 Variablendefinitionen Eine Variable wird definiert, indem ihr Typ und ihr Name durch Leerraum getrennt angegeben werden, den Abschluss bildet ein Semikolon: int x; int 2y; long int y4; short int w_3; float x+y; float x_plus_y; char _Next; unsigned char a; S. Börm (CAU Kiel) 4 8 4 4 8 4 4 4 ganze Zahl, Variablenname x Variablenname beginnt mit Ziffer lange ganze Zahl namens y4 kurze ganze Zahl namens w_3 Variablenname enthält Zeichen +“ ” Gleitkommazahl namens x_plus_y Buchstabe namens _Next Buchstabe ohne Vorzeichen namens a Einführung C Sommersemester 2014 12 / 68 Verbünde Mit dem Schlüsselwort struct können wir neue Typen aus bereits definierten Typen zusammensetzen. struct { int a; float x; } z; Die beiden Komponenten können wir dann mit z.a und z.x erreichen, sie sind Variablen des Typs int beziehungsweise float. Mathematik: Ein Typ entspricht einer Menge T , ein Verbundtyp entspricht dem kartesischen Produkt mehrere Mengen T = X × Y . S. Börm (CAU Kiel) Einführung C Sommersemester 2014 13 / 68 Typdefinitionen Spätestens bei Verbünden empfiehlt es sich, neu definierten Typen einen eigenen Namen zu geben. Diesem Zweck dient das Schlüsselwort typedef: typedef unsigned int uint; typedef struct { float re; float im; } complex; typedef struct { int r; int g; int b; } color; Die neuen Typen können in Variablendefinitionen verwendet werden: uint i; complex z; color background; S. Börm (CAU Kiel) Einführung C Sommersemester 2014 14 / 68 Übersicht 1 Einleitung 2 Daten 3 Ausdrücke und Funktionen 4 Fallunterscheidungen und Schleifen 5 Zeiger und Arrays 6 Programmbibliotheken 7 Praxis S. Börm (CAU Kiel) Einführung C Sommersemester 2014 15 / 68 Operatoren für arithmetische Berechnungen Die Inhalte von Variablen werden durch Operatoren verwendet und verändert. Arithmetische Operatoren führen Berechnungen durch. x + y 3 * a y - z b / 17 c % 5 Zuweisungsoperatoren verändern Werte von Variablen. a = b x += y a *= 3 i ++ ++ i S. Börm (CAU Kiel) y -= z b /= 17 j --- j Einführung C c %= 5 Sommersemester 2014 16 / 68 Operatoren für die Bitmanipulation int- und char-Variablen können auch als Folge von Bits interpretiert und entsprechend manipuliert werden. Bitweise Verknüpfungen führen Oder-, Exklusiv-Oder-, Und- sowie Komplement-Operationen für alle Bits des Ergebnisses durch. x | y x |= y ˜f p ˆ q p ˆ= q a & b a &= b Bitweises Schieben nach links“ und rechts“, wobei das niedrigste Bit ” ” rechts“ steht. ” x << 5 y >> 3 a <<= 2 b >>= 4 S. Börm (CAU Kiel) Einführung C Sommersemester 2014 17 / 68 Ausdrücke Mehrere Operatoren können zu komplexeren Termen zusammengesetzt werden. Dabei gelten die üblichen Regeln für arithmetische Ausdrücke, beispielsweise Punkt- vor Strichrechnung“. Mit Klammern lässt sich ” die Reihenfolge der Anwendung der Operatoren steuern. Für konstante Größen können Literale verwendet werden, beispielsweise 3 für die Zahl drei oder 3.141 als Näherung der Zahl π. x = (a + 7) * 3 a += 3 * b + 17 p = q = r S. Börm (CAU Kiel) Einführung C Sommersemester 2014 18 / 68 Typkonvertierung Mit dem Cast-Operator können wir Ausdrücke eines Typs in andere Typen umwandeln: int a = b = c = d = a; int b; float c; int d; 17; a / 3; ((float) a) / 3; 23 / ((int) c); Bei der Berechnung von b wird die ganzzahlige Division verwendet, bei der von c die konventionelle, bei der von d wiederum die ganzzahlige, da in diesem Fall c abgerundet wird. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 19 / 68 Funktionen Eine Funktion beschreibt eine Folge von Anweisungen, die der Reihe nach ausgeführt werden. Sie werden nach demselben Schema wie Variablen und Typen mit Namen bezeichnet. Die einfachste Anweisung ist ein mit einem Semikolon abgeschlossener Ausdruck, der ausgeführt wird. void compute() { x = 4 * y + 3; } Das Schlüsselwort void gibt in diesem Beispiel an, dass die Funktion keinen Wert zurückgibt. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 20 / 68 Parameter und Rückgabewert Eine Funktion kann eine Anzahl von Parametern erhalten. Parameter sind Variablen, denen Werte zugewiesen werden, wenn wir die Funktion aufrufen. Eine Funktion kann mit der Anweisung return ein Ergebnis zurückgeben. double area_triangle(double height, double base) { return height * base / 2.0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 21 / 68 Funktionen ohne Rückgabewert Falls eine Funktion keinen Wert zurückgeben soll, beispielsweise weil ihre Ergebnis anders übermittelt werden können, können wir den vordefinierten Typ void verwenden: void stop_program() { exit(); } Der Typ void weist die Besonderheit auf, dass wir keine Variablen oder Parameter dieses Typs definieren dürfen. Allerdings dürfen wir (dazu später mehr) Zeiger auf diesen Typ verwenden. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 22 / 68 Funktionsaufrufe Der Aufruf einer Funktion ist ein weiterer Operator, der die Parameter übergibt und das Ergebnis der Funktion als Wert zurückgibt. a = area_triangle(3.0, 1.5) b = area_triangle(4.0, 2.0) - 3.0 * area_triangle(1.0, 0.5) Die Ausführung unseres Programms beginnt mit dem Aufruf der Funktion main durch das System. Einfachstes Beispiel: int main() { return 0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 23 / 68 Lokale Variablen Zu Beginn einer Funktion können eine Reihe von Variablen definiert werden, die nur innerhalb der Funktion existieren und sichtbar sind: int main() { int i; int j; i = 3 + 5 * 7; j = i * i - 2; return j; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 24 / 68 Beispiel: Fläche eines Dreiecks Aufgabe: Berechne die Fläche des Dreiecks ABC. Mathematik: Fläche ist halbe Determinante der Kantenvektoren: 1 Bx − Ax Cx − Ax det By − Ay Cy − Ay 2 float area_triangle3(float ax, float ay, float bx, float by, float cx, float cy) { float bax; float bay; float cax; float cay; bax = bx - ax; bay = by - ay; cax = cx - ax; cay = cy - ay; return 0.5 * (bax * cay - bay * cax); } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 25 / 68 Übersicht 1 Einleitung 2 Daten 3 Ausdrücke und Funktionen 4 Fallunterscheidungen und Schleifen 5 Zeiger und Arrays 6 Programmbibliotheken 7 Praxis S. Börm (CAU Kiel) Einführung C Sommersemester 2014 26 / 68 Fallunterscheidungen Häufig müssen wir in Abhängigkeit von Variablen unterschiedliche Rechenoperationen ausführen. if(x) y = 2.0 * a + b; else z = 1.5 * b - a; Falls x ungleich null ist, werden die Befehle in der zweiten Zeile ausgeführt, sonst die in der vierten. Die dritte und vierte Zeile können wir wegfallen lassen, falls wir sie nicht benötigen. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 27 / 68 Operatoren für Wahrheitswerte In C steht der Wert 0 für falsch“ und jeder andere Wert für wahr“. ” ” Vergleichsoperatoren ergeben 1, falls eine Relation gilt, und 0 sonst. x < y x <= y a > b a >= b p != q p == q Vorsicht: x = y ist eine Zuweisung, x == y ist ein Vergleich. Logische Operatoren führen Oder-, Und- und Nicht-Operationen durch. x || y a && b !f Vorsicht: In x || y wird y nur ausgewertet, falls x gleich null ist, und in x && y wird es nur auswertet, falls x ungleich null ist. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 28 / 68 Beispiel: Fakultät per Rekursion Induktive Definition: Die Fakultät der Zahl 0 ist 1. Die Fakultät der Zahl n ist dasselbe wie das Produkt aus n und der Fakultät der Zahl n-1. unsigned int factorial(unsigned int n) { if(n == 0) return 1; else return n * factorial(n-1); } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 29 / 68 Beispiel: Fallunterscheidung in Ausdrücken Wir können in C eine Fallunterscheidung auch direkt in einen Ausdruck hineinschreiben, beispielsweise lässt sich das Maximum der Zahlen a und b wir folgt berechnen: maximum = (a < b ? b : a) Falls das erste Argument ungleich null ist, wird das zweite Argument ausgewertet, sonst das dritte. Die Berechnung der Fakultät lässt sich so kürzer schreiben: unsigned int factorial(unsigned int n) { return (n == 0 ? 1 : n * factorial(n-1)); } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 30 / 68 Blockanweisungen Formal unterscheidet die if-Anweisung nur zwischen zwei Anweisungen, die ausgeführt werden sollen. Wir können allerdings mehrere Anweisungen und sogar lokale Variablen mit geschweiften Klammern {, } zu einer Blockanweisung zusammenfassen: int i; int j; if(i == 3) { int j; j = i * 7 + 5; i = j / 2; } else i = 2 * i + 3; S. Börm (CAU Kiel) Einführung C Sommersemester 2014 31 / 68 Mehrfache Fallunterscheidung Wir können mit einer einzigen Fallunterscheidung auch mehrere mögliche Werte eines Ausdrucks behandeln: switch(x) { case 0: a = b; break; case 1: a = c; break; default: a = b + c; } Vorsicht: Ohne break werden auch die folgenden Befehle ausgeführt. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 32 / 68 while-Schleife Wir können dafür sorgen, dass eine Folge von Anweisungen wiederholt wird, solange eine Bedingung erfüllt ist: i = 1; j = 0; while(i < 1000) { i *= 2; j ++; } Der Schleifenrumpf, der Anweisungsblock in geschweiften Klammern, wird ausgeführt, solange die Auswertung der Schleifenbedingung, des Ausdrucks in den runden Klammern, einen von null verschiedenen Wert ergibt. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 33 / 68 Beispiel: Euklidischer Algorithmus Aufgabe: Berechnung des größten gemeinsamen Teilers. if(a == 0) return b; while(b != 0) { if(a > b) a -= b; else b -= a; } return a; Idee: Der ggT beider Zahlen bleibt in jedem Schritt unverändert. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 34 / 68 Beispiel: Ägyptisches Potenzieren Aufgabe: Berechne y = x p für eine natürliche Zahl p. y = 1; z = x; while(p > 0) { if(p % 2 == 1) y *= z; p /= 2; z *= z; } return y; Idee: Binärdarstellung p = pk 2k + pk−1 2k−1 + . . . + p1 21 + p0 führt zu k k−1 x p = (x 2 )pk (x 2 )pk−1 . . . (x 2 )p1 x p0 . Vorsicht: Division ganzer Zahlen ergibt eine ganze Zahl. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 35 / 68 for-Schleife Häufig sind vor dem Eintritt in eine Schleife Variablen zu setzen und in jedem Durchlauf zu aktualisieren. j = 0; for(i=1; i<=100; i++) j += i; Der erste Ausdruck der for-Anweisung wird vor Eintritt in die eigentliche Schleife ausgeführt. Die Schleife wird wiederholt, solange der zweite Ausdruck ungleich null ist. Am Ende jedes Schleifendurchlaufs wird der dritte Ausdruck ausgewertet. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 36 / 68 for-Schleife als while-Schleife Jede for-Schleife kann auch als while-Schleife geschrieben werden: for(i=7; i>0; i/=2) j++; ist äquivalent zu i = 7; while(i > 0) { j++; i /= 2; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 37 / 68 Beispiel: Fibonacci-Folge Aufgabe: Berechne die durch f0 = 0, f1 = 1, fk = fk−1 + fk−2 gegebene Fibonacci-Folge. if(k == 0) return 0; g = 0; f = 1; for(i=1; i<k; i++) { h = g; g = f; f = g + h; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 38 / 68 Übersicht 1 Einleitung 2 Daten 3 Ausdrücke und Funktionen 4 Fallunterscheidungen und Schleifen 5 Zeiger und Arrays 6 Programmbibliotheken 7 Praxis S. Börm (CAU Kiel) Einführung C Sommersemester 2014 39 / 68 Zeiger Eine Besonderheit der Sprache C ist die Möglichkeit, unmittelbar auf die Adressen von Variablen zuzugreifen. Wenn wir den Adressoperator & auf eine Variablen anwenden, erhalten wir einen Zeiger auf den zu ihr gehörenden Speicherbereich. void pointer() { int k; int *k_ptr; k_ptr = &k; } Zeiger können genauso in Variablen gespeichert werden wie Zahlen, der Typ T * beschreibt dabei Zeiger auf Variablen des Typs T. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 40 / 68 Dereferenzieren Indem wir den Inhaltsoperator * auf einen Zeiger anwenden, können wir den Speicherbereich, auf den er zeigt, wie eine Variable verwenden: int dereference() { int k; int *k_ptr; k_ptr = &k; *k_ptr = 7; return k; } Die Zuweisung an *k_ptr hat hier denselben Effekt wie eine Zuweisung an k, die Funktion gibt immer 7 zurück. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 41 / 68 Zeiger als Funktionsparameter Mit Zeigern kann eine Funktion mehrere Ergebnisse zurückgeben: void sum_and_product(float x, float y, float *sum, float *product) { *sum = x + y; *product = x * y; } int main() { float a; float b; sum_and_product(7, 5, &a, &b); return 0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 42 / 68 Arrays Wir können Variablen definieren, die mehrere Elemente eines bestimmten Typs aufnehmen, indem wir Arrays verwenden: void array() { int x[4]; x[0] = 3; x[2] = 8; x[1] = 5; x[3] = x[1] + x[2]; } Vorsicht: Die Elemente eines Arrays der Länge n sind mit 0 bis n-1 durchnumeriert, im Beispiel darf etwa x[4] nicht verwendet werden. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 43 / 68 Arrays und Zeiger In fast allen Fällen unterscheidet C nicht zwischen einem Array und einem Zeiger auf das erste Element des Arrays: void array_pointer() { int x[4]; int *x_ptr; x_ptr = x; x_ptr[2] = 7; x[3] = x_ptr[2]; } Vorsicht: Dadurch wird es dem Compiler unmöglich, die Korrektheit von Arrayzugriffen zu prüfen. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 44 / 68 Zeigerarithmetik Wenn wir zu einem Zeiger eine ganze Zahl hinzuaddieren, erhalten wir einen Zeiger auf einen anderen Eintrag des Arrays: void pointer_add() { float x[5]; float *x0_ptr, *x3_ptr, *x2_ptr; x0_ptr = x; x3_ptr = x0_ptr + 3; x2_ptr = x3_ptr - 1; } Vorsicht: Wir können in dieser Weise Werte anderer Variablen und womöglich sogar das Programm selbst verändern. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 45 / 68 Strings Zeichenketten werden in C als Arrays des Typs char dargestellt. Um Zeichenketten unterschiedlicher Länge handhaben zu können, wird das Ende der Zeichenkette durch eine Null markiert. Wie Zahlen können auch konstante Strings durch Literale definiert werden, nämlich durch in doppelte Anführungszeichen eingeschlossene Buchstabenfolgen. char *a; a = "Algorithmen und Datenstrukturen\n"; Mit einem Backslash \ können Sonderzeichen eingefügt werden, hier etwa durch \n der Wechsel in eine neue Zeile. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 46 / 68 Beispiel: Länge eines Strings int length_string(char *a) { int len; len = 0; while(*a != 0) { len++; a++; } return len; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 47 / 68 Beispiel: Suchen eines Teilstrings char * findsub_string(char *a, char *b) { char *asub, *bsub; while(*a != 0) { asub = a; bsub = b; while(*bsub != 0 && *asub == *bsub) { asub++; bsub++; } if(*bsub == 0) return a; a++; } return 0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 48 / 68 Beispiel: Befehlszeilenparameter Der Funktion main werden in einer Unix-Umgebung auch die in der Befehlszeile angegebenen Parameter als Funktionsparameter übergeben. Das erfolgt über ein Array aus Strings: int main(int argc, char **argv) { int i; for(i=1; i<argc; i++) if(strcmp(argv[i], "--help") == 0) print_help_message(); return 0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 49 / 68 Verbünde und Zeiger Ein Verbundtyp kann Zeiger auf sich selbst enthalten, wenn wir bei der Definition dem Verbundtyp einen Namen geben: typedef struct list_element_s { int entry; struct list_element_s *next; } list_element; Für den Zugriff auf die Komponenten eines Verbundtyps, auf die ein Zeiger verweist, gibt es als Abkürzung den Operator ->: list_element *this; this->entry = 42; this->next = 0; S. Börm (CAU Kiel) Einführung C Sommersemester 2014 50 / 68 Übersicht 1 Einleitung 2 Daten 3 Ausdrücke und Funktionen 4 Fallunterscheidungen und Schleifen 5 Zeiger und Arrays 6 Programmbibliotheken 7 Praxis S. Börm (CAU Kiel) Einführung C Sommersemester 2014 51 / 68 Funktionsdeklaration Wir können eine Funktion dem Compiler bekannt machen, ohne sie vollständig zu definieren. Statt einer Funktionsdefinition ist es dann eine Funktionsdeklaration: extern float area_circle(float radius); Eine deklarierte Funktion kann wie jede andere aufgerufen werden, allerdings muss spätestens zur Laufzeit des Programms auch eine vollständige Definition vorliegen. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 52 / 68 Header-Dateien Über Funktionsdeklarationen lässt sich einfach der Zugriff auf externe Programmbibliotheken und das Betriebssystem umsetzen. Die Deklarationen werden in einer separaten Datei zusammengefasst, die wir mit einer Präprozessor-Anweisung einbinden können: #include <math.h> int main() { double x; double y; x = sin(3.0) / cos(3.0) - tan(3.0); y = log(3.5) + exp(-2.5); return 0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 53 / 68 Standardbibliothek Die Kommunikation mit dem Betriebssystem, also insbesondere auch mit dem Anwender, erfolgt über eine Standardbibliothek, die eine Reihe von Funktionen enthält, unter anderem für die Ein- und Ausgabe von Zeichen, die Anforderung und Freigabe von Speicher, die Manipulation von Zeichenketten oder mathematische Operationen. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 54 / 68 Ausgabe von Daten #include <stdio.h> int main() { int i; printf("Hallo\n"); i = 3 * 4 + 7; printf("3 * 4 + 7 = %d\n", i); return 0; } Der erste Parameter definiert das Format der Ausgabe, mit % markierte Platzhalter werden durch die weiteren Parameter ersetzt. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 55 / 68 Eingabe von Daten #include <stdio.h> int main() { int i; int j; scanf("%d", &i); scanf("%d", &j); printf("%d mod %d = %d\n", i, j, i % j); return 0; } Auch hier definiert der erste Parameter, welche Daten die Funktion erwarten soll. Vorsicht: Die weiteren Parameter müssen Zeiger sein. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 56 / 68 Speicherverwaltung #include <stdlib.h> int main() { int *a; a = (int *) malloc(sizeof(int)); *a = 17; free(a); return 0; } Der Größenoperator sizeof ermittelt die Speichergröße eines Typs oder einer Variablen, Der Cast-Operator (T) interpretiert sein Argument als Typ T. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 57 / 68 Beispiel: Einfach verkettete Liste, Teil 1 typedef struct list_s { int entry; struct list_s *next; } list; list * addto_list(int entry, list *s) { list *new; new = malloc(sizeof(list)); new->entry = entry; new->next = s; return new; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 58 / 68 Beispiel: Einfach verkettete Liste, Teil 2 list *s, *x; s = addto_list(17, 0); s = addto_list(36, s); s = addto_list(5, s); for(x=s; x; x=x->next) printf("%d\n", x->entry); S. Börm (CAU Kiel) Einführung C Sommersemester 2014 59 / 68 Arrays variabler Größe #include <stdlib.h> int main() { int *fib; int n; int i; n = 17; fib = (int *) malloc(sizeof(int) * n); fib[0] = 0; fib[1] = 1; for(i=2; i<n; i++) { fib[i] = fib[i-1] + fib[i-2]; } free(fib); return 0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 60 / 68 Beispiel: Stringoperationen Da Strings nur Arrays sind, würde der Zuweisungsoperator = lediglich den Zeiger auf ihr erstes Element kopieren. Deshalb brauchen wir Hilfsfunktionen, die Speicher anfordern und die Arrays kopieren. #include <string.h> int main() { char *a; char *b; char *c a = "Algorithmen"; b = " und Datenstrukturen"; c = (char *) malloc(sizeof(char) * (strlen(a) + strlen(b) + 1)); strcpy(c, a); strcat(c, b); return 0; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 61 / 68 Übersicht 1 Einleitung 2 Daten 3 Ausdrücke und Funktionen 4 Fallunterscheidungen und Schleifen 5 Zeiger und Arrays 6 Programmbibliotheken 7 Praxis S. Börm (CAU Kiel) Einführung C Sommersemester 2014 62 / 68 Compiler-Warnungen Gute Compiler können eine Reihe typischer Programmierfehler erkennen, die in formal korrekten C-Programmen auftreten. Das folgende Programmfragment ist korrektes C, bewirkt aber vermutlich nicht das vom Programmierer Erwartete: if(x=y) x += 3; Wenn wir den GNU-Compiler mit zusätzlichen Parametern aufrufen, erhalten wir eine Warnung: gcc -c -Wall -Wextra ifdemo.c warning: suggest parentheses around assignment used as truth value S. Börm (CAU Kiel) Einführung C Sommersemester 2014 63 / 68 Defensives Programmieren Zusicherungen: C bietet eine einfache Möglichkeit, die Gültigkeit logischer Bedingungen zu überprüfen: #include <assert.h> assert(p != 0); p->entry = 5; Falls die in assert angegebene Bedingung nicht wahr ist, wird das Programm mit einer Fehlermeldung beendet. Initialisierung: Es ist häufig sinnvoll, Variablen rechtzeitig in einen definierten Zustand zu bringen. Das gilt insbesondere bei der Freigabe dynamischen Speichers: free(p); p = 0; S. Börm (CAU Kiel) Einführung C Sommersemester 2014 64 / 68 Verständlichkeit des Programmtexts Um ein Programm verständlich zu halten, empfiehlt es sich, Kommentare einzufügen, die zwischen /* und */ eingeschlossen werden: int i; /* Schleifenvariable */ /* Zahlen von 1 bis 10 aufsummieren */ j = 0; for(i=1; i<=10; i++) j += i; Kommentare werden durch den Präprozessor durch Leerraum ersetzt. Die Lesbarkeit eines Programms lässt sich weiter verbessern, indem konsequent Leerzeilen zwischen zusammengehörenden Anweisungen eingefügt und Rümpfe von Schleifen und Fallunterscheidungen durch Einrückungen kenntlich gemacht werden. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 65 / 68 Konstante Parameter Insbesondere bei Funktionen, die mit Zeigern arbeiten, ist es sinnvoll, das Schlüsselwort const einzusetzen, um sicher zu stellen, dass über bestimmte Zeiger nur lesend auf Variablen zugegriffen werden kann. int string_compare(const char *a, const char *b) { while(*a != 0 && *a == *b) { a++; b++; } if(*a == *b) return 0; else if(*a < *b) return -1; else return 1; } S. Börm (CAU Kiel) Einführung C Sommersemester 2014 66 / 68 Debugger Mit einem Debugger können übersetzte Programme Schritt für Schritt ausgeführt und Variablen untersucht werden. Zu der GNU Compiler Collection gehört beispielsweise der GNU Debugger gdb. gdb debugtest break debugtest.c:10 run print i step cont Für die Analyse von Speicherzugriffen ist valgrind ein hilfreiches Werkzeug, das das Programm auf einem simulierten Rechner ausführt und sein Verhalten protokolliert. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 67 / 68 Make Für umfangreichere Projekte bietet es sich an, den Aufruf der Compiler und Linker zu automatisieren. Das Programm make berücksichtigt dabei die Abhängigkeiten zwischen den einzelnen Dateien, die wir ihm in Form der Datei Makefile zur Verfügung stellen müssen: modul1.o: modul1.c modul1.h modul2.o: modul2.c modul2.h prog.o: prog.c modul1.h modul2.h prog: prog.o modul1.o modul2.o Für jede Abhängigkeit können wir (mit − → − − eingerückt) Befehle − → angeben, mit denen das Ziel aus den Quellen erzeugt werden kann. S. Börm (CAU Kiel) Einführung C Sommersemester 2014 68 / 68