Programmieren in C Prof. Dr. Rudolf Berrendorf [email protected] Fachbereich Angewandte Informatik Fachhochschule Bonn-Rhein-Sieg Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 2 Sie denken Sie kennen schon C? Wie wär‘s mit westley.c aus dem IOCCC 1988 (kein Vorbild!): #define _ -F<00||--F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 3 Literatur • R.Berrendorf: Einführung in die Programmiersprache C Benutzerhandbuch KFA-ZAM-BHB-0095, Forschungszentrum Jülich http://www.fz-juelich.de/zam/docs/ http://www.inf.fh-bonn-rhein-sieg.de/ lokale Kopie • S.P.Harbison, G.L.Steele Jr.: C: A Reference Manual 5. Ausgabe, Prentice Hall, 2002 • B.W.Kernighan, D.M.Ritchie: Programmieren in C 2. Ausgabe, Hanser, 1990 • Bjarne Stroustrup: Die C++ Programmiersprache 4. Auflage, Addison Wesley, 2000 Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 4 Historie von C • • • • • • • • • 1960: ALGOL60 CPL (Combined Programming Language) 1967: BCPL (Based Combined Programming Language) Martin Richards (MIT), Systemprogrammierung 1970: B Ken Thompson (AT&T Bell Labs), Implementierungssprache für Unix 1972: C Dennis M. Ritchie (AT&T Bell Labs), Implementierungssprache für Unix 1978: C-Sprachdefinition im Buch The C Programming Language von Brian W. Kernighan und Dennis M. Ritchie (K&R-C) 1983: Bildung des ANSI-Komitees X3J11 zur Standardisierung von C 1989: ANSI C (C89) 1999: kleinere Erweiterungen zum internationalen Standard ISO/IEC 9899:1999 (C99) (Viele Compiler kennen noch nicht die in C99 hinzugekommenen Erweiterungen) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 5 Eigenschaften von C • • • • • • • • • • • Universelle Programmiersprache: einsetzbar für Anwendungen in (fast) allen Bereichen Hardware-nahe Programmierung möglich Modulares Programmieren möglich Möglichkeiten zur Formulierung effizienter Programme C-Compiler erzeugen effizienten Code Viele Datentypen, viele Kontrollstrukturen, viele Operatoren Hohe Portabilität möglich (notwendige Voraussetzung: Beschränkung auf Standard-C) Standardbibliothek moderat umfangreich (verglichen mit Java), jedoch keine Änderungen am existierenden Interface über viele Jahre (voll aufwärtskompatibel) Sprache hat keine Konstrukte zum objektorientierten Programmieren Grafikbibliothek nicht Teil von C Fehlerquellen (== und =, Typumwandlung usw.) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 6 Advanced Hello World Datei hello.c: /* Füge Definitionen der Standard-I/O-Bibliothek ein */ #include <stdio.h> /* Füge Definitionen der Standard-Bibliothek ein */ #include <stdlib.h> /* hier folgt die Standard-Kopfzeile des Hauptprogramms */ int main (int argc, char **argv) { /* Deklariere eine Integer-Variable mit Namen i */ int i; /* Weise i einen Wert zu */ i = 4711; /* Gebe den Wert von i auf dem Terminal aus */ printf ("i = %d\n", i); /* Beende das Programm */ return EXIT_SUCCESS; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 7 Dateien • • • • • .c: C Source-Code; Teil eines C-Programms (C-Source) .h: Extern sichtbare Schnittstelle einer oder mehrerer .c-Dateien (C-Source) .o: Übersetzte Programmdatei (nicht-ausführbarer Objektcode) a.out: Ausführbare Programmdatei (ausführbarer Objektcode; Name der Datei beliebig) .a: Programmbibliothek (nicht-ausführbarer Objektcode) nicht-ausführbarer Objektcode C Source x.c Übersetzen ausführbarer Objektcode x.o Linken x.h Übersetzen y.o a.out y.c libgr.a Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 8 Übersetzen auf Unix-Systemen • Programm in einer Datei abspeichern. Konvention: Programmdateien: name.c Dateien mit ext. Schnittstellendef.: name.h • Übersetzen: cc –c name.c Erzeugt, falls fehlerfrei übersetzt werden kann, Datei name.o • Linken: cc –o name name.o Erzeugt, falls keine Fehler auftreten, die ausführbare Datei name • Ausführen: ./name • Übersetzen und Linken in einem Schritt: cc name.c Erzeugt a.out als ausführbare Datei • Linken mehrerer Objektdateien cc –o name name_1.o name_2.o ... name_n.o Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 9 Compiler-Optionen • -c nur Übersetzen, nicht Linken • -O Optimierung einschalten (evtl. –O0, –O1, -O2 –O3 usw. möglich) • -g Zusätzliche Informationen für Debugger in Objektdatei erzeugen. Optimierung und Debugger-Informationen schließen sich oft aus. • -ISuchpfad Suchpfad für Include-Dateien angeben (mehrere Suchpfade durch : getrennt). Mehrere –I Optionen hintereinander sind möglich. • Informationen zum C-Compiler wie gewohnt über "man cc" Die Auswertung erfolgt üblicherweise von links nach rechts! Beispiel: cc –c –O2 –I./include:/usr/local/produkt/include prog.c Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 10 Linker-Optionen • -o name Ausführbare Datei hat Namen name (Default: a.out) • -LSuchpfad Suchpfad für Libraries angeben (Mehrere Suchpfade durch : getrennt). Mehrere –L Optionen hintereinander sind möglich. • -lname Library libname.a (bzw. dyn. Library libname.so) dazulinken. Mehrere Libraries können durch Komma getrennt werden. Die Libraries werden nach dem Suchpfad gesucht. In den angegebenen Libraries werden alle Symbole gesucht, die zu diesem Zeitpunkt noch offen sind (d.h. zu der es eine Referenz aber keine Definition gibt). Die Auswertung erfolgt üblicherweise von links nach rechts! Beispiel: cc –o prog –L./lib prog.o –ltollelib,lib2 Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 11 Zusammenfassung Syntax Syntax Beispiel cc cc -o executable_name -o my_executable Compiler-Flags -O -I/usr/local/include Dateinamen (.c oder .o) datei1.c datei2.o Linker-Flags -I/usr/local/lib -lmeinelib obligatorische Angaben optionale Angaben Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 12 Beispiele cc -c x.c y.c Übersetzen x.c Übersetzen y.c x.o y.o cc -o ausfuehr x.c y.c x.c Übersetzen x.o Linken ausfuehr y.c Übersetzen y.o cc x.o y.c x.o Linken a.out y.c Übersetzen Fachbereich Angewandte Informatik y.o Programmieren in C Prof. Dr. Rudolf Berrendorf 13 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 14 Zeichensatz • Source-Zeichensatz (aus dem Programme bestehen) – Alphanumerischen Zeichen A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 – Leerzeichen und Zeilenendezeichen – Sonderzeichen ( ) [ ] { } < > + - * / % ^ ~ & | _ = ! ? # \ , . ; : ` " – Steuerzeichen: horizontaler Tabulator (TAB), vertikaler Tabulator, Seitenvorschub – Oft zusätzliche Zeichen möglich (nicht in Standard spezifiziert): • Beispiel: @, $, Backspace • (Eigentlich) nur in Zeichenkonstanten, Strings, Kommentaren, Dateinamen • Ausführbarer Zeichensatz – Evtl. andere Codierung als Source-Zeichensatz (Beispiel:Zeilenende) – Interessant (für den Compiler) bei Cross-Compilierung • Trigraphs (aus prähistorischer Zeit; vermeiden!) – Alternativschreibweise für einige Symbole, die früher nicht auf allen Tastaturen vorhanden waren – Beispiel: ??= für #, ??( für [ usw. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 15 Aufbau von C-Dateien • • • • • Formatfrei Leerzeichen, Zeilenendezeichen, vertikaler und horizontaler Tabulator, Seitenvorschub und Kommentar (s.u.) sind Trennzeichen (außer in Zeichenkonstanten und Strings) Mehrere Trennzeichen werden logisch als ein Trennzeichen angesehen Backslash \ direkt gefolgt von Zeilenende wird ignoriert, so dass eine logische Zeile entsteht. Dies ist z.B. bei Präprozessor-Direktiven notwendig, die sich nur über eine logische Zeile erstrecken können Groß- und Kleinschreibung ist signifikant Beispiele: Bezeichner1 Bezeichner2 Bezeichner3 Diese beiden (es folgt eine Backslash-Newline-Kombination) \ Zeilen werden als eine logische Zeile aufgefasst. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 16 Kommentare • • Dienen der besseren Lesbarkeit von Programmen Können überall im Programm erscheinen, außer in Zeichenkonstanten und Strings • Sind Trennzeichen • Können nicht geschachtelt werden! • Können über mehrere Zeilen gehen • Beginnen mit /* und enden mit */ über mehrere Zeilen möglich • // bis Zeilenende Beispiel: // Dies ist ein Kommentar /* Dies ist /* ebenfalls ein Kommentar */ /* Dies ist ein Kommentar, der über mehrere Zeilen geht. */ /* Dies ist /* noch ein Kommentar */ der hier leider nicht mehr weitergeht (Fehler) */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 17 Grundelemente der Sprache • • • • • • Bezeichner, z.B. meine_Variable Schlüsselwörter, z.B. const, int Operatoren, z.B. +, Separatoren oder Punktsymbole, z.B. [ ] Konstanten, z.B. 5, 3.14 Strings, z.B. "string" Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 18 Bezeichner • • • • • • Bezeichner werden für Variablen, Funktionen, Makros usw. verwendet Bezeichner sind eine Folge von Buchstaben, Ziffern und dem Zeichnen _, wobei das erste Zeichen keine Ziffer sein darf Längste mögliche Erweiterung bildet Bezeichner Klein-/Großschreibung wird unterschieden Keine reservierten Schlüsselwörter erlaubt Bezeichner können beliebig lang sein, wobei jedoch nur eine bestimmte Länge signifikant sein muss (C89 hat stärkere Restriktionen): – Mindestens die ersten 63 Zeichen bei internen Namen – Mindestens 31 Zeichen bei externen Namen Beispiel: I i A5 _Bezeichner_1_ Dies_Ist_ein_sehr_langer_Bezeichner_der_aber_gueltig_ist Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 19 Bezeichner Bezeichner Buchstabe Buchstabe _ Ziffer _ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 20 Vereinbarung • • • Variablen- und Funktionsnamen werden (überwiegend) klein geschrieben Makronamen werden komplett aus Großbuchstaben gebildet Namen mit _ als erstem Buchstaben sind für Namen von (internen) Bibliotheksfunktionen oder vordefinierten Makros „reserviert“ Beispiel: /* Makros */ #define PI #define DIMENSION_1 3.1415 100 /* Variablen */ int i,j; /* Funktionen */ int MeineTolleFunktion(); /* "reservierte" Namen */ __LINE__, __DATE__, _exit(); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 21 Reservierte Schlüsselwörter • C kennt die folgenden reservierten Schlüsselwörter mit vordefinierter Bedeutung, die z.B. nicht als Variablennamen oder Funktionsnamen verwendet werden können (wohl aber als Makronamen). • auto _Bool break case char _Complex const continue default restrict • Blau gekennzeichnete Namen sind neu in C99 • Vordefinierter Bezeichner (ist aber kein Schlüsselwort) do double else enum extern float for goto if _Imaginary inline int long register return short signed sizeof static struct switch typedef union unsigned void volatile while – Für jede Funktion findet implizit eine Vereinbarung statt: static const char __func__ [] = "Name der Funktion"; – Nutzung: if(failed) printf("Function %s failed\n", __func__); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 22 Operatoren und Separatoren • • • • Operatoren (z.B. +) werden in Ausdrücken benutzt Separatoren dienen der Trennung von Symbolen Bestehen Operatoren oder Separatoren aus mehreren Zeichen (z.B. <<), müssen diese (ohne Trennzeichen) zusammengeschrieben werden Operatoren und Separatoren sind: – Einfache Operatoren: – Zusammengesetzte Zuweisungsoperatoren: – Andere zusammengesetzte Operatoren: – Separatoren: Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf ! % ^ & * - + = ~ | . < > / ? += -= *= /= %= <<= >>= &= ^= |= -> ++ -- << >> <= >= == != && || ( ) [ ] { } , ; : ... 23 Konstanten • Zu den verschiedenen Basistypen in C gibt es Konstanten: – – – – – • Integer-Konstanten Fließkommakonstanten Zeichenkonstanten String-Konstanten (String ist kein Basistyp) Aufzählungskonstanten (werden wie Konstanten behandelt) Jede Konstante hat einen Typ und einen Wert. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 24 Ganzzahlige Konstanten (Integer) • Dezimale Angabe: Dezimalzahl ohne Vorzeichen und führende 0, gebildet aus den Dezimalziffern 0-9. Beispiel: 5 • 19 32 /* Werte: 5, 19, 32 */ Oktale Angabe: Eine Oktalzahl (zur Basis 8) mit führender 0, gebildet aus den Oktalziffern 0-7. Beispiel: 05 • 023 040 /* Werte: 5, 19, 32 */ Hexadezimale Angabe: Eine Hexadezimalzahl (zur Basis 16) mit führendem 0x oder 0X, gebildet aus den Hexadezimalziffern 0-9 und a-f oder A-F. Beispiel: 0x5 0x13 0x20 Fachbereich Angewandte Informatik /* Werte: 5, 19, 32 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 25 Typzusätze bei Integer-Konstanten Bei Integer-Typen wird noch zwischen verschiedenen „Untertypen“ unterschieden (später dazu mehr im Detail). Um solche Konstanten angeben zu können, gibt es Zusätze bei Angabe einer Integer-Konstanten: u oder U für Integer-Konstanten ohne Vorzeichen (unsigned) l oder L für lange Integer-Konstanten (long) ll oder LL für sehr lange Integer-Konstanten (long long) Beispiel: 10L 10LL 10u 10ul 10Ul 10ull Fachbereich Angewandte Informatik /* /* /* /* /* /* Wert: Wert: Wert: Wert: Wert: Wert: long int 10 */ long long int 10 */ unsigned int 10 */ unsigned long int 10 */ unsigned long int 10 */ unsigned long long int 10 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 26 Integer-Konstanten Integer-Konstante Dezimalkonstante Oktalkonstante Int-Suffix Hexadezimalkonstante u-Suffix Int-Suffix u ll-Suffix U u-Suffix l-Suffix l-Suffix l l-Suffix L u-Suffix ll-Suffix ll-Suffix ll u-Suffix Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf LL 27 Integer-Konstanten Dezimalkonstante Ziffer ≠ 0 Ziffer Oktalkonstante 0 oktale Ziffer Hexadezimalkonstante 0 x hexadezimale Ziffer X oktale Ziffer 0 ... 7 hexadezimale Ziffer 0 ... 7 Fachbereich Angewandte Informatik a ... f A ... Programmieren in C Prof. Dr. Rudolf Berrendorf F 28 Typzusätze bei Integer-Konstanten Der Typ einer Integer-Konstanten ist der erste „passende“ Typ nach folgendem Schema (C99; in anderen C-Versionen anders!): Konstante Typ Dezimalzahl ohne Anhang int, long, long long Oktal-/Hexadezimalzahl ohne Anhang int, unsigned, long, unsigned long, long long, unsigned long long Anhang u oder U unsigned int, unsigned long, unsigned long long Dezimalzahl mit Anhang l oder L long, long long Oktal-/Hexadezimalzahl mit l oder L long, unsigned long, long long, unsigned long long Anhang sowohl u/U als auch l/L unsigned long, unsigned long long Dezimalzahl mit ll/LL long long Oktal-/Hexadezimalzahl mit ll/LL long long, unsigned long long Anhang sowohl u/U als auch ll/LL unsigned long long Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 29 Beispiel Beispiel: Rechner, auf dem int=16 Bit, long=32 Bit (bei signed 1 Bit Vorzeichen). 32000 int (-215 ≤ x ≤ 215-1) 32000u unsigned int (0 ≤ x < 216, ohne Vorzeichen-Bit) 33000 long (Umwandlung nach long, da als int nicht darstellbar) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 30 Fließkommakonstanten Es gibt drei reelle Fließkommatypen (float, double, long double). Fließkommakonstanten ohne Zusatz sind vom Typ double! Durch Anhängen des Zusatzes f oder F entsteht eine float-Konstante, durch Anhängen des Zusatzes l oder L eine Konstante vom Typ long double. Durch Einfügen von complex.h sind auch komplexe Konstanten möglich in der Form 1.0+1.0*I für die komplexe Zahl (1,1). Beispiel: 3.14 3.1415926356f 3.14L Fachbereich Angewandte Informatik /* double */ /* float */ /* long double */ Programmieren in C Prof. Dr. Rudolf Berrendorf 31 Fließkommakonstanten • Dezimalzahl mit Punkt Beispiel: 3.14 .5 • Ganzzahl mit Exponent e oder E: Beispiel: 5E10 5e10 • 3. 3e-1 Kombination aus beiden Alternativen Beispiel: 0.31415e1 Fachbereich Angewandte Informatik 1.0e-10 Programmieren in C Prof. Dr. Rudolf Berrendorf 32 Fließkommakonstanten Fließkommakonstante . Ziffer Exponent f-Suffix Ziffer . Ziffer Ziffer Exponent Exponent f-Suffix e E Ziffer + F - l Hexadezimale Angaben ebenfalls möglich (hier nicht) Fachbereich Angewandte Informatik f Programmieren in C Prof. Dr. Rudolf Berrendorf L 33 Character-Konstanten • • Eine Character-Konstante ist ein Zeichen des ausführbaren Zeichensatzes, das in Apostophe (keine Anführungszeichen!) eingeschlossen ist. Ausnahmen sind (können durch Escape-Sequenzen dargestellt werden; s.u.): – Apostroph selber ′ – Backslash \ – Zeilenendezeichen • • Der Wert einer Character-Konstanten ist die Codierung im verwendeten Zeichensatz. Beispiel: Das Leerzeichen hat im ASCIIZeichensatz die Codierung 32, im EBCDIC-Zeichensatz die Codierung 64. Der Typ einer Character-Konstanten (ein Ausdruck) ist int! Beispiel: ′a′ Fachbereich Angewandte Informatik ′$′ ′.′ Programmieren in C Prof. Dr. Rudolf Berrendorf 34 Escape-Sequenzen für Zeichen • • • • • • • • • • • • ′\a′ ′\b′ ′\f′ ′\n′ ′\r′ ′\t′ ′\v′ ′\\′ ′\′′ ′\“′ ′\?′ ′\ooo′ • ′\xh′ Fachbereich Angewandte Informatik Gong-Zeichen Backspace Seitenvorschub (Formfeed) Newline Zeilrücklauf (Carriage Return) horizontaler Tabulator (Tab) vertikaler Tabulator Backslash \ selber Apostroph-Zeichen Anführungszeichen Fragezeichen oktale Angabe des Zeichencodes (ooo sind 1-3 oktale Ziffern). Beispiel: ′\0′ für Codierung 0 Hexadezimale Angabe des Zeichencodes h ist eine beliebige Anzahl von Hexadezimalziffern, die durch eine Nicht-Hexadezimalziffer beendet wird. Beispiel: ′x20′ steht im ASCII-Zeichensatz für das Leerzeichen. Programmieren in C Prof. Dr. Rudolf Berrendorf 35 Unterstützung großer Zeichensätze • • Zur Zeit geringe Verbreitung Hier nur am Rande erwähnt • Codierung von großen Zeichensätzen (z.B. Japanisch) über: – Beispiel für Multibyte-Character: – Beispiel für Wide Character: • ′abc′ L′a′ Universal Character Names (C99) in Character- oder String-Konstanten sowie Bezeichnern: – \u hex-digit4 – \U hex-digit8 – Beispiel: \u0024 ($) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 36 String-Konstanten • • • • • • Ein String (Zeichenfolge) ist kein Basistyp in C. In C wird ein String-Konstante angegeben als eine Folge von Zeichen in Anführungszeichen. Escape-Sequenzen für Zeichen im String sind erlaubt. Der Typ einer String-Konstanten ist ein Feld von Zeichen. Wird eine String-Konstante über mehrere Zeilen definiert, so muss direkt vor einem Zeilenende ein Backslash \ stehen (siehe Präprozessor). Stehen zwei Strings unmittelbar hintereinander (nur durch Leerzeichen, Zeilenende etc. getrennt), so fügt der Compiler diese beiden StringKonstanten zu einer String-Konstanten zusammen. Beispiele: ″String″ ″Auch \a Escape-Sequenzen sind erlaubt!″ ″Dieser String geht über \ zwei Zeilen″ ″Hieraus erzeugt″ ″der Compiler″ ″einen String″ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 37 String-Konstanten • • • • Für eine String-Konstante mit n Zeichen legt der Compiler automatisch einen Speicherbereich (char-Feld) der Länge n+1 an. Die ersten n Zeichen enthalten die Zeichen des angegebenen Strings. Das n+1-te Zeichen ist ein Null-Character (′\0′), das vereinbarungsgemäß jeden String in C abschließen muss! So angelegte String-Konstanten dürfen nicht modifiziert werden! Beispiele: ″abc″ wird abgespeichert als (jedes Kästchen ist ein Byte): a b c \0 "" wird abgespeichert als: \0 Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 38 Aufzählungskonstanten • In C gibt es Aufzählungstypen, Mengen von benannten, ganzzahligen Konstanten. Beispiel: enum Tage {Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag}; • • Der Typ einer Aufzählungskonstanten ist int! Der Wert einer Aufzählungskonstanten ist die Position in der Aufzählungsmenge (bei 0 beginnend). Im Beispiel 0 für Montag, 1 für Dienstag usw. • Details später bei der Beschreibung von Aufzählungstypen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 39 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 40 Präprozessor • • • Übersetzt man ein Programm mit einem C-Compiler, so wird automatisch ein Präprozessor vorgeschaltet. Präprozessor ist ein reiner Makroprozessor, der textuellen Ersatz vornimmt und eine neue Datei produziert, die an den eigentlichen CCompiler weitergegeben wird! Aufgaben des Präprozessors: – Einfügen weiterer Dateien innerhalb einer Datei (#include) – Bedingte Übersetzung (#if ...) – Makrodefinitionen und –ersetzung – Zusätzliche Compiler-Steuerung über #pragma • • • Gesteuert wird der Präprozessor über Direktiven, die alle mit einem # als erstem Nichtleerzeichen einer Zeile anfangen. Direktiven müssen in eine logische Zeile passen. Längere Direktiven mit Backslash-Newline-Kombination über mehrere physikalische Zeilen möglich. Zwischen # und Direktivenbezeichner können Leerzeichen stehen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 41 Ausführungsstufen des Präprozessors • • • Der Präprozessor durchläuft mehrere logische Stufen hintereinander. In der Praxis führen Präprozessoren dies alles in einem Durchlauf aus. Die logischen Stufen sind: – Ersetzen von Trigraph-Sequenzen – Löschen von Backslash-Newline-Kombinationen – Aufspalten des Programms in Grundelemente (Bezeichner, Zahlen, etc.), die durch Trennzeichen separiert sind. Kommentare werden logisch durch ein Leerzeichen ersetzt. – Ausführen von Präprozessor-Direktiven inkl. Makroersetzung – Ersetzen von Escape-Sequenzen in Character- und String-Konstanten. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 42 Einfügen von Dateien • • Mittels der #include-Direktive kann man andere Dateien in den Code einfügen. Schachtelung möglich, d.h. in eingefügten Dateien können wiederum #include-Direktiven stehen. Wird fast ausschließlich zum Einfügen von .h-Dateien genutzt. • Mehrere Formen möglich: • – #include ″Dateiangabe″ Die Datei wird relativ zum aktuellen Verzeichnis gesucht. – #include <Dateiname> Die Datei wird an systemspezifischen Verzeichnissen gesucht. Auf UnixSystemen in /usr/include und evtl. /usr/local/include. – #include Makroname1...Makronamen Nach ihrer Ersetzung sollten die Makronamen eine der beiden obigen Formen ergeben. • • Die Dateiangabe ist relativ zum jeweiligen Bezugspunkt, d.h. Angaben wie ../Datei.h sind möglich. Der Bezugspunkt wird mit Pfadangaben in den Dateiangaben verändert. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 43 Einfügen von Dateien Beispiel 1: #include ″test.h″ #include ″../test2.h″ /* im lokalen Verzeichnis */ /* im Vaterverzeichnis des lokalen Verzeichnisses */ Beispiel 2: #include <stdlib.h> /* wird zuerst als /usr/include/stdlib.h gesucht */ Beispiel 3: Datei test.c: #include ″../test.h″ /* Im Vaterverzeichnis suchen */ Datei ../test.h: #include ″test1.h″ Fachbereich Angewandte Informatik /* Im Vaterverzeichnis weitersuchen nach test1.h */ Programmieren in C Prof. Dr. Rudolf Berrendorf 44 Beispiel für den Einsatz Datei prog.c: #include <stdlib.h> #include ″mydefs.h″ int main(int argc, char **argv) { double umfang, radius, flaeche; radius = 20.0; umfang = 2 * PI * radius; flaeche = flaechenberechnung(radius); } Datei mydefs.h: /* Makrodefinition */ #define PI 3.1415 /* Funktionsdefinition (Typangabe) */ double flaechenberechnung (double); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 45 Makro • Makros dienen: – – – – • • • • • • der Abkürzung häufig benutzter Zeichenfolgen der Definition logischer Konstanten (Konsistenz im Programm) dem „Umdefinieren“ existierender Namen der bedingten Übersetzung von Programmteilen Makros gibt es mit und ohne Parameter Nach einer Makrodefinition wird jedes Erscheinen dieses Makronamens im weiteren Text durch den Ersatztext textuell (!) ersetzt. Im Ersatztext können selbst wieder Makros verwendet werden. Prinzipiell lassen sich z.B. auch reservierte Wörter umdefinieren -> schlechter Programmierstil. Makronamen werden vereinbarungsgemäß aus Großbuchstaben gebildet. Mit der Compiler-Option –E kann man sich (auf Standardausgabe) den Programmtext nach Durchlauf des Präprozessors anschauen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 46 Makrodefinition ohne Parameter Definition eines parameterlosen Makros: #define Makroname Ersatztext Makroname muss ein gültiger Bezeichner sein. Ersatztext kann ein beliebiger Text sein, insbesondere auch leer. Der Ersatztext fängt direkt hinter dem Makronamen an und geht bis zum Ende der (logischen) Zeile. Beispiel: #define PI 3.1415 #define VEKTOR_DIM #define MATRIX_GROESSE 100 (VEKTOR_DIM * VEKTOR_DIM) double matrix[MATRIX_GROESSE]; /* wird textuell ersetzt durch: double matrix[(100 * 100)]; */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 47 Makrodefinition mit Parameter Definition eines Makros mit Parametern: #define Makroname(p1,...,pn) Ersatztext Die öffnende Klammer hinter dem Makronamen muss unmittelbar folgen, sonst wäre dies ein Makrodefinition ohne Parameter. p1,...,pn sind formale Parameter. Jedes weitere Vorkommen des Makronames im Text bewirkt Folgendes: 1. Identifizierung der aktuellen Parameter des Makroaufrufes, die durch Kommas getrennt sind 2. Ersetzen weiterer Makroaufrufe in diesen aktuellen Parametern 3. Ersetzen der formalen Parameter durch die aktuellen Parameter 4. Ersetzen des Makroaufrufes durch den Ersatztext mit den aktuellen statt den formalen Parametern. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 48 Beispiele zu Makrodefinition mit Parameter #define SQUARE(x) x*x #define LOOP(start, test, inc, body) \ for (start ; inc; test) \ body; int main(int argc, char **argv) { int i,j; LOOP( i=0 , i < 100 , ++i , j = SQUARE(j) ) } Nach Makroexpansion: int main(int argc, char **argv) { int i,j; for ( i = 0; i < 100; ++i) j = j*j; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 49 Potentielle Fehler Beispiel 1: Prioritäten von Operatoren Beispiel 2: Nebeneffekte #define SQUARE(x) b = SQUARE(a+1); #define SQUARE(x) ((x) * (x)) b = SQUARE(a++); x * x Erzeugter Code: Erzeugter Code: b = a+1 * a+1 b = ((a++) * (a++)); Tips: 1. 2. Großzügig im Ersatztext Klammern verwenden. Jedes Vorkommen eines Argumentes im Ersatztext klammern. Als aktuelle Argumente von Makroaufrufen nur Ausdrücke ohne Nebeneffekte verwenden. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 50 Makrodefinition mit variabler Parameteranzahl Definition eines Makros mit einer festen Anzahl (kann auch 0 sein) von Parametern und möglichen weiteren Parametern: #define Makroname(p1,p2,... ) Ersatztext Mit ... werden syntaktisch alle nachfolgenden Parameter bezeichnet, die bei einem Aufruf übergeben werden. Im Ersatztext kann man auf diese Parameter mit __VA_ARGS__ zugreifen Beispiel: #if defined(DEBUG) #define my_printf(...) fprintf(stderr, __VA_ARGS__) #else #define my_printf(...) printf(__VA_ARGS__) #endif ... my_printf("Hier kracht es, weil i=%d\n", i); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 51 undef eines Makros Oft ist es wünschenswert, dass ab einem bestimmten Punkt im Programm ein Makro nicht länger definiert sein soll. Dazu gibt es die Direktive #undef Makroname Ab diesem Punkt ist kein Makro unter Makroname mehr bekannt. Beispiel: #define TEST_CODE ... #undef TEST_CODE Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 52 Bedingte Übersetzung Mit Hilfe des Präprozessors ist es möglich, nur bestimmte Teile des Programms übersetzen zu lassen. Dies ist z.B. dann nützlich, wenn man aus einem Source-Programm verschiedene Versionen erzeugen möchte, z.B. für Unix und Windows. Beispiel: #if defined(UNIX) # include <unix.h> #elif defined(WINDOWS) # include <windows.h> #endif Durch Definieren des Makros UNIX bzw. WINDOWS werden unterschiedliche Teile des Programms vom Präprozessor an den eigentlichen Compiler weitergereicht. Mit der Compiler-Option –DMakroname kann man gezielt Makros auch von der Kommandozeile definieren. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 53 Direktiven zur bedingten Übersetzung • #if const_expr Das nächste Programmstück bis zur nächsten bedingten Direktive auf gleichem Niveau wird nur übersetzt, wenn const_expr (ein konstanter Integer-Ausdruck) ungleich 0 ist. • #ifdef Makroname Übersetzen, wenn Makroname an dieser Stelle bekannt ist. • #ifndef Makroname Übersetzen, wenn Makroname an dieser Stelle nicht bekannt ist. • #elif const_expr Nachfolgendes bis zur nächsten bedingten Direktive übersetzen, falls vorangehende bedingte Direktiven auf gleichem Niveau nicht genommen wurde und const_expr ungleich 0 ist. • #else Bis zum entsprechenden #endif übersetzen, falls keine vorangehende bedingte Direktive auf gleichem Niveau genommen wurde. • #endif Abschließende Direktive eines bedingten Blocks. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 54 Beispiele Beispiel 1: Beispiel 3: #define UNIX #if !defined(TEST_H_INCLUDED) # define TEST_H_INCLUDED # include <test.h> ... #endif #ifdef UNIX # include <unix.h> #else # include „meinedatei.h“ #endif Beispiel 4: Beispiel 2: #if 0 Programmstück, das nicht übersetzt werden soll #endif Fachbereich Angewandte Informatik #define DEBUG 2 #if (DEBUG > 0) printf(″Kontrollausgabe″); # if (DEBUG > 1) printf(″Mehr Details″); # endif #endif Programmieren in C Prof. Dr. Rudolf Berrendorf 55 Sonstige Direktiven • #pragma Compilerpragma Compiler-abhängige Informationen können an den Compiler übergeben werden • # Leere Direktive ohne Effekt. • #line Zeilennummer [″Dateiname″] Nützlich im Zusammenhang mit Programmierwerkzeugen, die Programmtransformationen ausführen (Rückbezug zur Originaldatei). • #error token Compiler gibt eine Meldung aus, die aus token besteht. Beispiel 1: Beispiel 2: #pragma optimize #pragma 27 ″orignaldatei.c″ #if defined(SYSTEM) ... #else #error ″das sollte nicht passieren″ #endif Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 56 Makro-Operatoren • defined(Makroname) Liefert 1, falls Makroname an diesem Punkt bekannt ist, ansonsten 0. • # Der Präfixoperator kann nur im Zusammenhang mit #define verwendet werden. Er bewirkt das Einschließen des nachfolgenden Argumentes in Anführungszeichen. Beispiel: #define tempfile(dir) #dir name = tempfile(/usr/include); erzeugt name = ″/usr/include″; • ## Der Infixoperator bewirkt eine Konkatenation beider Operanden. Beispiel: #define cat(x,y) x ## y cat(var, 123) erzeugt var123 Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 57 Vordefinierte Makros • • • • • • __LINE__ Dezimalkonstante mit der aktuellen Zeilennummer während der Übersetzung. __FILE__ String-Konstante mit dem aktuellen Dateinamen während der Übersetzung. __DATE__ String-Konstante mit dem aktuellen Datum der Übersetzung. __TIME__ String-Konstante mit dem aktuellen Zeitpunkt der Übersetzung. __STDC__ Liefert 1, wenn der Compiler konform zum ANSI-Standard ist. Beispiel: printf(″Programmversion 1.0, uebersetzt am %s um %s″, __DATE__, __TIME__); if( i == j ) printf(″In Zeile %d in Datei %s stimmt was nicht″, __LINE__, __FILE__); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 58 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 59 Anweisungen • • Anweisungen steuern Kontrollfluss Jede Anweisung wird durch ein Semikolon abgeschlossen (Ausnahme: zusammengesetzte Anweisung) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 60 Leere Anweisung Syntax: ; Beschreibung: Wird üblicherweise dort eingesetzt, wo aus syntaktischen Gründen eine Anweisung erforderlich ist. Beispiel: for ( i = 0; (i < 100) && (a[i] != 5); ++i) ; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 61 Ausdrucksanweisung Syntax: Ausdruck ; Beschreibung: Durch Anhängen eines Semikolons an einen Ausdruck (z.B. 3+4) erhält man eine Ausdrucksanweisung. Der Wert des Ausdrucks wird berechnet und das Ergebnis ignoriert. Eine Ausdrucksanweisung macht nur Sinn, wenn in dem Ausdruck Nebenwirkungen vorhanden sind. Beispiel: /* Ein Funktionsaufruf ist ist ein Ausdruck */ myfun(10); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 62 Sprungzielanweisung Syntax: Bezeichner : Anweisung Beschreibung: Jeder Anweisung kann ein Label vorangestellt werden, wobei ein Label ein Bezeichner gefolgt von einem Doppelpunkt ist. Ein Label kann nur über eine goto-Anweisung (später) angesprochen werden. Ausnahme: Label im Zusammenhang mit switch-Anweisungen (später). Ein LabelBezeichner kann auch schon vor seiner Definition benutzt werden (d.h. goto Bezeichner). Der Gültigkeitsbereich ist die umschließende Funktion. Beispiel: /* Das Label heißt hier Fehler */ Fehler: i = 2; ... goto Fehler; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 63 Zusammengesetzte Anweisung Syntax: { Deklaration } Anweisung Beschreibung: Oft bezeichnet man dies auch als Block. In C89 muss die Reihenfolge zwingend eingehalten werden: Zuerst alle Deklarationen, dann erst Anweisungen. Beispiel: int main(int argc, char **argv) { int i; i = 2; { /* <- hier beginnt der Block */ int i; i = 5; } /* <- hier endet der Block */ } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 64 while-Schleife Syntax: while ( Ausdruck ) Anweisung Beschreibung: Die Anweisung im Schleifenrumpf wird solange ausgeführt, solange der Ausdruck Ausdruck wahr ist (d.h. ungleich 0). Alternative Abbruchmöglichkeiten werden später behandelt. Beispiel: int fun1(int arg) { int i; i = 1; while(arg != 0) { i = i * 2; arg = arg – 1; } return i; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 65 do-while-Schleife Syntax: do Anweisung while ( Ausdruck ) ; Beschreibung: Die Anweisung im Schleifenrumpf wird solange ausgeführt, bis der Ausdruck Ausdruck falsch ist (d.h. gleich 0). Im Gegensatz zur whileSchleife wird die Anweisung im Schleifenrumpf mindestens einmal ausgeführt. Alternative Abbruchmöglichkeiten werden später behandelt. Beispiel: int fun2(int arg) { int i = 1; do { i = i * arg; arg = arg – 1; } while(arg != 0); return i; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 66 for-Schleife Syntax: for ( init ; expr2 ; expr3 ) Anweisung Beschreibung: Zuerst wird init ausgewertet und das Resultat ignoriert. Dann wird expr2 ausgewertet. Falls der Wert falsch (d.h. 0) ist, bricht die Schleife ab. Ansonsten wird die Anweisung ausgeführt und vor dem nächsten Test von expr2 wird expr3 ausgewertet und das Resultat ignoriert. Alle Angaben init, expr2, expr3 sind optional und können weggelassen werden. init kann ein Ausdruck oder eine Deklaration sein. Beispiel: int fun3(int arg) { for ( int i = 1; arg != 0; arg = arg – 1 ) i = i * arg; return i; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 67 goto-Anweisung Syntax: goto Bezeichner ; Beschreibung: Es wird zu dem angegeben Bezeichner eines Labels verzweigt. Springt man in einen Block hinein, so wird zwar Speicherplatz für die Blocklokalen Variablen angelegt, jedoch werden keine Initialisierungsausdrücke für diese Variablen ausgewertet! Beispiel: int fun4(int a[100], int suchwert) { int i; for ( i = 1; i < 100; i = i + 1 ) if ( a[i] == suchwert) goto gefunden; gefunden: return i; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 68 break-Anweisung Syntax: break ; Beschreibung: Mit der break-Anweisung verlässt man die innerste Iterations- (for, while, do-while) oder switch-Anweisung (später). Beispiel: int fun5(int a[100], int suchwert) { int i; for ( i = 1; i < 100; i = i + 1 ) if ( a[i] == suchwert) break; return i; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 69 continue-Anweisung Syntax: continue ; Beschreibung: Mit der continue-Anweisung bricht man innerhalb einer IterationsAnweisung (for, while, do-while) die aktuelle Iteration ab und verzweigt zum Ende des Schleifenrumpfes, bricht aber nicht die Schleife ab. D.h. es würde in einer for-Schleife als nächstes wieder überprüft, ob die Abbruchbedingung erfüllt ist. Beispiel: double logsum( double a[100], int max ) int i; double sum = 0.0; for ( i = 0; i < max; i = i + 1 ) { if ( a[i] == 0.0 ) continue; sum += log(a[i]); } return sum; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf { 70 return-Anweisung Syntax: return Ausdruck ; Beschreibung: Die return-Anweisung bewirkt ein sofortiges Verlassen der Funktion und Rückkehr zur aufrufenden Funktion. Der Ausdruck Ausdruck ist optional. Falls man den Ausdruck weglässt, muss die Funktion vom Resultattyp void sein. Beispiel: int const5( void ) { return 5; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 71 if-Anweisung Syntax: if ( Ausdruck ) Anweisung1 else Anweisung2 ; Beschreibung: Zuerst wird Ausdruck ausgewertet. Ist der Ausdruck wahr (d.h. ungleich 0), wird Anweisung1, ansonsten Anweisung2 ausgeführt. Die Angabe des else-Zweiges ist optional. Beispiel: int fun6( int arg ) { if ( arg < 0) return –1; else return 1; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 72 if-Anweisung Durch eine Schachtelung von if-Anweisungen kann es zu Mehrdeutigkeiten kommen. Im Fall if ( arg1 < 0) if ( arg2 == 0 ) return 0 else return 1; Worauf bezieht sich das letzte else? In C (und anderen Sprachen) ist es so definiert, dass sich ein else immer auf das letzte offene if bezieht. Man sollte in solchen Fällen der besseren Lesbarkeit einen Block mit {} einführen: if ( arg1 < 0) { if ( arg2 == 0 ) return 0 else return 1; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 73 switch-Anweisung Syntax: switch Ausdruck ( ) Anweisung und in diesem Zusammenhang: case default Fachbereich Angewandte Informatik const_expr : : Programmieren in C Prof. Dr. Rudolf Berrendorf 74 switch-Anweisung Die allgemeine Anwendungsform einer switch-Anweisung ist: switch (Ausdruck ) { case Ausdruck_1: .... case Ausdruck_n: default: Anweisung_1; break; Anweisung_n; break; Anweisung_n+1; } Der Ausdruck Ausdruck wird ausgewertet. Dann werden nacheinander die konstanten Ausdrücke Ausdruck_1,... ausgewertet, bis Ausdruck gleich einem Ausdruck_i ist. In diesem Fall wird Anweisung_n ausgeführt und mit der anschließenden break-Anweisung die switchAnweisung verlassen. Trifft kein Fall zu und ist ein default-Label vorhanden, so wird diese Anweisung ausgeführt. Ist nach einer Anweisung_i keine breakAnweisung vorhanden, wird mit Anweisung_i+1 weitergemacht! Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 75 Beispiel void fehlermeldung ( int fehlercode ) { switch ( fehlercode ) { case 1: printf("Fehler kritisch"); break; case 2: printf("Fehler fatal"); break; default: printf("Fehler ignorieren"); } switch ( fehlercode ) { case 1: case 2: exit(EXIT_FAILURE); } } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 76 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 77 Funktionen • • (später im Detail mehr) Funktionsdefinitionen bestehen aus – Funktionskopf (header), der die Schnittstelle angibt – Rumpf (body), der die Berechnungsvorschrift angibt • Ein Funktionsprototyp spezifiziert lediglich die Schnittstelle und enthält statt einem Rumpf ein Semikolon. Funktionsprototypen ermöglichen u.a. dem Compiler Typüberprüfungen und automatische Typumwandlungen bei Funktionsaufrufen. // header int myfun ( int arg1 ) { // body return arg1 + 1; } Fachbereich Angewandte Informatik // Funktionsprototyp int myfun ( int arg1 ) ; Programmieren in C Prof. Dr. Rudolf Berrendorf 78 Funktionen • • In C ausschließlich call-by-value Parameterübergabe Ändern von Variablen in aufrufender Funktion kann durch Übergabe von Zeigerwerten (Adressen der Variablen) erreicht werden void swap (int *x, int *y) { // tausche den Inhalt an den beiden Adressen, // die durch x und y angegeben sind int tmp = *x; *x = *y; *y = tmp; } int swaptest() { int i=1, j=2; // rufe swap mit den Adressen der beiden Variablen i und j auf swap(&i, &j); printf("i=%d, j=%d\n", i,j); // Ausgabe: i=2, j=1 } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 79 Hauptprogramm • • Für jedes ausführbare Programm muss eine Funktion mit Namen main existieren, das der Loader als Programmstart aufruft Funktionsprototyp: int main ( int argc, char ** argv ); • • Der Funktionswert von main ist ein int. Dieser Wert wird an das Betriebssystem zurückgegeben und man kann z.B. diesen Wert in einer Shell abfragen. Es gibt zwei logische Konstanten (in <stdlib.h>), EXIT_SUCCESS und EXIT_FAILURE, die man in Zusammenhang mit einer return-Anweisung im Hauptprogramm nutzen kann, um einen fehlerfreien bzw. mit Fehlern behafteten Programmablauf zu kennzeichnen. Über den Wert argc kann man abfragen, wie viele Argumente (z.B. in der Kommandozeile) an das Programm übergeben wurden. In argv stehen als Strings die Argumente. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 80 Beispiel Programm: #include <stdio.h> int main ( int argc, char **argv ) { int i; for ( i=0; i < argc; ++i ) printf (″Argument %d war %s\n″, i, argv[i] ); } Aufruf aus der Kommandozeile: Prompt> Argument Argument Argument Argument Prompt> a.out 0 war 1 war 2 war 3 war Fachbereich Angewandte Informatik test1 4711 test4 a.out test1 4711 test4 Programmieren in C Prof. Dr. Rudolf Berrendorf 81 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 82 Typen • • • • • Typen sind Wertemengen mit den darauf definierten Operationen. Jedes Objekt in C (Konstante, Variable, Funktion) hat einen Typ. Durch Typüberprüfung kann der Compiler viele Fehler erkennen. Typumwandlung zwischen manchen Typen möglich Basistypen: – – – – – • • Leerer Typ Zusammengesetzte Typen – – – – – • Integer-Typen Character-Typen _Bool Aufzählungstypen Fließkommatypen Zeigertypen Feldtypen Strukturtypen Vereinigungstypen Funktionstypen Typzusätze Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 83 Einordnung von Typen C Typ Kategorie short, int, long, long long (signed und unsigned) integrale Typen arithmetische Typen skalare Typen char (signed und unsigned) _Bool enum {...} float, double, long double float/double/long double _Complex Fließkommatypen T * Zeigertypen T [...] Feldtypen struct {...} Strukturtypen union {...} Vereinigungstypen T (...) Funktionstypen void Fachbereich Angewandte Informatik aggregierte Typen Typ void Programmieren in C Prof. Dr. Rudolf Berrendorf 84 Integer-Typen • Integer-Typen dienen zur Repräsentation von: – – – – Ganzzahligen Werten mit und ohne Vorzeichen Bit-Vektoren Boolschen Werten (0=falsch, alles andere=wahr) Character-Werten (Unterschied Character-Wert und Character-Typ!) • Integer-Typen mit Vorzeichen (Präfix signed optional): short int, int, long int, long long int alternative Schreibweise: short, long, long long • Integer-Typen ohne Vorzeichen (unsigned): unsigned short int, unsigned int, unsigned long int, unsigned long long int alternativ: unsigned short, unsigned long, unsigned long long • Character-Typen: char, signed char, unsigned char • Aufzählungstypen Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 85 Integer-Typen • Größenverhältnisse bzgl. Speicherbelegung und damit Wertebereich: – short int ≤ int ≤ long int – unsigned short int ≤ unsigned int ≤ unsigned long int • Es können auch alle drei Typen bzgl. Größe zusammenfallen • int entspricht im Allgemeinen der Wortlänge des Rechners • • • signed-Versionen sind gleich groß wie unsigned-Versionen unsigned-Versionen unterscheiden sich von signed-Versionen durch die Interpretation des Speicherplatzes (Vorzeichenbit, größere positive Zahlen darstellbar mit unsigned) Vermeiden Sie das Mischen/die Verwendung vieler verschiedener Integer-Typen (wann immer es geht: int) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 86 Mindestanforderungen an Wertebereich Die folgenden logischen Konstanten sind in <limits.h> definiert und geben jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen an die Wertebereiche der int-Typen sind dahinter angegeben: Konstante Mindestanforderung Bedeutung SHRT_MIN -32767 Min. Wert von short SHRT_MAX 32767 Max. Wert von short USHRT_MAX 65535 Max. Wert von unsigned short INT_MIN -32767 Min. Wert von int INT_MAX 32767 Max. Wert von int UINT_MAX 65535 Max. Wert von unsigned int LONG_MIN -231 Min. Wert von long LONG_MAX 231-1 Max. Wert von long ULONG_MAX 232-1 Max. Wert von unsigned long LLONG_MAX 263-1 Max. Wert von long long (Rest analog) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 87 Extended Integer Typen • • • Oft benötigt man zur korrekten Programmierung exakte Größenangaben zu einem Typ, z.B. int64 (wie in Java direkt definiert) In C war das bisher nicht möglich! Ausweg: Extended Integer Typen definiert in – inttypes.h – stdint.h • • Dort sind zusätzliche (abgeleitete) int-Typen definiert. Syntax dieser Typen: – intN_t und uintN_t, wobei N=8, 16, 32 oder 64 (implementierungsabhängig auch mehr) für genau N Bits – int_leastN_t, uint_leastN_t analog für mindestens N Bits – int_fastN_t, uint_fastN_t analog für mindestens N Bits und möglichst schnell • Beispiele: – – – – int8_t uint16_t int_fast32_t uint_least64_t Fachbereich Angewandte Informatik // // // // int, genau 8 Bits unsigned int, genau 16 Bits int, mindestens 32 Bits, schnell unsigned int, mindestens 64 Bit Programmieren in C Prof. Dr. Rudolf Berrendorf 88 Beispiele für Variablendeklarationen int x; unsigned int ux; unsigned long int ulx; signed int sx; short shx; signed short int shx2; uint64_t counter; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 89 Character-Typ • • • Character-Typen belegen genau ein Byte Können genau ein Zeichen des Zeichensatzes aufnehmen Es gibt char, signed char und unsigned char • Verwendet man ein Character-Objekt (Konstante, Variable) in einem Ausdruck, so ist der Wert des (Teil-)Ausdrucks ein int ! Fehlerquelle: Manche Bibliotheksfunktione zum Einlesen von Zeichen liefern die logische Konstante EOF=-1 zurück. Weist man den Resultatwert (int) einer char-Variablen zu und testet anschließend die char-Variable, ob diese gleich EOF (int) ist, so kann es durch implizite Typumwandlungen zu unerwünschten Ergebnissen kommen. Beispiel: char c; signed char sc; unsigned char uc; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 90 Mindestanforderungen an Wertebereich Die folgenden logischen Konstanten sind in <limits.h> definiert und geben jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen an die Wertebereiche der char-Typen sind dahinter angegeben: Konstante Mindestanforderungen an Wert Bedeutung CHAR_BIT 8 Bits pro Byte SCHAR_MIN -127 Min. Wert signed char SCHAR_MAX 127 Max. Wert signed char UCHAR_MAX 255 Max. Wert unsigned char CHAR_MIN 0 oder SCHAR_MIN Min. Wert von char CHAR_MAX UCHAR_MAX oder SCHAR_MAX Max. Wert von char MBLEN_MAX 1 Max. Anzahl Byte in Multibyte-Character Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 91 Aufzählungstyp Ein Aufzählungstyp ist eine Anzahl von int-Werten, die jeweils mit einem Bezeichner benannt sind. Die Syntax zur Definition eines Aufzählungstyps in C ist: enum typ_name { const_def_list }; wobei typ_name ein optionaler Typname ist und const_def_list eine durch Kommas getrennte Liste von Bezeichnern mit optionalen Wertangaben. Mit jedem Bezeichner in const_def_list wird ein int-Wert assoziiert, ohne weitere Angabe beginnend bei 0 und jeweils um 1 inkrementiert. Beispiel: enum Jahreszeiten { Fruehjahr, Sommer, Herbst, Winter, Karneval=5 }; enum Jahreszeiten stuermisch; stuermisch = Herbst; Fachbereich Angewandte Informatik /* Variable stuermisch deklarieren */ /* Zuweisen eines Aufzählunswertes */ Programmieren in C Prof. Dr. Rudolf Berrendorf 92 Aufzählungstyp • Explizites Setzen eines Aufzählungswertes durch Bezeichner=Wert • Durch explizites Setzen eines Aufzählungswertes beginnt das Weiterzählen bei diesem Wert. Es ist erlaubt, dass zwei Bezeichner den gleichen Wert haben. Bereits definierte Bezeichner kann man als Wertangabe verwenden. • • Beispiel: enum aufzaehl1 { eins=1, zwei = eins+1, fuenf=zwei*zwei+eins }; enum aufzaehl2 { wert1=4, wert2=4, wert3, wert4}; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 93 Boolean Typ • • • • Neu in C99 <stdbool.h> Dort ist ein Makro bool definiert, das den Wert _Bool hat _Bool mit Werten 0 und 1 • Wird implementiert durch einen unsigned int Typ Beispiel: #include <stdbool.h> _Bool b1; bool b2; // b1 ist vom Typ _Bool // ditto b2 bool less(int x, int y) { bool b = x < y; return b; } Fachbereich Angewandte Informatik // Teste, ob x kleiner als y ist Programmieren in C Prof. Dr. Rudolf Berrendorf 94 Reelle Fließkommatypen • • • Drei reelle Fließkommatypen: float, double, long double Größenverhältnis: float ≤ double ≤ long double Alle Fließkommakonstanten ohne expliziten Typzusatz sind vom Typ double! • In C89 liefern die meisten mathematischen Bibliotheksfunktionen (sin etc.) einen double-Wert, auch wenn das Argument ein float war. Beispiel: float f1, f2; double d; long double ld; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 95 Mindestanforderungen an Wertebereich Die folgenden logischen Konstanten sind in <floats.h> definiert und geben jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen an die Wertebereiche der Fließkommatypen sind dahinter angegeben (DBL_ und LDBL_ Konstanten existieren ebenfalls): Konstante Mindestanforderung an Wert FLT_ROUNDS Bedeutung Rundungsart: 0: nach 0 (Abschneiden) 1: zum Nächsten 2: nach +∞ 3: nach - ∞ FLT_EPSILON 1e-5 Minimales x, s.d. 1.0+x ≠1 FLT_DIGITS 6 Anz. Dezimalziffern Genauigkeit FLT_MIN 1e-37 Min. positive Zahl FLT_MAX 1e37 Max. darstellbare Zahl Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 96 Komplexe Fließkommatypen • • Neu in C99 Typ _Complex mit den Prefixen float, double, long double • Komplexer Wert wird intern repräsentiert als Feld des Basistyps mit 2 Elementen (reeller und imaginärer Teil) • #include <complex.h> • In complex.h ist ein Makro complex definiert, hinter dem _Complex steht (sollte man verwenden) Beispiel: float _Complex fc; double complex dc; long double complex ldc; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 97 Leerer Typ (void) • • • Der leere Typ void hat keine Werte und auf ihm sind keine Operationen definiert. Er dient als Statthalter für nicht-existente Objekte. Verwendung im Zusammenhang mit: – Generischen Zeigern: void *malloc(size_t size); – Funktionsdefinition einer Funktion, die kein Resultat liefert: void f(int x); – Typangabe in einer parameterlosen Funktion: int f(void); Beispiele: void *ptr; void f1(int x); int f2(void); /* Zeiger auf nichts, generischer Zeiger */ /* Funktion liefert keinen Ergebniswert */ /* Funktion hat keine Parameter */ int i; i = 3+4; (void) 3+4; /* explizites „Wegschmeißen“ eines Wertes */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 98 Zusammengesetzte Typen Zusammengesetzte Typen sind induktiv über den bisher definierten Basistypen definiert. Seien T, T1,...,Tn Typen. Dann sind in C auch Typen: 1. 2. 3. 4. 5. Zeiger auf T Feld von T Struktur von T1,...,Tn Vereinigung von T1,...,Tn Funktion, die Argumenttypen T1,...,Tn hat und einen Resultattyp T besitzt Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 99 Zeigertyp • • • • • • • • Zu einem Typ T (Beispiel: int) kann man einen Typ Zeiger auf T definieren (Beispiel: Zeiger auf int). Syntax für Variablendeklaration: T *varname; Zeiger = Adresse im (virtuellen) Adressraum Ein Zeiger kann dann auf ein Objekt des Basistyps zeigen Der spezielle Zeigerwert NULL (in <stddef.h> definiert) gibt an, dass der Zeiger momentan auf kein Objekt zeigt. Zeiger auf T1 und Zeiger auf T2 sind nicht typkompatibel! Generischer Zeigertyp void * ist typkompatibel zu allen Zeigertypen, d.h. man kann einen Wert vom Typ void * zu einem beliebigen anderen Zeigertyp umwandeln und umgekehrt. die zwei wichtigsten Operatoren für Zeiger sind: – Adressoperator &: Angewandt auf ein Objekt vom Typ T liefert es die Adresse dieses Objektes als Typ Zeiger auf T – Dereferenzierungsoperator *: Angewandt auf einen Zeigerwert p vom Typ Zeiger auf T liefert es den Wert vom Typ T, auf den der Zeiger zeigt (falls dies ein zulässiger Zeigerwert ist) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 100 Beispiel 104 108 i j ptr_i i = 5; 5 ? ? ptr_i = NULL; 5 ? 0 ptr_i = &i; 5 ? 100 j = *ptr_i; 5 5 100 *ptr_i = 7; 7 5 100 ptr_i = &j; 7 5 104 int i, j; int *ptr_i; Fachbereich Angewandte Informatik Adresse = 100 Programmieren in C Prof. Dr. Rudolf Berrendorf 101 Feldtyp • • • • Feld ist Ansammlung von Objekten gleichen Typs, die hintereinander im Speicher abgelegt sind Für jeden Typ T (außer void und Funktionstypen) kann man einen Typ Feld von T definieren: T feldname[Dimensionsangabe] Mehrdimensionale Felder möglich: T feldname[dim1]...[dimn] Feldelemente: – Indizierung über feld[index] – Index beginnt bei 0 bis dim-1 – Bei mehrdimensionalen Feldern: feldname[index1]...[indexn] – Feldname[index1,...,indexn] ist syntaktisch erlaubt, bedeutet aber etwas vollkommen verschiedenes! Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 102 Speicherung von Feldern • • • Im Gegensatz zu Java wird in C ein zweidimensionales Feld nicht als Vektor von Zeigern implementiert, sondern alle Feldelemente hintereinander im Speicher abgelegt (d.h. dim1*dim2 Elemente). Zeilenweise Abspeicherung, d.h. letzter Index ändert sich schneller. Optimierung des Speicherzugriffs (und damit der Laufzeit) bei mehrdimensionalen Feldern: Letzten Index schneller laufen lassen! // C int x[2][3]; // Java int[][] x = new int[2][3]; // Zugriff for (int i=0; i<2; i++) for(int j=0; j<3; j++) x[i][j] = i+j; // Zugriff for (int i=0; i<2; i++) for(int j=0; j<3; j++) x[i][j] = i+j; Daten Vektor mit Zeigern x x Fachbereich Angewandte Informatik Daten Programmieren in C Prof. Dr. Rudolf Berrendorf 103 Dimensionsangaben • Explizite Dimensionsangabe in eckigen Klammern (Normalfall) Beispiel: int a[10]; int b[3][4]; • /* int-Feld der Dimension 10 */ /* int-Feld der Dimension 3x4 */ Implizite Dimensionsangabe durch Angabe eines Initialisierungswertes Beispiel: char str[] = "Init.wert"; int c[] = {1,2,3,4}; • /* char-Feld der Dim.10 (inkl \0) */ /* int-Feld der Dimension 4 */ Weglassen der Dimensionsangabe (nur innerste Dimension möglich bei mehrdimensionalen Feldern): – Bei formalen Funktionsparametern (Typ T wird automatisch in Zeiger auf T umgewandelt) – Objekt hat external linkage (später) und Definition geschieht anderswo – Feld wird im gleichen Modul (Datei mit allen includes) später deklariert Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 104 Beispiele zum Weglassen der Dimensionsangabe /* Funktionsparameter */ void myfun ( int a[] ); /* external linkage */ int d[]; /* in Datei x.c */ int d[10]; /* in Datei y.c */ /* gleiches Modul */ int e[][4]; int e[2][4]; Fachbereich Angewandte Informatik /* Matrix der Dimension ?x4 */ /* Matrix der Dimension 2x4 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 105 Dimensionsangaben • • Dimensionsangaben müssen zur Übersetzungszeit auswertbar d.h. konstant sein. Ausnahmen (C99): – variable length arrays • Größe wird erst zur Laufzeit festgelegt • Nur für lokale Feldvariablen in Blöcken erlaubt (d.h. kein static oder extern möglich) • Feld wird jedes mal bei Eintritt in den Block angelegt am Deklarationspunkt und wird gelöscht, wenn der Block verlassen wird – Formale Feldparameter in Funktionen können durch vorangehende Parameter dimensioniert werden oder in Funktionsprototypen mit * angegeben werden /* Feldparameter mit variabler Länge */ void myfun ( int n, int m, int a[n][m] ) { /* Feld mit variabler Länge */ int dim = n*n, b=m+5; int feld[dim]; } /* Funktionsprototyp */ void yourfun ( int a[*][*] ) ; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 106 Zusammenhang Felder-Zeiger Es besteht ein enger Zusammenhang zwischen Feldern und Zeigern. Schreibt man den Feldnamen in einem Ausdruck, dann wird dies implizit umgewandelt in einen Zeiger auf die Adresse des ersten Feldelementes (Startadresse des Feldes) Beispiel: int a[100]; int *ptr; ptr = a; ptr = &(a[0]); *ptr = 5; *ptr = 7; Fachbereich Angewandte Informatik // // // // ptr zeigt auf erstes Element von a, d.h. a[0] äquivalent zu letzter Zeile ptr zeigt auf die absolute (virtuelle) Adresse 5 In die Speicherstelle 5 wird der Wert 7 geschrieben Programmieren in C Prof. Dr. Rudolf Berrendorf 107 Strukturtyp Mehrere benannte Komponenten lassen sich in C zu einem neuen Strukturtyp zusammenfassen. Die einzelnen Komponenten werden hintereinander im Speicher abgelegt, es können jedoch „Löcher“ im Speicher zwischen den einzelnen Komponenten entstehen (Padding). Syntax: struct Bezeichner { Komponentenliste }; Der Bezeichner ist optional. Der Name des neu eingeführten Typs ist struct Bezeichner, d.h. inkl. struct. Die Komponenten haben die Form von Deklarationen, im Normalfall in der einfachen Form Typangabe Komponentenname ; Zur Definition von rekursiven Datenstrukturen kann man innerhalb der Komponentenliste schon auf den neueingeführten Typ T über einen Zeigertyp Zeiger auf T verweisen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 108 Beispiele /* Typdefinition */ struct complex { float re; float im; }; /* Variablendeklarationen mit dem neuen Typen */ struct complex complexe_variable = {3.14, 2.71}, *ptr_cvar = &complexe_variable; complexe_variable re 3.14 im 2.71 Fachbereich Angewandte Informatik ptr_cvar Programmieren in C Prof. Dr. Rudolf Berrendorf 109 Beispiele /* Typdefinition */ struct complex { float re; float im; }; /* "Konstruktor" für diesen Datentyp in C */ struct complex new_complex ( float re, float im ) { struct complex c; c.re = re; c.im = im; // Zuweisung an die Komponenten der Struktur return c; // Hier wird der Wert der lokalen Variablen c zurückgegeben! } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 110 Beispiele /* Typdefinition und Variablendeklaration in einem Schritt */ struct Binaerbaum /* Typ struct Binaerbaum */ { int daten; struct Binaerbaum *linker_Sohn; // Zeiger linker Sohn struct Binaerbaum *rechter_Sohn; // Zeiger rechter Sohn } Baum1, Baum2, // 2 Variablen dieses Typs *ptr_Baum; // 1 Variable mit Zeigertyp daten Baum1 Baum2 0 0 ptr_Baum linker_Sohn rechter_Sohn Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 111 Referenzierung der Komponenten Der Zugriff auf die einzelnen Komponenten einer Strukturvariablen erfolgt über Variablenname . Komponentenname Beispiel: complexe_variable.re = 1.0; complexe_variable.im = 0.0; Für Variablen, die den Typ Zeiger auf Struktur haben, gibt es eine abkürzende Schreibweise (diese wird praktisch nur verwandt!). Statt (*Variablenname).Komponentenname: Variablenname -> Komponentenname Beispiel: ptr_cvar->re = 1.0; (*ptr_cvar).re = 1.0; Fachbereich Angewandte Informatik // äquivalent zu letzter Zeile Programmieren in C Prof. Dr. Rudolf Berrendorf 112 Weiteres Beispiel struct Binaerbaum baum1 = {1, NULL, NULL}, baum2 = {2, NULL, NULL}, baum3 = {3, NULL, NULL}, ptr_baum = &baum1; daten Baum1 Baum2 Baum3 1 2 3 ptr_Baum linker_Sohn rechter_Sohn ptr_Baum->linker_Sohn = &Baum2; ptr_Baum->rechter_Sohn = &Baum3; Baum1 1 Fachbereich Angewandte Informatik Baum2 Baum3 2 3 Programmieren in C Prof. Dr. Rudolf Berrendorf 113 Padding Auf vielen Rechner gibt es Einschränkungen bzw. Leistungseinbußen, wenn Daten bestimmten Basistyps nicht an Wortgrenzen im Speicher abgelegt sind (Alignment). Beispiel: float x; double y; Angenommen, ein float-Wert belegt 4 Bytes und ein double-Wert 8 Bytes. Würden die beiden Variablen hintereinander (ohne Lücke) im Speicher abgelegt, so würde die Variable y nicht auf einer Adresse abgelegt, die ein Vielfaches ihrer Größe ist. Aus diesem Grunde kann es sein, dass ein Compiler automatisch eine (kleinen) Zwischenraum zwischen den Komponenten einfügt. 4 0 8 x Fachbereich Angewandte Informatik frei y Programmieren in C Prof. Dr. Rudolf Berrendorf 114 Operationen auf Strukturen Es ist möglich 1. Eine Strukturvariable einer anderen Strukturvariablen gleichen Typs zuzuweisen 2. Dass eine Funktion eine Struktur als Ergebnistyp hat 3. Dass eine Struktur als Parameter an eine Funktion übergeben wird Es ist nicht möglich zwei Strukturvariablen auf Gleichheit zu testen. Wieso? Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 115 Bit-Felder Innerhalb einer Struktur, und nur dort, ist die Definition von Bit-Feldern erlaubt. Die Strukturkomponenten, die das Bit-Feld ausmachen, müssen einen der Typen int, unsigned int oder signed int haben. Beispiel: struct RX_opcode { unsigned int unsigned int unsigned int unsigned int unsigned int unsigned int }; opcode:8; R1:4; X2:4; B2:4; D2:12; :0; /* /* /* /* /* /* Operationscode */ Register 1 */ Segmentregister */ Basisregister */ Displacement */ Alignment */ Hinter dem Typnamen der Komponenten steht der (optionale) Name der Komponenten gefolgt von einem Doppelpunkt und der Anzahl (nichtnegativ, konstant) der Bits. Ist die Anzahl 0, bewirkt dies das Ablegen einer eventuell folgenden Bit-Komponenten auf einer Wortgrenze. Maximalgröße eines Bit-Feldes: Größe eines int Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 116 Vereinigungstyp Die Syntax zur Definition eines Vereinigungstyps ist fast identisch mit der eines Strukturtyps, lediglich das Schlüsselwort ist union statt struct: union Bezeichner { Komponentenliste }; Der Zugriff auf die Komponenten erfolgt ebenfalls durch Variablenname.Komponentenname In der Abspeicherung der Komponenten besteht jedoch ein grundlegender Unterschied. Beim struct werden die Komponenten hintereinander im Speicher abgelegt, beim union übereinander! Die Größe des Vereinigungstyps (d.h. die Ausdehnung im Speicher) ist die Größe der größten Komponenten. Ein Vereinigungstyp sollte nur eingesetzt werden, wenn dies wirklich erforderlich ist. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 117 Beispiel struct Datum { enum {double_Datum, int_Datum } Datumtyp; /* 1. Komponente struct */ union { double dwert; int iwert; } Datumwert; /* 2. Komponente des struct */ } datum; /* Variable vom Typ struct Datum */ datum.Datumtyp = int_datum; datum.Datumwert.iwert = 13657; datum.Datumtyp = double_datum; datum.Datumwert.dwert = 13657.0; /* Erlaubt, macht aber hier keinen Sinn */ datum.Datumwert.dwert = 13657.0; ... = datum.Datumwert.iwert; Adresse= 100 Datumtyp 104 dwert iwert Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 118 Funktionstyp Zu einem Typ T (außer Feld- oder selbst Funktionstyp) kann man einen Typ Funktion, die T als Resultat liefert konstruieren. Die "normale" Form dafür sieht aus (später mehr): Resultattyp Funktionsname ( Parameterangaben ) Beispiel: int myfun (int x, float y); 3 Operationen sind mit Funktionsbezeichnern möglich: • Aufruf der Funktion: myfun(1, 3.14); • Benutzen der Funktion als aktuellen Parameter: anderefun(1, myfun); • Zuweisung an Variable entsprechenden Typs: funvariable = myfun; Erscheint ein Funktionsname außerhalb eines Funktionsaufrufes, d.h. nicht myfun(...), so wird der Typ des Ausdrucks automatisch in Zeiger auf eine Funktion, die T liefert umgewandelt (siehe Operation 3 oben). Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 119 Funktionsdeklaration und -definition Unterschied zwischen Funktionsdefinitionen und -deklarationen: 1) Funktionsdefinition: Es wird ein Objekt dieses Typs erzeugt (Speicher verbraucht). Beispiel: int square( int x ) { return x*x; } 2) Funktionsdeklaration: Es werden nur Angaben zum Typ gemacht, es wird kein Speicher verbraucht. Beispiel: int square( int x ) ; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 120 Beispiele /* Funktion, die kein Argument hat und ein int-Resultat liefert */ int fun1(void); /* Funktion, die ein int-Argument erwartet und kein Ergebnis liefert */ void fun2( int x ); /* Strukturtyp */ struct complex {float x; float y;}; /* Funktion, die 3 Argumente erwartet: - ein int - ein float - ein struct complex und einen Zeiger auf ein int als Ergebnis liefert. */ int * fun3(int x, float y, struct complex z); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 121 Typdeklaration Mit einer typedef-Deklaration kann man ein Synonym (Abkürzung) für einen anderen Typen einführen, es entsteht dadurch kein neuer Typ! Vorteile: • Komplizierte Typen sind lesbarer • Bedeutungsvolle Namen für Typen Die allgemeine Syntax ist: typedef Deklaration; Deklaration wird später besprochen. Im einfachen Fall sieht dies so aus: typedef Typ typedef-name ; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 122 Beispiele /* Counter (der neu eingeführte Bezeichner) ist ein Synonym für int */ typedef int /* Typangabe */ Counter; /* neuer Name */ /* Unter Complex ist der struct-Typ ab jetzt ansprechbar */ typedef struct {float re; float im;} /* Typangabe */ Complex ; /* neuer Name */ Complex c; c.re = 5.0; c.im = 0.0; /* Hier wird es komplizierter und stimmt nicht mehr mit dem einfachen Schema "typedef Typ Name" überein. Hier wird der Name "Cfun" eingeführt, der ein Synonym ist für den Typ "Zeiger auf eine Funktion, die kein Argument nimmt und ein Complex als Resultat liefert. */ typedef Complex (*Cfun)(void); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 123 Typzusätze Typzusätze (type qualifier) helfen einem Compiler effizienten und korrekten Code in Spezialfällen zu erzeugen. Durch den Zusatz const gibt man an, dass auf dieses Objekt nur lesen zugegriffen wird. Durch den Zusatz volatile gibt man an, dass dieses Objekt auch außerhalb des Programms verändert werden kann (z.B. in parallelen Programmen). Die Position der Typzusätze in einer Definition oder Deklaration ist wichtig (s.u.)! Der Zusatz restrict teilt dem Compiler im Zusammenhang mit Zeigern mit, dass dieser Zeigerwert zur Zeit die einzige Möglichkeit ist, auf das Objekt zuzugreifen (d.h. kein anderer Zeiger zeigt auf das gleiche Objekt). Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 124 Beispiel const int x = 5; // Wert von x wird nicht mehr modifiziert int fun(const int x) {...} const int *const_ptr; int * const ptr_const; // Argument x wird nicht verändert // Zeiger auf int-Konstante // konstanter Zeiger auf int volatile int x; // Synchronisationsvariable void synchronisieren(void) { /* Frage solange Wert ab, bis dieser (von außen) auf einen Wert ungleich 0 gesetzt wird. */ while(x == 0) ; } extern char * restrict ptr; void alloc(void) { ptr = malloc(sizeof(*ptr)); } Fachbereich Angewandte Informatik // kein anderer Zeiger zeigt auf das Objekt, // auf das ptr zeigt // nur ptr zeigt auf dieses Objekt Programmieren in C Prof. Dr. Rudolf Berrendorf 125 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 126 Deklarationen • • • Deklaration = Assoziation eines Bezeichners mit einem Objekt Definition = Deklaration + Speicherbereich Bezeichner für folgende Objekte möglich: – – – – – – – – Variablen Funktionen Typen Typbezeichner Komponenten von Struktur- oder Vereinigungstypen Aufzählungskonstanten Anweisungslabel Präprozessor-Makros Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 127 Deklarationen Deklarationen bestehen aus: 1. 2. 3. 4. Optionaler Speicherklassenangabe Optionaler Typangabe Einem Deklarator mit dem Namen des zu deklarierenden Objektes Optionale Initialisierungswerte für dieses Objekt (nur in Definition) Beispiel: static int x = 5; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 128 Programmhierarchie • • • • • • Ein C-Programm kann aus mehreren Programmdateien bestehen Eine Programmdatei (xyz.c) kann aus mehreren top-levelDeklarationen bestehen, in denen globale Variablen und Funktionen definiert werden Jede Funktion kann Deklarationen von formalen Parametern der Funktion haben, sowie einen Funktionsrumpf Jeder Funktionsrumpf kann mehrere evtl. geschachtelte Blöcke enthalten Jeder Block besteht aus optionalen Deklarationen gefolgt von optionalen Anweisungen Eine Übersetzungseinheit ist eine Datei inkl. aller eingefügten Headerdateien Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 129 Programmhierarchie C-Programm x.c y.c z.c Top-level Deklarationen: - globale Variablen - Typdeklarationen - Funktionsdefinitionen Funktionsdefinition: Rumpf (äußerer Block) Innerer Block Fachbereich Angewandte Informatik Formale Parameter int f ( int x, float y ) { int i; i = x; { int j; j = i; } } Programmieren in C Prof. Dr. Rudolf Berrendorf 130 Beispiel datei1.c int i; datei2.c // Variablendefinition int i; // Typdeklaration struct mystruct { int x; float y; }; // Variablendefinition // Funktionsdefinition int myfun( int x ) { return x+1; } int j; // Funktionsdefinition int main( int argc, char **argv ) { int i = 0; // keine top-level Deklaration printf("myfun1=%d\n", myfun(i); printf("myfun1=%d\n", myfun2(i)); // Variablendefinition // Funktionsdefinition int myfun2( int x ) { return x+2; } } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 131 Gültigkeitsbereich von Bezeichnern Bezeichner haben einen Gültigkeitsbereich (scope), innerhalb dessen die Deklaration aktiv (bekannt) ist. In C existieren 5 mögliche Gültigkeitsbereiche: 1. Bezeichner einer top-level-Deklaration: vom Deklarationspunkt bis zum Ende der Datei 2. Bezeichner in einer Deklaration von formalen Parametern einer Funktion: vom Deklarationspunkt bis zum Ende der Funktion bzw. des Funktionsprototypen 3. Bezeichner zu Beginn eines Blocks: Deklarationspunkt bis Ende Block 4. Anweisungslabel: innerhalb der umschließenden Funktion 5. Makroname: vom #define bis zum Ende der Datei bzw. bis zu einem entsprechenden #undef Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 132 Beispiel /* TEST_CODE ist bis zum Ende der Datei gültig */ #define TEST_CODE /* global_var ist gültig bis zum Ende der Datei */ int global_var; /* Parameter x ist nur innerhalb der Klammern gültig */ double sin( double x ); int main( int argc, char **argv ) { /* local_var ist nur innerhalb dieses Blocks gültig */ int local_var; /* Das Label ende ist innerhalb dieser Funktion gültig */ ende: ; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 133 Überladen von Bezeichnern In C existieren 5 Namensklassen. Ein Bezeichner x kann gleichzeitig in allen 5 Klassen existieren. Aus dem Kontext ist definiert, welcher Bezeichner welcher Namensklasse gemeint ist. Die Nutzung des gleichen Bezeichners in mehr als einer Namensklasse ist schlechter Programmierstil! Die Namensklassen sind: 1. Präprozessorname (nach Präprozessorphase nicht mehr) 2. Anweisungslabel 3. Name eines Struktur-, Vereinigungs-, Aufzählungstypen 4. Komponente eines Struktur-, Vereinigungstypen 5. Alle anderen Bezeichner Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 134 Beispiel #define x y /* Präprozessorname */ int main( int argc, char **argv) { struct x /* Name eines Strukturtyps */ { int x; /* Name einer Komponenten */ } x; /* Name einer Variablen */ x: ; /* Label */ /* alles klar? */ x.x = 4; goto x; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 135 Sichtbarkeit Zusätzlich zum Gültigkeitsbereich eines Bezeichners und den Namensklassen kann es jedoch durch eine (erlaubte) Mehrfachverwendung des gleichen Bezeichners zu Konflikten kommen: Beispiel: int f; /* globale Variable */ int main(int argc, char **argv) { double f; /* Block-lokale Variable */ f = 5.0; /* welches f? */ } Namenskonflikte treten dann auf, wenn gleiche Bezeichner zur gleichen Namensklasse gehören, aber in zwei verschiedenen (geschachtelten) Gültigkeitsbereichen. Prioritätsegeln: • Formale Funktionsparameter überschreiben globale Deklarationen • Deklaration am Blockanfang überschreiben äußere Deklarationen Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 136 Vorwärtsreferenzen Normalerweise muss vor Benutzung eines Bezeichners dieser deklariert sein (Compiler muss Größe etc. wissen). Zwei Ausnahmen: 1. Anweisungslabel in goto-Anweisung darf vor der Label-Deklaration genutzt werden 2. Ein Typbezeichner eines Struktur, Vereinigungs oder Aufzählungstypen kann schon vor seiner Deklaration in einer typedef-Deklaration oder in Verbindung mit einem Zeigertypen benutzt werden. Beispiel: Beispiel: struct type_a { struct type_b *x; }; struct type_b { struct type_a *y; }; int main(int argc, char **argv) { goto ende; Fachbereich Angewandte Informatik ende: ; } Programmieren in C Prof. Dr. Rudolf Berrendorf 137 Lebensdauer von Speicherobjekten Objekte (Variablen, Funktionen) existieren zur Laufzeit eines Programms. Die Lebensdauer (storage duration) eines Objektes ist die Zeitdauer, die der Speicherbereich angelegt ist, der das Objekt aufnimmt. Man unterscheidet: 1. Statische Lebensdauer: Speicher wird vor dem Start des Programms ein mal angelegt und wird erst wieder freigegeben, wenn das Programm beendet wird. (Funktionen, top-level Variablen, staticVariablen) 2. Automatische Lebensdauer: Der Speicher wird jedes mal zu Beginn eines Blocks angelegt und am Ende des Blocks wieder frei gegeben. (Lokale Variablen eines Blocks, Parameter einer Funktion) 3. Dynamische Lebensdauer: Der Speicherbereich wird explizit durch den Programmierer verwaltet (angelegt, freigegeben). Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 138 Beispiel Codesegment int global_j; main f int f( int arg ) { int local_j; local_j = arg; global_j = arg; return global_j + local_j; } Datensegment global_j int main(int argc, char **argv) { int i,j; global_j = 6; for(i = 0; i < 2; ++i) j = f(i); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf Stacksegement argc argv i J arg local_j 139 Beispiel Codesegment int main(int argc, char **argv) { static int x; auto int y; int *p; p = (int *) malloc(sizeof(*p)); free(p); } main Datensegment x Stacksegement argc argv y p Heap *p Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 140 Bindung von Bezeichnern Teilt man das gesamte Programm auf mehrere Dateien auf (Datei_1.c, ..., Datei_n.c), so ist weiterhin die Bindung von Bezeichnern von Interesse: 1. 2. 3. Externe Bindung: Der gleiche Bezeichner bezeichnet in allen Dateien das gleiche Objekt. Interne Bindung: Nur innerhalb einer Datei bezeichnet ein Bezeichner das gleiche Objekt. Keine Bindung: Objekte, die nur einmal vorkommen. Regeln (Speicherklasse=auto, register, static, extern): Top-level Innerhalb Block Keine Speicherklassenangabe Variable: externe Bindung Funktion: wie mit extern Variable: ohne Bindung Funktion: wie mit extern Speicherklassenangabe static Interne Bindung Variable: ohne Bindung Funktion: interne Bindung Speicherklassenangabe extern a) Bindung wie gleicher Bezeichner auf top-level b) Falls nicht vorhanden: externe Bindung a) Bindung wie top-level b) Nicht vorhanden: extern Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 141 Beispiel Top-level Innerhalb Block Keine Speicherklassenangabe Variable: externe Bindung Funktion: wie mit extern Variable: ohne Bindung Funktion: wie mit extern Speicherklassenangabe static Interne Bindung Variable: ohne Bindung Funktion: interne Bindung Speicherklassenangabe extern a) Bindung wie gleicher Bezeichner auf top-level b) Falls nicht vorhanden: externe Bindung a) Bindung wie top-level b) Nicht vorhanden: extern int glo_var; int getchar(); static int datei_var; static int f(); extern int glo_var; extern int glo_var2; int main(int argc, char **argv) { int x; int getchar(); static int y; static int g(); extern int glo_var; } Fachbereich Angewandte Informatik /* /* /* /* /* /* externe externe interne interne externe externe /* /* /* /* /* keine Bindung */ externe Bindung (s.o.) */ keine Bindung */ interne Bindung */ externe Bindung (s.o.) */ Programmieren in C Prof. Dr. Rudolf Berrendorf Bindung Bindung Bindung Bindung Bindung Bindung */ */ */ */ (s.o.) */ */ 142 Speicherklasse Die Speicherklasse eines Objektes hat Einfluss auf: 1. 2. 3. Bindung Lebensdauer Gültigkeitsbereich Es existieren 4 Speicherklassen: 1. 2. 3. 4. auto register static extern Die Angabe einer Speicherklasse ist optional bei einer Definition / Deklaration eines Objektes möglich. Dann muss sie an erster Stelle stehen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 143 Speicherklasse auto Nur in Deklarationen am Anfang von Blöcken erlaubt Dort ist dies auch der Default und wird deshalb meist weggelassen Lebensdauer: automatische Lebensdauer (bei Blockeintritt wird Objekt erzeugt und bei Verlassen des Blocks vernichtet) Gültigkeit: Das Objekt ist bis zum Ende des Blocks bekannt. Beispiel: int myfun() { int x; return 3; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 144 Speicherklasse register Ursprüngliche Idee: Hinweis für Compiler (hat heute nur Nachteile; s.u.) Nur für lokale Variablen und Funktionsparameter möglich Kein Adressoperator zusammen mit register-Variablen möglich Sonst wie auto Lebensdauer: automatische Lebensdauer (bei Blockeintritt wird Objekt erzeugt und bei Verlassen des Blocks vernichtet Gültigkeit: Das Objekt ist bis zum Ende des Blocks bekannt. Beispiel: int myfun(register int y) { register int x; return 3 * y; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 145 Speicherklasse static In Funktionsdefinition: keine externe Bindung In Funktionsdeklaration: Funktion wird später in Datei noch definiert (dann ebenfalls mit static-Angabe) Lebensdauer: Statische Lebensdauer (Wert bleibt auch bei Verlassen des Blocks erhalten) Gültigkeit: Das Objekt ist nur innerhalb des Blocks (Variablen) bzw. innerhalb der Datei (Funktionen, top-level Variablen) bekannt. Beispiel: int myfun(int y) { static int x; return x * y; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 146 Speicherklasse extern In top-level Deklarationen und am Anfang eines Blocks Lebensdauer: Statische Lebensdauer (Wert bleibt auch bei Verlassen des Blocks erhalten) Gültigkeit: Das Objekt einer top-level Deklaration ist global bekannt. Objekte, die am Anfang eines Blocks deklariert werden, sind nur innerhalb des Blocks bekannt. Beispiel: extern int yourfun(int x); extern int x; int myfun(int y) { return x * y; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 147 Default-Speicherklasse Ist keine Speicherklassenangabe in einer Deklaration vorhanden, gilt folgender Default: 1. 2. 3. 4. Top-level Deklarationen haben Speicherklasse extern Funktionsdefinitionen haben Speicherklasse extern Parameterdeklarationen ohne Angabe register (nur diese Angabe wäre dort erlaubt) bedeuten "kein register" Deklarationen am Anfang eines Blocks haben die DefaultSpeicherklasse extern für Funktionen und auto für alle anderen Objekte Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 148 Beispiel void myfun(void) { extern int lock; register int i; auto int j; static int k; static int f(void); extern int g(void); /* /* /* /* global bekannte Variable */ lokale bekannte Variable */ lokal bekannte Variable */ lokal bekannte Variable; behält Wert */ /* lokal bekannte Funktion */ /* global bekannte Funktion */ } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 149 Deklarationen Allgemeine Form einer Deklaration: Typangabe Deklarator Wobei: 1. 2. Typangabe die Typspezifikation einschließlich Speicherklassenbezeichner und Typzusätze ist Deklarator den Bezeichner des Objektes enthält, das deklariert werden soll Beispiel: int x; const int y; int *const z; float a[3]; Fachbereich Angewandte Informatik /* /* /* /* Typangabe=int; Bezeichner=x */ Typangabe=const int; Bezeichner=y */ Typangabe=int; Bezeichner=z (Rest gehört zu Dekl.) */ Typangabe=float; Bezeichner=a */ Programmieren in C Prof. Dr. Rudolf Berrendorf 150 Art der Deklaration Deklarator hat die Form: Bezeichner Dann ist der Typ des Bezeichners Typangabe. Beispiel: int x; /* x hat Typ int */ struct { float re; float im; } c1; /* c1 hat den Strukturtyp */ typedef struct {float re; float im; } Complex; Complex c2; /* c2 hat Typ Complex */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 151 Art der Deklaration Deklarator hat die Form: ( Deklaration ) Dann ist der Typ des Bezeichners der, der durch Deklaration spezifiziert wird. Die Klammern dienen der besseren Lesbarkeit oder zum Setzen von Bindungsprioritäten. Beispiel: int int int int (x); *(*p); (*q)[]; *(q[]); Fachbereich Angewandte Informatik /* /* /* /* x hat Typ int */ entspricht **p */ Zeiger auf Feld von int's */ Feld von Zeigern auf int */ Programmieren in C Prof. Dr. Rudolf Berrendorf 152 Art der Deklaration Deklarator hat die Form: * Deklaration * type_qualifier_list Deklaration Dann ist der Typ des Bezeichners ejn Zeiger auf den Typ, der durch Typangabe Deklaration spezifiziert wird, unter Berücksichtigung der type_qualifier_list. type_qualifier_list ist eine optionale Liste aus const und/oder volatile. Beispiel: int *x; const int *ptr_const_ptr; int * const constr_ptr; int *volatile vol_ptr; struct { float re, im;} *complex_ptr; Fachbereich Angewandte Informatik /* /* /* /* x hat Typ: Zeiger auf int */ Zeiger auf int-Konstante */ Konstanter int-Zeiger */ volatiler int-Zeiger */ /* Zeiger auf Struktur */ Programmieren in C Prof. Dr. Rudolf Berrendorf 153 Art der Deklaration Deklarator hat die Form: Deklaration [ ] Deklaration [const_expr ] Deklaration [var_expr] Deklaration [*] C99 C99 Dann ist der Typ des Bezeichners ejn Feld des Basistyps, der durch Typangabe Deklaration spezifiziert wird. const_expr muss ein konstanter Ausdruck sein, dessen Wert echt größer 0 sein muss. Die Fälle, in denen die Dimensionsangabe weggelassen werden kann, wurde bereits besprochen. Ebenso Felder variabler Größe. Beispiel: float a[3]; float *(pa[3]); int x[]; volatile int y[3]; float b[3][4]; Fachbereich Angewandte Informatik /* /* /* /* /* float-Feld mit 3 Elementen */ Feld von float-Zeigern */ int-Feld ohne Dimensionsangabe */ Feld von volatilen int's */ Feld der Dimension 3x4 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 154 Art der Deklaration Deklarator hat die Form: Deklaration ( ) Deklaration ( id_list ) Dann ist der Typ des Bezeichners ejne Funktion, die einen Wert von einem Typ liefert, der durch Typangabe Deklaration spezifiziert wird. Nicht zulässig als Basistyp sind Funktions- oder Feldtypen. Fehlt id_list, so bedeutet dies, dass keine Angaben zu Parametern der Funktion gemacht werden (nicht notwendigerweise, dass diese Funktion keine Parameter besitzt). Diese Form der Spezifikation eines Funktionstypen sollte nicht mehr verwendet werden. Statt dessen gibt es Funktionsprototypen. Beispiel: int f(); int *(g()); int (*h)(); int f(x,y) float x,y; {...} Fachbereich Angewandte Informatik /* /* /* /* /* Funktion, die int liefert */ Funktion, die Zeiger auf int liefert */ Zeiger auf Funktion, die int liefert */ Funktionsdefinition mit zwei formalen Parametern */ so nicht mehr benutzen! */ Programmieren in C Prof. Dr. Rudolf Berrendorf 155 Art der Deklaration Deklarator hat die Form: Deklaration ( parameter_type_list ) Dann ist der Typ des Bezeichners ejne Funktion, die einen Wert von einem Typ liefert, der durch Typangabe Deklaration spezifiziert wird. Nicht zulässig als Basistyp sind Funktions- oder Feldtypen. parameter_type_list ist eine durch Kommas getrennte Liste von Typen oder Deklaratoren, die Typangaben zu den den Funktionsparametern machen: • Die Typangabe void besagt, dass diese Funktion keine Parameter hat. • Fall die Liste mit ... endet, so bedeutet dies, dass keine Angaben über Anzahl und Typ weiterer Parameter gemacht wird. • Als Speicherklassenangabe ist nur register erlaubt Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 156 Beispiel int ((*u) ( char (*) float ) (long), ) (double, ... ); Um solch eine Deklaration zu verstehen, sollte man zuerst versuchen, die Klammerhierarchie zu verstehen. Anschließend sollte man den Namen des zu deklarierenden Objektes herausfinden. Die Deklaration hat die Form: int (*u()) () . Der Name des Objektes ist u. u ist ein Zeiger auf eine Funktion, die eine Funktion liefert, die wiederum ein int-Resultat liefert. u hat zwei Parameter: (1) char (*) (long) (hier fehlt der Name des formalen Parameters, also nur Typangabe) und (2) float. Der erste Parameter ist ein Zeiger auf eine Funktion, die ein long-Parameter erwartet und ein char als Ergebnis liefert. u liefert als Ergebnis eine Funktion, die ein int liefert und selber mindestens einen Parameter erwartet, dieser ist vom Typ double. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 157 Default-Typen Aus Kompatibilität mit alten C-Compilern ist das Weglassen einer Typspezifikation für Variablen- und Funktionsdefinitionen erlaubt. Der Default ist dann int. Man sollte dies nicht mehr verwenden! In C99 ist dies auch nicht mehr erlaubt, dort muss ein Typ angegeben werden! Beispiel: x; f(i) { return i; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 158 Priorität der Deklaratoren Priorität: 1. Klammerung 2. Funktions- und Felddeklaratoren 3. Zeigerdeklaratoren Beispiel: int *sum1(); int *a[a2]; int (*sum2)(void); Fachbereich Angewandte Informatik /* Funktion, die Zeiger auf int liefert */ /* Feld von Zeigern auf int */ /* Zeiger auf int-Funktion */ Programmieren in C Prof. Dr. Rudolf Berrendorf 159 Implizite Deklarationen Erscheint im Programmtext ein bis dahin unbekannter Bezeichner gefolgt von einer öffnenden Klammer, so wird implizit vom Compiler eine Deklaration vorgenommen, die diese Funktion als int-Funktion auf dem top-level deklariert. Vorsicht: Die Problematik wird klar, wenn man das Beispielprogramm unten anschaut. Beispiel: int main(int argc, char **argv) { double x; x = sin(3); /* sin ist eigentlich vom Typ double sin(double); Hier wird implizit die Deklaration extern int sin(); auf dem top-level durchgeführt. */ } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 160 Deklaration / Definition bei top-level Variablen Deklaration = Typangabe Definition = Typangabe + Anlegen von Speicher Folgerung: Zu jedem Objekt kann es viele Deklarationen evtl. in verschiedenen Dateien geben, aber nur eine Definition geben. Regeln zur Unterscheidung Deklaration/Definition von Variablen auf toplevel: 1. Beginnt eine Deklaration mit dem Schlüsselwort extern (ohne Initialisierung), so ist dies eine Deklaration. 2. Für jede Variable mit externer Bindung muss irgendwo in einer der Programmdateien genau eine Definition erfolgen. 3. Ist ein Initialisierungsausdruck vorhanden, so ist es eine Definition (mit oder ohne extern erlaubt). 4. Schwebende Definition: keine Initialisierung, keine oder staticSpeicherklasse. Kann zu einer Variablen mehrfach in einer Datei vorkommen. Falls keine externe Definition dieses Bezeichners in der Datei (inkl. Includes) existiert, wird daraus eine Definition mit Initialisierungswert 0 gemacht. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 161 Beispiel extern int static int extern int int i3; int i4; int i4; static int i1; i2 = 2; i3 = 3; i5; /* falsch wären: int i2; int i5; */ Fachbereich Angewandte Informatik /* /* /* /* /* /* /* Deklaration; externe Bindung */ Definition; interne Bindung */ Definition; externe Bindung */ schwebende Def. (s.o.); externe Bindung */ schwebende Def. (s.u.); externe Bindung */ schwebende Def. (s.o.); externe Bindung */ schwebende Def.; interne Bindung */ Widerspruch zu oben (interne/externe Bindung) Widerspruch zu oben (interne/externe Bindung) Programmieren in C Prof. Dr. Rudolf Berrendorf 162 Deklaration / Definition bei Funktionen Die Unterscheidung bei Funktionen ist einfach: 1. Folgt auf die schließende Klammer ein Semikolon, so ist dies eine Deklaration. 2. Folgt kein Semikolon, so ist dies eine Definition (und es muss ein { oder Typangaben zu Parametern folgen). Beispiel: int f1 ( int i, int j ) ; /* Deklaration */ int f2 ( int i, int j ) { return i+j; } /* Definition */ int f3 ( i, j ) int i,j; { return i-j; } /* Definition */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 163 Inline Funktionen • • • • • • Neu in C99 Voranstellen des Schlüsselworts inline in Funktionsdefinition und Funktionsdeklaration Hinweis an den Compiler, diese Funktion an Aufrufstellen zu expandieren (Optimierung). Eine Funktion hat eine Inline-Definition, wenn alle top-level Deklarationen der Funktion in der Übersetzungseinheit inline enthalten und kein extern vorliegt In genau einer Übersetzungseinheit muss aber eine Definition der Funktion mit extern erfolgen (falls der Compiler keine Inline-Expansion vornimmt) Typische Nutzung einer Inline-Definition in Headerdatei Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 164 Beispiel square.h: abstand.c: // inline Definition // und Deklaration für externe Funktion inline double square( double x) { return x * x; } #include <square.h> #include <math.h> square.c: #include <square.h> // externe Definition // durch extern wird inline in square.h // hier überschrieben extern double square( double x) { return x * x; } Fachbereich Angewandte Informatik // berechne Abstand zweier Koordinaten // Wurzel((x2-x1)^2 + (y2-y1)^2) double abstand( double x1, double y1, double x2, double y2) { // Compiler hat 2 Möglichkeiten: // 1) inline expandieren // 2) square explizit aufrufen return sqrt( square(x1-x2) + square(y2-y1)); } Programmieren in C Prof. Dr. Rudolf Berrendorf 165 Typnamen An manchen Stellen ist ein Typname verlangt, z.B. bei der Umwandlung eines Wertes von einen Typ in einen anderen (von int nach long). Ein Typname ist syntaktisch eine Deklaration ohne Bezeichner. Beispiel: int /* int */ int * /* Zeiger auf int */ int *[3] /* Feld mit 3 Zeigern auf int */ int (*const []) (unsigned int,...) /* Feld unbestimmter Dimension mit konstanten Zeigern auf Funktionen, die ein int-Resultat liefern und die einen ersten Parametern vom Typ unsigned int haben und eine unbestimmte Anzahl und Typen weiterer Parameter Wo würde der Bezeichner in einer Deklaration hingehören? */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 166 Initialisierungen In einer Variablendefinition kann man zusätzlich einen Initialisierungswert mit angeben, mit dem die Variable bei jeder Allokierung vorbesetzt wird. Der Wert wird jeweils neu berechnet. Syntax: Deklaration = Ausdruck Einschränkungen: 1. Initialisierungsangaben sind nicht erlaubt bei einer Variablendefinition in einem Block, wenn die Variable interne oder externe Bindung hat. 2. Nur konstante Ausdrücke (zur Übersetzungszeit kann der Wert ausgerechnet werden) für Objekte mit statischer Lebensdauer und wenn Typ ein Feld, Struktur oder Vereinigung ist. Beispiel: static int i = 5; static int *j = &i; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 167 Regeln zur Initialisierung: Klammerung Der Initialisierungsausdruck bzw. Teilausdrücke können bzw. müssen durch { } geklammert werden: 1. Skalare Ausdrücke können geklammert werden. Beispiel: ink k = { 5 * (j+1) }; 2. Ausdrücke für nicht-skalare Variablen (Feld, Struktur, Vereinigung) müssen geklammert werden. Beispiel: float Grenze[2] = { -1.0f, 1.0f }; 3. Ausnahme: String = Character-Felder Beispiel: char str[] = "Stringwert"; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 168 Regeln zur Initialisierung: Implizite Initialisierungen Objekte mit statischer Lebensdauer, die nicht explizit initialisiert werden, werden implizit vorbesetzt: 1. Objekte mit arithmetische Typ werden mit 0 (des entsprechenden Typs) vorbesetzt 2. Objektes eines Zeigertyps werden mit dem Nullzeiger vorbesetzt. Beispiel: static static static static int i; float f; double d; int *ptr_i; Fachbereich Angewandte Informatik /* /* /* /* bekommt bekommt bekommt bekommt Wert Wert Wert Wert Programmieren in C Prof. Dr. Rudolf Berrendorf 0 */ 0.0f */ 0.0 */ NULL */ 169 Regeln zur Initialisierung: Vereinigungstyp Bei Variablen eines Vereinigungstyps kann man nur Werte für die erste Komponente des Vereinigungstyps angeben. Beispiel: union { float x; int i; } uvar = 3.0; Fachbereich Angewandte Informatik /* Es ist nicht möglich, uvar.i zu initialisieren */ Programmieren in C Prof. Dr. Rudolf Berrendorf 170 Benannte Initialisierer • • • Neu in C99 Initialisierung bestimmter Teile eines Feldes, Struktur oder Union Syntax: – [expr]=val für Felder – .name=val für Strukturen und Unions Beispiel: // Feldelemente 1,2,7 werden explizit initialisiert (Rest implizit) int feld1[10] = { [1]=5, [7]=3, [2]=1}; // Feld hat 5 Elemente [1, 2, 5, 4, 5] int feld2[] = {1, 2, 3, [2]=5, 4, 5}; struct s { int i; float f; char s[4}; } s1 = { .s="str", .f=3.14}; Fachbereich Angewandte Informatik // Komponenten s und f explizit initialisiert Programmieren in C Prof. Dr. Rudolf Berrendorf 171 Regeln zur Initialisierung: Sonstiges 1. 2. 3. 4. 5. Felder ohne Dimensionsangabe aber mit Initialisierungsausdruck werden entsprechend der Anzahl der Werte im Initialisierungsausdruck dimensioniert. Es dürfen nicht mehr Werte angegeben werden, als zu initialisierende Objekte vorhanden sind (Beispiel: nicht mehr Teilausdrücke, als Feld Elemente hat). Es dürfen weniger Werte angegeben werden. Dann gelten die Regeln zur impliziten Initialisierung für die restlichen Elemente (mit 0 vorbesetzen). Die innersten Klammern bei Unterdeklarationen (z.B. 2-dim. Feld) können weggelassen werden. Regeln gelten rekursiv für Untertypen Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 172 Beispiele /* impliziert dimensioniertes Feld */ int x[] = { 3, 4, 5 }; int y[] = { {3}, {4}, {5} }; /* Strukturvariable */ struct { float re, im; } cmpl = { 2.0, 1.0 }; /* Geschachtelte Strukturen */ struct { int x[3]; struct {float re, im; } cmpl; } s[2] = { /* es folgt s[0] { { 1,2,3 }, { 2.0, 1.0} } , /* es folgt s[1] */ { { 4,5,6 }, { 4.0, 2.0 } } }; Fachbereich Angewandte Informatik /* 1. Komponente: x */ /* 2. Komponente: cmpl */ /* 1. Komponente: x */ /* 2. Komponente: cmpl */ /* 1. Komponente: x */ /* 2. Komponente: cmpl */ Programmieren in C Prof. Dr. Rudolf Berrendorf 173 Beispiele /* nicht-konstante Ausdrücke */ extern int getchar(void); int f(void) { int ch = getchar() + 'a'; } /* bei jedem Aufruf von f */ /* Zwei Felder der Dimension 4x3. Die ersten 3 Zeilen werden mit den vorgegebenen Zahlen explizit initialisiert, die letzte Zeile jeweils implizit mit Nullen besetzt. */ int x[4][3] = { {1,2,3}, {4,5,6}, {7,8,9} }; int y[4][3] = { 1,2,3,4,5,6,7,8,9 }; /* Das jeweils erste Element jeder Zeile wird explizit gesetzt, die anderen implizit mit Nullen besetzt. */ int z[4][3] = { {1}, {4}, {7}, {10} }; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 174 Beispiele /* äquivalent: */ char s[] = "abc"; char t[] = { 'a', 'b', 'c', '\0' }; /* Hier wird ein char-Zeiger initialisiert. Der String "abc" kann nicht (!) verändert werden. *p = 'd' ist nicht zulässig! char *p = "abc"; /* Rückgriff auf schon definierte Objekte */ static short sh, *ptr_sh = &sh; static double feld[100]; double *ptr_f = feld; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 175 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 176 Ausdrücke Ausdrücke berechnen einen Wert. Beispiel: 3+4 Ein l-Wert (links-Wert) ist ein Ausdruck, der auf ein Objekt im Speicher verweist (z.B. ein Zeigerwert, ein Variablenname). Als Faustregel gilt, dass alles, was auf der linken Seite einer Zuweisung stehen kann, ein l-Wert ist. Beispiel: int a, b; a = b; a = (3+4); Fachbereich Angewandte Informatik /* b ist in diesem Ausdruck ein l-Wert */ /* (3+4) ist kein l-Wert */ Programmieren in C Prof. Dr. Rudolf Berrendorf 177 Einfacher Ausdruck Bezeichnung Wert Konstante Wert der Konstanten (Bei String-Konst. Zeiger auf 1. Zeichen) Variablenname (arithm.Typ, Zeigertyp, enum, struct/union) Wert der Variablen Variablenname eines Feldtyps Zeiger auf 1. Element (außer bei sizeof; s.u.) Funktionsname Zeiger auf Funktionscode (außer bei sizeof) ( expression ) Klammerung Ausdruck (Auswertungsreihenfolge später) Beispiel: int i,j; char *cptr, cfeld[10]; int fn( int (*fun)(void)); int fn1(void); j = 1; cptr = "abc"; i = j; cptr = cfeld; i = fn( fn1 ); Fachbereich Angewandte Informatik /* /* /* /* /* Konstante 1 */ String-Konstante: Zeiger auf 1. Zeichen */ Variable arithmetischen Typs */ Feldname: Zeiger auf 1. Element */ Funktionsname fn1: Zeiger auf Code */ Programmieren in C Prof. Dr. Rudolf Berrendorf 178 Einfacher Ausdruck Bezeichnung Wert x [ expr ] Auswahl Feldkomponente (x Feld- oder Zeigervariable; expr int-Wert) x.name Komponente name der Struktur/Union x x -> name Komponente name einer Struktur, auf die x (Zeigerwert) zeigt. &x Adresse von x. x muss l-Wert sein. Kein Bit-Feld, keine Variable der Speicherklasse register. *x Der Wert, worauf x zeigt. x muss Zeigertyp besitzen. Beispiel: int i, a[10]; struct {int ival; } str, *ptr_str; i = a[3]; // Zugriff auf 3.Komponente des Feldes str.ival = str.ival + 1; // Zugriff auf Komponente ival der Struktur ptr_str = &str; // ptr_str zeigt nun auf str (*ptr_str).ival = (*ptr_str).ival + 1; // Komponente ival inkrementieren str.ival = str.ival + ptr_str->ival; // ptr_str->ival entspricht (*ptr_str).ival Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 179 Arbeiten mit Zeigern // Wichtig in den Beispielen: 0 entspricht false // kopiere einen String von source nach dest // (genügend Speicherplatz in dest vorausgesetzt) char *my_strcpy(char *dest, *char *source) { char *save = dest; // Anfang des Resultatstrings merken // Strings werden mit '\0' abgeschlosssen while(*dest++ = *source++) ; // in letzter Zeile schon alles gemacht return save; // Anfang des Resultatstrings als Ergebnis } // finde das Ende eines Strings char *ende_string(char *str) { // Strings werden durch '\0' abgeschlossen while (*p++) ; return p; // gebe Position (=Zeiger) zurück } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 180 Einfacher Ausdruck Bezeichnung Wert (Typname) x Wert x wird (falls möglich) in einen Wert des Typs Typname umgewandelt (cast-Operation) (Typname) { init-list } Cast von einem anonymen Objekt eines zusammengesetzten Typs (C99) x=y y wird an x (l-Wert) zugewiesen. Ergebnis des Gesamtausdrucks ist Wert von y. Bei Überlappung der Speicherbereiche nicht definiert. sizeof expr sizeof(Typname) Größe von expr bzw. Typname in Bytes. Zur Übersetzungszeit berechnet, also keine Seiteneffekte zur Laufzeit (Bsp.: sizeof ++i). Feldname: gesamtes Feld und nicht Zeigerwert Nicht erlaubt: Funktion, Bit-Feld, void, Feld ohne expl. Längenangabe x (Argumentliste) Funktionsaufruf der Funktion x mit aktuellen Argumenten. Argumentwerte werden ausgerechnet und auf Laufzeitstack abgelegt (call-by-value). Beachte: Die Zuweisung ist in C eine Operation und keine Anweisung! Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 181 Einfacher Ausdruck Beispiel: int i, j, k; i k j j i = = = = = (int) 3.0; (j = i); sizeof i; sizeof(int); fun1( i, i+1, fun2(j)); i = 5; j = 3; if( i = j ) ++i; Fachbereich Angewandte Informatik /* /* /* /* /* Typ cast */ Zuweisung j = i ist Ausdruck */ Größe in Bytes von i */ Größe in Bytes vom Typ int */ 3 Argumente werden vorher ausgewertet */ /* Häufigster Fehler! Compiler-Option -Wall! */ Programmieren in C Prof. Dr. Rudolf Berrendorf 182 Anonyme Objekte // Berechnung von Zweierpotenzen für 0<=n<=7 mit table-lookup // über Typcast eines anonymen Objekts eines zusammengesetzten Typs (hier Feld) inline int POW2(int n) { assert((n >= 0) && (n <= 7)); // nur für 0 <= n <= 7 erlaubt // Typcast eines Initialisierungsausdrucks und Indizierung dieser Konstanten return (const int[]) {1,2,4,8,16,32,64,128} [n]; } struct point { int x; int y; }; void drawPoint(struct point p); void drawLine(struct point *from, struct point *to); void meineGrafik(int n) { // Arbeiten mit anonymen Objekten drawPoint((struct point) {.x=n, .y=2*n}); drawLine(&(struct point){.x=n, .y=n}, &(struct point){.x=-n, .y=-n}); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 183 Inkrement- / Dekrement-Ausdruck Bezeichnung Wert ++x Wert von x (l-Wert) wird inkrementiert und wieder in x gespeichert, Der Wert des Ausdrucks ist der neue Wert von x. --x Wert von x (l-Wert) wird dekrementiert und wieder in x gespeichert, Der Wert des Ausdrucks ist der neue Wert von x. x++ Wert von x (l-Wert) wird inkrementiert und wieder in x gespeichert, Der Wert des Ausdrucks ist der alte Wert von x. x-- Wert von x (l-Wert) wird dekrementiert und wieder in x gespeichert, Der Wert des Ausdrucks ist der alte Wert von x. Beispiel: int i, j; i j i j = = = = 1; ++i + 1; 1; i++ + 1; /* i=2, j=3 */ /* i=2, j=2 */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 184 Arithmetischer Ausdruck Bezeichnung Wert +x Unäres Plus -x Unäres Minus x+y Addition x-y Subtraktion x*y Multiplikation x/y Division. Für Ausdruck mit integralem Typ ganzzahlige Division. x%y Modulo-Bildung (nur integrale Typen). Beispiel: float x; int i; x = 5.0 + 2.0 * 12.0; i = 9 / 4; i = 9 % 4; Fachbereich Angewandte Informatik /* Wert 29 */ /* Wert 2 */ /* Wert 1 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 185 Anmerkungen 1. 2. 3. Typanpassungen findet evtl. vor Anwendung der Operation statt (später) Under-/Overflow bei Operationen mit signed Argumenten: Ergebnis nicht definiert (Fehler, Modulo-Arithmetik usw. möglich) Under-/Overflow bei Operationen mit unsigned Argumenten: Modulo-Arithmetik Beispiel: Beispiel: #include <limits.h> ui 1111 = 15 uj +1111 = 15 -------------1110 = 30%16 = 14 int i, j; unsigned int ui, uj; i = j = ui = uj i = i + ui = ui INT_MAX; = UNINT_MAX; j; + uj; Fachbereich Angewandte Informatik /* nicht definiert */ /* definiert */ Programmieren in C Prof. Dr. Rudolf Berrendorf 186 Zeigerarithmetik Mit Zeigerwerten ist ebenfalls (eingeschränkt) Addition und Subtraktion möglich. Gerechnet wird nicht in Bytes, sondern in Einheiten des Basistyps, d.h. ptr+1 entspricht dem Wert von ptr (1000) + so viele Bytes, wie der Grundtyp (z.B. int=4) beansprucht (1004). Einschränkungen: 1. Addition: Nur ein Operand darf Zeiger sein, der andere muss int sein. Das Resultat ist ein Zeiger. 2. Subtraktion: Zwei Zeigeroperanden liefern ein int-Resultat, linker Operand ein Zeiger, rechter Operand ein int liefert Zeiger-Resultat. Beispiel: int i, *ptr1, ptr2; /* Annahme: i liegt auf Adresse 1000, Größe eines ints=4 Bytes */ ptr1 = &i; ptr2 = ptr1 + 1; i = ptr1 - ptr2; ptr2 = ptr1 - 1; /* /* /* /* Fachbereich Angewandte Informatik ptr1 = ptr2 = i = -1 ptr2 = 1000 */ 1004 */ */ 996 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 187 Relationaler und logischer Ausdruck Bezeichnung Wert x<y kleiner x>y größer x <= y kleiner gleich x >= y größer gleich x == y gleich x != y ungleich x && y logisches Und x || y logisches Oder !x logische Negation • • • • Wahrheitswerte sind vom Typ int: 0=falsch, ungleich 0=wahr || und && werten zweites Argument nur aus, wenn nötig! Entweder haben beide Operanden einen arithmetischen Typ oder beide sind Zeiger auf Objekte kompatiblen Typs. Evtl. Typanpassungen der Operanden Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 188 Beispiel int i, a[2], *ptr1, *ptr2; i = (3 < 4) || f(10000); j = (3 < 4) && f(10000); /* f(10000) wird nicht ausgewertet! */ /* f(10000) wird ausgewertet! */ ptr1 = &a[0]; ptr2 = &a[1]; i = ptr1 < ptr2; /* liefert wahr */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 189 Bit-Ausdruck Bezeichnung Wert x << y Links-Shift von x um y Stellen x >> y Rechts-Shift von x um y Stellen x&y Bit-weises Und x|y Bit-weises Oder x ^y Bit-weises Xor (1, wenn beide Bits verschieden) ~x Bit-weise Negation Für Shift-Operationen gilt: • • • Rechter Operand > 0 und < Anzahl Bits des linken Operanden Es werden Null-Bits nachgeschoben Falls linker Operand signed-Typ hat und negativer Wert vorliegt, so ist das Ergebnis undefiniert. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 190 Beispiel unsigned int i; i i i i i i i = = = = = = = 1; i << 3; i >> 2; i | 5; i & 3; i ^ 5; i & ~i; /* /* /* /* /* /* /* Fachbereich Angewandte Informatik i i i i i i i hat hat hat hat hat hat hat Wert Wert Wert Wert Wert Wert Wert 1 */ 10002 00102 01112 00112 01102 00002 = = = = = = 8 */ 2 */ 7: 00102 3: 01112 6: 00112 0: 01102 Programmieren in C Prof. Dr. Rudolf Berrendorf | 01012 & 00112 ^ 01012 &~01102 = = = = 01112 00112 01102 01102 */ */ */ & 10012 = 00002 */ 191 Kurzformen von Zuweisungen Bezeichnung Kurzform für x += y x=x+y x -= y x=x-y x *= y x=x*y x /= y x=x/y x %= y x=x%y x <<= y x = x << y x >>= y x = x >> y x &= y x=x&y x |= y x=x|y x ^= y x = x ^y Die Kurzformen sind in C ebenfalls Operationen und keine Anweisungen! Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 192 Sonstige Ausdrücke Bezeichnung Wert ( e0 ) ? e1 : e2 Der Ausdruck e0 wird ausgewertet. Falls dieser wahr (!=0) ist, so wird e1 ausgewertet, ansonsten e2. Der Wert des Gesamtausdrucks ist der Wert von e1 bzw. e2. Beachte: Entweder e1 oder e2 wird nur ausgewertet. e1 , e2 Zuerst wird e1 ausgewertet, das Ergebnis ignoriert und anschließend e2 ausgewertet. Der Typ und das Ergebnis des Gesamtausdrucks ist der Typ bzw. das Ergebnis von e2. Beispiel: int i, i, max; k = i = (j = 1) + 1 , 2*j; max = (i > j) ? i : j; Fachbereich Angewandte Informatik /* i=2, j=1, k=2 */ /* max = (2 > 1) ? 2 : 1 = 2 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 193 Konstante Ausdrücke An manchen Stellen sind konstante Ausdrücke erforderlich: 1. Testausdruck in #if-Präprozessordirektiven 2. Dimensionsangaben bei Feldern 3. case-Label in switch-Anweisungen 4. Länge von Bit-Feldern 5. Explizite Werte für Aufzählungselemente 6. Initialisierungswerte für static- und extern-Variablen Bildung von konstanten Ausdrücken: 1. Konstanten eines integralen Typs 2. Klammern () 3. Unäre Operatoren: + - ~ ! sizeof 4. Binäre Operatoren: + - * / % << >> == != < <= > >= & ^ | && || 5. Ternäre Operatoren: ?: Der sizeof-Operator kann nicht in Präprozessor-Direktiven verwendet werden. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 194 Priorität der Operatoren Operator Assoziativität f() a[] -> . links -> rechts x++ x-- links -> rechts ++x --x ~ ! +x -x * & (type) sizeof rechts -> links * / % links -> rechts + - links -> rechts << >> links -> rechts < <= > >= links -> rechts == != links -> rechts & links -> rechts ^ links -> rechts | links -> rechts && links -> rechts || links -> rechts ?: rechts -> links = += -= *= /= %= &= ^= |= <<= >>= rechts -> links , links -> rechts Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 195 Reihenfolge der Auswertung Die Reihenfolge der Auswertung eines Ausdrucks ist in C nur in wenigen Fällen vorgegeben: 1. 2. 3. 4. f(args) && und || ?: , Ansonsten ist der Compiler frei, in welcher Reihenfolge er Teilausdrücke auswertet. Beispiel: int a,b,c,d; a = (a + b) + (c + d); /* Trotz Klammerung dürfte der C-Compiler dies auswerten als: a = (a + d) + (b + c); */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 196 Typumwandlungen In manchen Fällen ist ein Umwandlung von einem Typ in einen anderen Typ nötig, was explizit oder implizit geschehen kann: 1. 2. 3. 4. 5. Explizite Umwandlung in cast-Ausdruck. Beispiel: i = 3 + (int) 4.0; Implizite Umwandlung eines Operanden. Beispiel: i = 3 + 4.0; Implizite Umwandlung bei einer Zuweisung Beispiel: int i = 4.0; Aktuelles Argument in Funktionsaufruf wird auf den Typ des formalen Parameters angepasst. Beispiel: int fun(int arg); i = fun(4.0); Ergebniswert einer Funktion wird implizit auf den Ergebnistypen umgewandelt. Beispiel: int fun(int arg) { return 4.0; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 197 Prinzipielle Möglichkeiten der Typumwandlung ↓von →nach void int float Zeiger ok void ok Integer ok ok ok Fließkomma ok ok ok Zeiger ok ok Feld ok Struktur ok Funktion ok ok Feld (ok) Struktur Funktion (ok) (ok) bedeutet implizite Umwandlung. Beispiel: Der Wert eines Feldnamens in einem Ausdruck ist der Zeiger auf das erste Element des Feldes. Bei Zeigertypen muss zwischen Zeigertypen auf Objekte und Zeigertypen auf Funktionen unterschieden werden. Ein Objektzeigertyp und ein Funktionszeigertyp sind nicht in den anderen Typ umwandelbar. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 198 Umwandlung nach void Jeder Wert kann in den Typ void umgewandelt werden. Der Effekt ist, dass damit der Wert verschwindet. Eine sinnvolle Anwendung ist der Fall, wo man explizit darauf hinweisen möchte, dass man den Wert ignorieren möchte. Beispiel: int printf(char *format, ...); /* Die printf-Funktion liefert als Ergebnis die Anzahl der ausgegebenen Zeichen. */ (void) printf("hallo\n"); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 199 Umwandlung nach Integertyp 1. Von int-Typ: Falls der Wert im neuen Typ darstellbar ist, dann umwandeln. Ansonsten, falls der Ergebnistyp ein unsigned int ist, so schneide obersten Bits ab (Modulo-Bildung). Ansonsten ist das Ergebnis undefiniert. 2. Von Fließkommatyp: Der ganzzahlige Teil des Wertes wird umgewandelt. Falls dieser Wert nicht in dem int-Typ darstellbar ist, ist die Umwandlung undefiniert. 3. Von Zeigertyp: Der Zeigerwert wird als unsigned int mit der gleichen Größe wie die des Zeigers angesehen. Darauf werden obige Regeln angewendet. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 200 Beispiel unsigned int ui; int i; signed int si; int *ptr; i = 5l; ui = ULONG_MAX; /* (long int)5 ist in int darstellbar, also 5 */ /* ULONG_MAX ist evtl. nicht in unsigned int darstellbar. Obersten Bits werden dann abgeschnitten. */ si = ULONG_MAX; /* undefiniert */ i = 3.5; /* 3.5 wird nach (int)3 umgewandelt */ ptr = &i; i = (int)ptr; /* z.B. Adresse 1000 */ /* i erhält den Wert (int)1000 */ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 201 Umwandlung nach Fließkommatyp 1. Von int-Typ: Eine entsprechende Näherung wird als Wert genommen. 2. Von Fließkommatyp: Von float nach double: Ist möglich. Von double nach float: Falls der Wert in float darstellbar ist, wird dieser Wert genommen (gerundet oder abgeschnitten ist impl.abhängig). Ansonsten undefiniert. Beispiel: float f; double d; d = 5; d = 5.0f; f = 5.0; Fachbereich Angewandte Informatik /* Umwandlung nach 5.0 */ /* Umwandlung nach (double)5.0 */ /* Umwandlung nach (float)5.0 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 202 Umwandlung nach Zeigertyp 1. Von Zeigertyp: Zeigerwert bleibt erhalten im neuen Zeigertyp. 2. Von Integertyp: Integerwert wird als Adresse interpretiert. Nicht portabel! Ausnahme: 0 (NULL). 3. Von Feldtyp: Der Feldname wird als Adresse des ersten Elementes genommen (Ausnahme: sizeof) Beispiel: float f, *fptr; int i, *iptr, ifeld[10] fptr = &f; iptr = (int *)fptr; i = 0x0005; iptr = (int *)i; iptr = ifeld; Fachbereich Angewandte Informatik /* /* /* /* /* z.B. Adresse 1000 */ iptr bekommt 1000 zugewiesen */ An Adresse 5 liegt z.B. ein HW Control-Port */ *iptr würde den Inhalt der Adresse 5 ergeben. */ iptr hat &ifeld[0] */ Programmieren in C Prof. Dr. Rudolf Berrendorf 203 "Übliche" Umwandlungen Bis jetzt haben wir nur prinzipiell besprochen, welche Umwandlungen möglich sind und was für Auswirkungen diese Umwandlungen haben. Wann finden die Umwandlungen statt? 1. 2. 3. Explizite Umwandlung (cast) Umwandlung in Zuweisungen Umwandlung in Ausdrücken Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 204 Umwandlungen in Casts Über Casts lässt sich explizit eine Typumwandlung erzwingen. Die erlaubten Umwandlungen in Casts sind: Cast-Typ Ursprungstyp Beliebiger arithmetischer Typ Beliebiger arithmetischer Typ beliebiger int-Typ beliebiger Zeigertyp void *, Zeiger auf T beliebiger int-Typ, void *, beliebiger Zeigertyp void beliebig Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 205 Umwandlungen in Zuweisungen Stimmt der Typ auf der linken Seite der Zuweisung nicht mit dem Typ auf der rechten Seite überein, wir der Wert rechts auf den Typ der linken Seite angepasst. Erlaubte ist dabei: Linke Seite Rechte Seite Beliebiger arithmetischer Typ Beliebiger arithmetischer Typ Struktur-/Union-Typ kompatibler Struktur-/Union-Typ void * 0, void *, Zeiger Zeiger auf Typ T1 0, void *, Zeiger auf T2 (wobei T1 und T2 kompatibel sein müssen) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 206 Beispiel int i, *ptr_i, feld_i[2]; double d; short int si; void *ptr_v; i = 3.0; d = 3; si = 3; /* implizite Umwandlung (double)3.0 -> (int)3 */ /* implizite Umwandlung (int)3 -> (double)3.0 */ /* implizite Umwandlung (int)3 -> (short)3 */ ptr_i = 0; ptr_v = ptr_i; ptr_i = ptr_v; /* 0 kann man Variablen beliebigen Zeigertyps zuweisen */ /* möglich, weil ptr_v Typ void * hat */ /* möglich, weil ptr_v Typ void * hat */ ptr_i = feld_i; /* Adresse des ersten Elementes */ /* Falsch: */ const int *ptr_ci; int *ptr_i; ptr_i = ptr_ci; /* Falsch: Nicht-kompatible Typen, keiner davon void * Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf */ 207 Unäre (einstellige) Umwandlungen Die unären Umwandlungen werden angewandt auf Operanden folgender Operationen: 1. 2. 3. Funktionsargumente zu Funktionen, zu denen kein Funktionsprototyp bekannt ist Unäre Operatoren: ! - ~ * Binäre Operatoren: << >> Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 208 Unäre (einstellige) Umwandlungen Integertypen ist ein Rang zugeordnet, der in den Umwandlungsregeln eine Rolle spielt. Rang Typen 60 long long int, unsigned long long int 50 long int, unsigned long int 40 int, unsigned int 30 short, unsigned short 20 char, unsigned char, signed char 10 _Bool Die extended Integertypen müssen implementierungsabhängig in dieser Tabelle eingeordnet sein. Beispiel: In einer Implementierung könnte int64_t den Rang 60, in einer anderen Implementierung den Rang 55 haben. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 209 Unäre (einstellige) Umwandlungen Die angewandte unäre Umwandlung ist die erste Regel der folgenden Tabelle, die anwendbar ist. Operandentyp Zieltyp Fließkommatyp keine Umwandlung Feld von T Zeiger auf T Funktion, die T liefert Zeiger auf Funktion, die T liefert int-Typ mit Rang >= int keine Umwandlung signed int Typ mit Rang < int int unsigned int Typ mit Rang < int und alle Werte dieses Typs sind darstellbar in int int unsigned int Typ mit Rang < int und nicht alle Werte dieses Typs sind darstellbar in int unsigned int Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 210 Beispiel int i; char c; short s1=1, s2=4; c = 'a'; i = -c; i = s1 << s2; Fachbereich Angewandte Informatik /* Umwandlung nach (int)c und dann unäres Minus */ /* Umwandlung nach (int)s1 << (int)s2 */ Programmieren in C Prof. Dr. Rudolf Berrendorf 211 Binäre Umwandlungen Anwendung bei den meisten binären Operatoren und auf das 2. Und 3. Argument des ?:-Operators. Umwandlung durch die erste anwendbare der folgenden Regeln: 1. Ist ein Operand long double: Umwandlung nach long double 2. Ist ein Operand double: Umwandlung nach double 3. Ist ein Operand float: Umwandlung nach float 4. Anwenden der unären Umwandlungen auf beide Operanden und anschließend höchstens eine der folgenden Regeln: - Ist ein Operand unsigned long int: Umwandlung nach unsigned long int - Ist ein Operand long int und der anderen unsigned int: - Sind alle unsigned int Werte in long darstellbar, dann long - Ansonsten unsigned long int - Ist ein Operand long int: Umwandlung nach long int - Ist ein Operand unsigned int: Umwandlung nach unsigned int Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 212 Binäre Umwandlungen Anwendung bei den meisten binären Operatoren und auf das 2. Und 3. Argument des ?:-Operators. Zuerst wird auf beiden Operanden getrennt eine unäre Umwandlung durchgeführt. Dann wird die erste der folgenden Regeln angewandt: Ein Operandentyp Andere Operandentyp Umwandlung nach long double Reeller Typ long double double reeller Typ double float reeller Typ float unsigned Typ unsigned Typ unsigned Typ mit höherem Rang signed Typ signed Typ signed Typ mit höherem Rang unsigned Typ signed Typ mit <= Rang unsigned Typ unsigned Typ signed Typ mit > Rang, kann alle Werte des unsigned darstellen signed Typ unsigned Typ signed Typ mit > Rang, kann nicht alle Werte des unsigned darstellen unsigned Version von signed Typ beliebiger Typ beliebiger Typ keine Umwandlung Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 213 Beispiel /* unäre Umwandlungen */ /* binäre Umwandlungen */ int i; short s = 1; long int il; unsigned long int iul; float f; f = 1.0 + 1.0ld; f = 1 + 1.0f; /* f = (float) ( (long double)1.0 + 1.0ld ) */ /* f = (float)1 + 1.0f */ i = s + 1; i = s + 1u; /* i = (int)s + 1 */ /* i = (int) ( (unsigned int)(int)s + 1u Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf ) */ 214 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 215 Ein/Ausgabe Dateien sind eine Folge von Bytes ohne jegliche Struktur. Lediglich Textdateien (s.u.) haben eine Zeilenstruktur dadurch, dass dem Zeichen '\n' eine besondere Bedeutung zukommt. Um mit einer Datei zu arbeiten braucht man einen Dateibeschreiber vom Typ FILE *, der in <stdio.h> definiert ist. Solch einen Beschreiber bekommt man beim Öffnen einer Datei. Es gibt drei bereits vorhandene und geöffnete Dateien (<stdio.h>): 1. stdin Standardeingabe 2. stdout Standardausgabe 3. stderr Standardfehlerausgabe Eine Datei kann in einer von zwei Modi geöffnet werden: 1. Formatierte Ein-/Ausgabe 2. Unformatierte Ein-/Ausgabe Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 216 Öffnen einer Datei FILE *fopen(const char *filename, const char *mode); filename ist der Name der zu öffnenden Datei in der Notation des jeweiligen Systems (Unix: "/tmp/datei.dat", Windows: "C:\\tmp\\datei.dat"). mode ist der Öffnungsmodus mit einer Kombination der Buchstaben (s.u.): r Von Datei nur lesen w Auf Datei nur schreiben a Auf Datei nur schreibend anfügen b Binärdatei (Default: Textdatei) + Sowohl schreiben als auch lesen möglich (erste Operation entscheidet) fopen liefert einen Dateibeschreiber vom Typ FILE * zurück. Falls ein Fehler aufgetreten ist, ist dies NULL. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 217 Öffnungsmodus "r" Existierende Textdatei zum Lesen öffnen. "w" Textdatei zum Schreiben öffnen. Falls Datei existiert, wird Inhalt gelöscht. "a" Textdatei zum Anfügen öffnen. Falls keine Datei existiert, erzeugen. "rb" Binärdatei; s.o. "wb" Binärdatei; s.o. "ab" Binärdatei; s.o. "r+" Existierende Textdatei zum Lesen oder Schreiben öffnen. "w+" Textdatei zum Lesen oder Schreiben öffnen. Falls Datei existiert, wird Inhalt gelöscht. "a+" Textdatei zum Lesen oder Anfügen öffnen. "rb+" Binärdatei; s.o. "wb+" Binärdatei; s.o. "ab+" Binärdatei; s.o. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 218 Öffnungsmodus Eigenschaft Modus r w a r+ w+ a+ ja nein nein ja nein nein nein ja nein nein ja nein ja nein nein ja ja ja Schreiben erlaubt? nein ja ja ja ja ja Schreiben beginnt am Ende nein nein ja nein nein ja Datei muss existieren? Inhalt einer existierenden Datei verloren? Lesen erlaubt? Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 219 Weitere Dateioperationen int fclose(FILE *f); Schließt die Datei. Liefert 0, falls kein Fehler auftrat. int fflush(FILE *f); Synchronisiert interne Puffer mit der Datei. int remove(const char *filename); Löscht die Datei mit dem Namen filename. Liefert 0, wenn ok. int rename(const char *old, const char *new); Benennt Datei old in new um. Liefert 0, wenn ok. FILE *tmpfile(void); Erzeugt temporäre Datei im Modus "wb+". Datei wird automatisch gelöscht, wenn Datei geschlossen oder Programm beendet wird. Liefert 0, wenn nicht ok. char *tmpname(char *s); Erzeugt nicht-existierenden Dateinamen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 220 Beispiel #include <stdio.h> int main(int argc, char **argv) { FILE *f; /* Öffnen der Datei */ f = fopen(argv[1], "w"); if(f == NULL) exit(EXIT_FAILURE); /* Schreiben */ fprintf(f, "Hallo\n"); printf("Hallo"); /* Rausschreiben des Schreibpuffers für stdout */ fflush(stdout); /* Datei schließen */ if( fclose(f) != 0) exit(EXIT_FAILURE); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 221 Formatierte Ausgabe int fprintf(FILE *f, char *format, ...); int printf(char *format, ...); int sprintf(char *str, char *format, ...); Schreibt eine Anzahl von Werten auf die Datei f bzw. stdout bzw. str. Die Anzahl wird implizit durch den Formatstring vorgegeben. Liefert Anzahl geschriebener Zeichen, falls ok, ansonsten einen negativen Wert. Der Formatstring wird zeichenweise ausgegeben, bis ein % erscheint, was eine Formatangabe einleitet. Für jede Formatangabe wird eine Werteangabe in ... benötigt. Eine Formatangabe besteht aus % gefolgt von optionalen Zusatzangaben Flags Minimale Feldweite Präzision Zusatzangaben zu short/long gefolgt von genau einem Formatbuchstaben. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 222 Formatbuchstaben Buchstabe Argumenttyp Ausgabe d, i int Dezimalzahl x,X unsigned int Hexadezimalzahl o unsigned int Oktalzahl u unsigned int Dezimalzahl (ohne Vorzeichen) c int Zeichen s char * String f double Reelle Zahl im Format [-]m.dddddd e,E double Reelle Zahl im Format [-]m.dddddde[+|-]dd g,G double Wie %e, falls Exponent klein, sonst %f. a, A double Reelle Zahl in hexadezimaler Notation p void * Zeigerwert als (impl.abhängige) Dezimalzahl % - %-Zeichen n int * Keine Ausgabe! Nächstem Argument wird Anzahl der bis jetzt ausgegebenen Zeichen zugewiesen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 223 Beispiel #include <stdio.h> int main(int argc, char **argv) { int i; printf("%d %x %c %s\n", 4711, 17u, 'a', "hallo"); printf("%f %p %n\n", 3.14, &i, &i); } Ausgabe: 4711 11 a hallo 3.140000 0xbffffa64 Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 224 Optionale Zusatzangaben 1) Flags (Auswahl): linksbündige Ausgabe + Zahlendarstellung beginnt immer mit Vorzeichen Leerzeichen Zahlendarstellung beginnt mit Leer- oder Minuszeichen 0 0 statt Leerzeichen als Füllzeichen 2) Minimale Feldweite in Form einer Dezimalzahl Mit Füllzeichen bis zur Feldweite auffüllen 3) Präzision in der Form .Dezimalzahl Bei Fließkommaformaten: Nachkommastellen Bei int: minimale Anzahl von Ziffern Bei char *: maximale Feldweite 4) Typangaben (Auswahl): ll long long bei int-Formaten erwartet h/l short bzw. long Argument werden bei int-Formaten erwartet L long double Argument bei Fließkomma-Formaten erwartet Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 225 Beispiel #include <stdio.h> int main(int argc, char **argv) { printf("%5.3f\n", 3.141592654); printf("%10d\n", 1); printf("%-10d\n", 1); } Ausgabe: 3.141 1 1 Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 226 Formatierte Eingabe int fscanf(FILE *f, char *format, ...); int scanf(char *format, ...); Int sscanf(char *str, char *format, ...); Liest eine Anzahl von Werten von der Datei f bzw. stdout bzw. str. Die Anzahl wird implizit durch den Formatstring vorgegeben. Liefert Anzahl gelesener Werte (nicht Zeichen), falls ok, ansonsten EOF. Der Formatstring wird zeichenweise durchgearbeitet. Leerzeichen werden im Format wie in der Eingabe ignoriert. Zeichen in der Formatangabe ungleich % müssen auch in der Eingabe entsprechende Zeichen haben. Ein %-Zeichen leitet eine Formatangabe ein. Für jede Formatangabe wird ein entsprechender Zeiger (!) in ... benötigt. Eine Formatangabe besteht aus % gefolgt von optionalen Zusatzangaben Maximale Feldweite (Integer-Zahl) Zusatzangaben wie z.B. short/long (h,l,L) gefolgt von genau einem Formatbuchstaben. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 227 Formatbuchstaben Buchstabe Argumenttyp Eingabe d int * Dezimalzahl i int * Dezimal-, Oktal oder Hexadezimalzahl o int * Oktalzahl x,X int * Hexadezimalzahl u unsigned int * Dezimalzahl ohne Vorzeichen c char * Ein Zeichen (vorangehende Leerzeichen ignorieren) s char * String (vorangehende Leerzeichen ignorieren). String ist bei Leerzeichen beendet. e,f,g float * Fließkommazahl (Vorsicht:: float * und nicht double *) % - % selber p void ** Implementierungsabhängige Zeigerdarstellung [ char * Angabe einer Menge von erlaubten Eingabezeichen Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 228 Beispiel 1 #include <stdio,h> int main(int argc ,char **argv) { int i; float f; char str[10]; scanf("%d %f", &i, &f); printf("%d %f\n", i, f); scanf("%s", str); printf("%s\n", str); } Eingabe: 3 -3.14 abc def Ausgabe: 3 -3.14 abc Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 229 Beispiel 2 #include <stdio,h> int main(int argc ,char **argv) { char str[10]; // Lese max. 9 Ziffern ein, '\0' wird angehängt scanf("%9[0123456789]", str); // Lese alles außer Ziffern ein, max 9 Zeichen, '\0' wird angehängt // ^ bedeutet Negation scanf("%9[^0123456789]", str); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 230 Weitere Funktionen int fgetc(FILE *f); int fputc(int c, FILE *f); Liest bzw. schreibt nächstes Zeichen (EOF, wenn Fehler). char *fgets(char *s, int n, FILE *stream); Höchstens n-1 Zeichen werden aus einer Zeile eingelesen und mit \0 abgeschlossen char *fputs(const char *s, FILE *f); Schreibt s auf Datei. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 231 Unformatierte Ein-Ausgabe size_t fread(void *ptr, size_t size, size_t nmemb, FILE *f); Liest nmemb Elemente jeweils der Größe size aus der Datei und speichert diese ab ptr im Speicher. size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *f); Schreibt nmemb Elemente jeweils der Größe size aus dem Speicher ab ptr auf die Datei. int fseek(FILE *f, long int offset, int whence); Positioniert den internen Dateizeiger auf die Position, die offset addiert mit whence ergibt, wobei whence sein kann: SEEK_SET: Dateianfang SEEK_CUR: aktuelle Position in Datei SEEK_END: Dateiende void rewind(FILE *f); Setzt den Dateizeiger auf den Anfang der Datei. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 232 Beispiel /* ohne Fehlerüberprüfungen! */ #include <stdio.h> int main(int argc, char **argv) { FILE *f; int ifeld[10] = {,0,1,2,3,4,5,6,7,8,9}; /* Schreiben des gesamten Feldes auf Datei */ f = fopen("test.dat", "wb"); fwrite(ifeld, sizeof(ifeld[0]), 10, f); fclose(f); /* Lesen des gesamten Feldes von Datei */ f = fopen("test.dat", "rb"); fread(ifeld, sizeof(ifeld[0]), 10, f); fclose(f); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 233 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 234 Standardbibliotheken In der Bibliothek libc.a (automatisch eingebunden) und libm.a sind alle ANSI-C Funktionen enthalten. Wenn man eine Funktion nutzen will, sollte man die entsprechende Include-Datei xyz.h einbinden. Hier werden nur die wichtigsten Funktionen besprochen. Online-Dokumentation über "man abc" für Funktion abc. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 235 Headerdateien (1) Headername Funktion assert.h Zusicherungen complex.h Komplexe Zahlen ctype.h Zeichenklassen (islower, isupper etc.) errno.h letzte Fehlernummer (in errno) fenv.h low-level Kontrolle über Fließkommaverhalten (z.B. Rundung) float.h Angaben zu Fließkommawerten (z.B. FLT_MAX) inttypes.h Ausgabeformatierung von extended int-Typen iso646.h Makros für einige Operatoren (z.B. not_eq für !=) limits.h Angaben zu int-artigen Werten (z.B. INT_MAX) locale.h Lokalisierung (z.B. Währungszeichen) math.h Mathematikfunktionen (z.B. sin, cos) setjmp.h Nichtlokale Sprünge Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 236 Headerdateien (2) Headername Funktion signal.h Signale stdarg.h Variable Argumentlisten stdbool.h Boolsche Werte stddef.h Grundlegende Typen und Konstanten (z.B. NULL, size_t) stdint.h extended int Typen stdio.h Ein-/Ausgabe stdlib.h Nützliche Funktionen (z.B. malloc, qsort, system, getenv) string.h String-Funktionen tgmath.h Typ-generische mathematische Makros (sin für float, double,...) time.h Zeit- und Datumsfunktionen wchar.h Wide und Multibyte Character wctype.h Wide und Multibyte Character Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 237 Zusicherungen mit <assert.h> Beispiel: #include <assert.h> #include <stdlib.h> /* #define NDEBUG */ int fun(int arg) { // Diese Funktion ist nur für Argumente mit 0 < arg < 5000 spezifiziert assert((arg > 0) && (arg < 5000)); return 2*arg; } int main(int argc, char **argv) { int i, *ip; ip = (int *) malloc(sizeof(*ip)); assert(ip != NULL); // Das würde man besser mit if() abprüfen i = fun(4711); assert(i > 0); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 238 Fehlersuche mit <assert.h> cc -DNDEBUG prog.c cc prog.c übersetzt Programm ohne die Überprüfung. übersetzt das Programm mit Überprüfung Verwenden Sie assertions insbesondere bei umfangreichen Programmen: 1. 2. Überprüfen, ob Funktionsargumente wirklich sinnvolle Werte haben Überprüfen, ob Funktionsresultate wirklich sinnvolle Werte haben Übersetzen Sie die Produktionsversion ihres Programms mit -DNDEBUG und die Testversion ohne diese Option. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 239 Signale <signal.h> Signale dienen zur asynchronen Programmausführung, z.B. im Fehlerfall. C kennt mindestens die folgenden Signale: Signalname Bedeutung SIGABRT Fehlerhafte Programmbeendung (z.B. über abort()) SIGFPE Fließkommafehler (z.B. Division durch 0) SIGILL Illegale Machineninstruktion SIGINT Ctl-C gedrückt SIGSEGV Illegaler Speicherzugriff SIGTERM Signal zur Programmbeendigung Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 240 Signale <signal.h> Zu jedem Signal kann man einen Signal-Handler (eine Funktion) angeben, die bei Auftreten des Signals aufgerufen wird. Angabe über: void (*signal ( int sig, void (*func)(int))) (int); int raise(int sig); Beispiel: Ausgabe: #include <signal.h> #include <assert.h> int a, b=0, c=0; > a.out Fehler erkannt Bis jetzt läuft alles gut. Floating point exception (core dumped) void sig_handler(int sig) { printf("Fehler erkannt\n"); c = 1; } int main(int argc, char **argv) { (void)signal(SIGFPE,sig_handler); a = b / c; printf("Bis jetzt läuft alles gut.\n"); c = 0; (void)signal(SIGFPE, SIG_DFL); a = b / c; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 241 Mathematische Funktionen (1) <math.h> Prototyp Beschreibung double acos( double x ); Arcuscosinus (Hauptwert) double asin( double x ); Arcussinus (Hauptwert) double atan( double x ); Arcustangens x (Hauptwert) double atan2( double y, double x ); Arcustangens y/x (Hauptwert) double cos( double x ); Cosinus (in Radianten) double sin( double x ); Sinus (in Radianten) double tan( double x ); Tangens (in Radianten) double cosh( double x ); Cosinus Hyperbolicus double sinh( double x ); Sinus Hyperbolicus double tanh( double x ); Tangens Hyperbolicus double exp( double x ); Exponentialfunktion ex Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 242 Mathematische Funktionen (2) <math.h> Prototyp Beschreibung double pow( double x, double y); xy double sqrt( double x ); Wurzel x double ceil( double x ); Kleinster ganzzahliger Wert nicht kleiner als x double floor( double x ); größter ganzzahligert Wert nicht größer als x double fabs( double x ); Absolutwert double fmod( double x, double y ); Rest von x/y double modf( double x, double *ptr); Ganzzahliger Teil von x ist Ergebnis, Nachkommateil in *ptr double log( double x ); loge(x) double log10( double x ); log10(x) double ldexp( double x, int exp ); x * 2exp double frexp( double x, int *exp); Aufspalten von x in normalisierten Bruchteil in [0.5,1) oder 0 und Potenz von 2 in *exp (entspricht ldexp-1) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 243 Typgenerische Mathematikmakros • • Bis C89 gab es die mathematischen Bibliotheksfunktionen nur für double Ab C99 gibt es die typgenerischen Makros für Fließkommatypen in tgmath.h, die entsprechend dem Argumenttyp eine andere Funktion aufrufen (z.B. steht hinter dem Makro sin dann sinf, sin, sinl für ein float, double bzw. long double Argument). Beispiel: #include <tgmath.h> int main(int argc, char **argv) { float f = 1.0f; double d = 1.0; long double l = 1.0L; f = sin(f); d = sin(d); l = sin(l); // Hier wird sinf aufgerufen // hier wird sin aufgerufen // hier wird sinl aufgerufen } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 244 Zeichenfunktionen <ctype.h> Prototyp Beschreibung int isalnum( int c ); Wahr, falls c Buchstabe oder Ziffer int isalpha( int c); Wahr, falls c Buchstabe int iscntrl( int c); Wahr, falls c Kontrollzeichen int isdigit( int c ); Wahr, falls c Ziffer int isgraph( int c ); Wahr, falls c druckbares Zeichen ungleich Leerzeichen int isprint( int c ); Wahr, falls c druckbares Zeichen int ispunct( int c ); Wahr falls c druckbares Zeichen und kein Buchstabe, Ziffer, Leerz. int isspace( int c ); Wahr, falls c kein Leerzeichen int isupper( int c ); Wahr, falls c Großbuchstabe int islower( int c ); Wahr, falls c Kleinbuchstabe int isxdigit( int c ); Wahr, falls c Hexadezimalzeichen (0-9, a-f, A-F) int tolower( int c ); Liefert Großbuchstaben, wenn c Kleinbuchstabe. Sonst c. int toupper( int c ); Liefert Kleinbuchstaben, wenn c Großbuchstabe. Sonst c. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 245 Stringfunktionen <string.h> Prototyp Beschreibung void *memcpy(void *s1, const void *s2, size_t n); Kopiert n Zeichen von s2 nach s1 void *memmove(void *s1, const void *s2, size_t n); Kopiert n Zeichen von s2 nach s1 (Überlappung erlaubt) int memcmp(const void *s1, const void *s2, size_t n); Vergleicht n Zeichen von s1 und s2 void *memchr(const void *s, int c, size_t n); Sucht Zeichen c ab Position s. Liefert gefundene Position/NULL void *memset(void *s, int c, size_t n); Füllt Speicherbereich mit c Beispiel: #include <string.h> int a[1000000], b[1000000]; int main(int argc, char **argv) { int i; for(i = 0; i < 1000000; i++) a[i] = i; memcpy(a, b, 100000 * sizeof(int)); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 246 Stringfunktionen <string.h> Prototyp Beschreibung char *strcpy(char *s1, const char *s2); Kopiert s2 nach s1 char *strcat(char *s1, const char *s2); Fügt s2 an s1 an int strcmp(const char *s1, const char *s2); Vergleicht s1 mit s2. Liefert -1,0,1 char *strchr(const char *s, int c); Sucht c in s und liefert Zeiger/NULL. char *strstr(const char *s1, const char *s2); Sucht s2 in s1. Liefert Zeiger/NULL. int strlen(const char *s); Länge des Strings (ausschließlich \0) Beispiel: #include <string.h> int main(int argc, char **argv) { char s1[10], *s2 = "hallo"; printf("Laenge=%d, <ll> kommt vor:%p \n", strlen(s2), strstr(s2, "ll")); strcpy(s1, s2); /* nicht möglich: strcpy(s2, s1); */ } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 247 Zeit und Datum <time.h> Es existieren 3 Datentypen zur Darstellung von Datum und Zeiten: clock_t Skalarer Wert für CPU-Zeit time_t Skalarer Wert für kompakte Darstellung von struct tm struct tm Strukturierter Wert mit mindestens den folgenden Komponenten int tm_sec; Sekunden (0-59) int tm_min; Minuten (0-59) int tm_hour; Stunden seit Mitternacht (0-23) int tm_mday; Tag des Monats (1-31) int tm_mon; Monat des Jahres (0-11) int tm_year; Jahr seit 1900 int tm_wday; Wochentag seit Sonntag (0-6) int tm_yday; Tag seit Jahresanfang (0-365) int tm_isdst; Sommerzeit-Flag (>0 falls Sommerzeit) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 248 Zeit und Datum <time.h> Prototyp Beschreibung clock_t clock(void); CPU-Zeit in Ticks. Dividiert durch CLOCKS_PER_SEC liefert CPU-Zeit in s. Vorsicht: int-Division! time_t time(time_t *timer); Ermittelt aktuelle Daten. Falls timer!=NULL, wird dort ebenfalls abgespeichert. double difftime(time_t t1, time_t t2); Differenz in Sekunden time_t mktime(struct tm *timeptr); Umwandlung struct tm nach time_t char *asctime(const struct tm *t); Umwandlung Zeit nach String (in statischen Bereich) struct tm *localtime(const time_t *t); Umwandlung time_t nach struct tm mit lokaler Zeitzone struct tm *gmtime(const time_t *timer); Umwandlung time_t nach struct tm mit Zeitzone UTC size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *t); Umwandlung von Daten mit mit Formatierungsangaben Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 249 Beispiel #include <time.h> #include <math.h> int main(int argc, char **argv) time_t t; clock_t t0, t1; int i; double a = 0.0; { /* CPU-Zeit Start und aktuelle Zeit */ t0 = clock(); t = time(NULL); /* in lokale Zeit und nach String umwandeln */ printf("Lokale Zeit und Datum sind: %s\n", asctime(localtime(&t))); /* CPU-Zeit verbrauchen und Gesamt-CPI-Zeit ausgeben */ for(i=0; i < 1000000; i++) a += sin(i); printf("verbrauchte CPU-Zeit: %f s\n", (clock()-t0)/(double)CLOCKS_PER_SEC); } Ausgabe: Lokale Zeit und Datum sind: Fri Nov Der Wert von a ist 0.232884 verbrauchte CPU-Zeit: 0.260000 s Fachbereich Angewandte Informatik 3 09:12:49 2000 Programmieren in C Prof. Dr. Rudolf Berrendorf 250 Speicherverwaltung <stdlib.h> Prototyp Beschreibung void *malloc(size_t size); Legt neuen Speicherbereich im Heap an. void *calloc(size_t nmemb, size_t size); Legt neuen Speicherbereich (mit 0) im Heap an void free(void *ptr); Gibt Speicherbereich wieder frei void *realloc(void *ptr, size_t size); Vergrößert/verkleinert Speicherbereich, evtl. an anderer Stelle im Speicher Beispiel: #include <stdlib.h> int main(int argc, char **argv) { int *ptr_i, *ptr_j; ptr_i = (int *)malloc(sizeof(*ptr_i)); *ptr_i = 4711; ptr_i = (int *)realloc(ptr_i, 2 * sizeof(*ptr_i)); free(ptr_i); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 251 Schnittstelle zum Betriebssystem <stdlib.h> Prototyp Beschreibung void exit(int status); Beendet die Programmausführung void abort(void); Erzeugt SIGABRT-Signal und beendet Programm mit Fehlercode int atexit(void (*func)(void)); Teilt dem System mit, dass vor dem eigentlichen Programmende die Funktion func aufgerufen werden soll char *getenv(const char *name); Liefert einen String/NULL auf den Wert der Umgebungsvariable name int system(const char *str); Ruft den Kommandointerpreter (shell) des Betriebssystems mit dem Kommando str auf. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 252 Beispiel <stdlib.h> #include <stdlib.h> #define UVAR "PS1" /* Name einer Umgebungsvariablen */ /* Diese Funktion soll vor Programmende ausgeführt werden */ void exit_fun(void) { printf("Jetzt geht es langsam zu Ende\n"); } int main(int argc, char **argv) { char *wert; /* exit_fun soll vor Programmende ausgeführt werden */ atexit(exit_fun); /* Umgebungsvariable suchen */ if( (wert = getenv(UVAR)) == NULL) printf("Keine Umgebungsvariable %s bekannt\n", UVAR); else printf("Wert von %s ist %s\n", UVAR, wert); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 253 Sortieren <stdlib.h> void qsort(const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); Beispiel: #include <stdlib.h> int mycompare(const void *arg1, const void *arg2) return *(int*)arg1 - *(int *)arg2; } { int main(int argc, char **argv) { int ifeld[10] = {9,8,7,6,5,4,3,2,1,0}; qsort((const void *)ifeld, 10, sizeof(ifeld[0]), mycompare); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 254 Binäres Suchen <stdlib.h> void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); Beispiel: #include <stdlib.h> int mycompare(const void *arg1, const void *arg2) return *(int*)arg1 - *(int *)arg2; } { int main(int argc, char **argv) { int ifeld[10] = {0,1,2,3,4,5,6,7,8,9}; int suchen = 2; if(bsearch((const void *)&suchen, (const void *)ifeld, 10, sizeof(ifeld[0]), mycompare) == NULL) exit(EXIT_FAILURE); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 255 Zufallszahlen <stdlib.h> Prototyp Beschreibung int rand(void); Liefert eine Zufallszahl zwischen 0 und RAND_MAX. Bei Normierung auf [0,1) muss man mit der Integer-Division aufpassen. void srand(unsigned int seed); Initialisiert den Zufallszahlengenerator mit dem Startwert seed. So kann man wiederholt die gleiche Folge von Zufallszahlen erzeugen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 256 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 257 Modulare Programmierung • Nur Bezeichner global bekannt machen, die auch außerhalb der .cDatei genutzt werden sollen. Dies kann man bei Objekten dadurch erreichen, dass alle anderen (lokalen) Objektdefinition (Variablen, Funktionen) auf dem top-level das Attribut static bekommen. • Alle global sichtbaren Bezeichner einer Datei test.c in einer Datei test.h mit vollem Typ und dem Zusatz extern deklarieren (nicht definieren!). Ebenfalls Typdefinitionen aus test.c, die global bekannt sein sollen, (nur) in test.h angeben. • In allen Dateien, die solch ein Objekt bzw. Typ nutzen (auch in der zugehörigen Definitionsdatei test.c) ein #include "test.h" einfügen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 258 Beispiel: bsp.c /* global sichtbare Deklarationen dieser Datei Auch hier ein include, damit der Compiler Inkonsistenzen prüfen kann. */ #include "bsp.h" /* global_var ist eine extern sichtbare Variable */ int global_var = 5; /* local_var soll nur innerhalb der Datei bekannt sein */ static int local_var = 3; /* local_fun soll nur innerhalb der Datei bekannt sein */ static double local_fun(int y) { return 2.0 * local_var * global_var * y; } /* global_fun ist eine extern sichtbare Funktion */ double global_fun( double x ) { return local_fun(x) + x + global_var + local_var; } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 259 Beispiel: bsp.h /* global sichtbare Deklarationen der Datei bsp.c */ /* global_var ist eine extern sichtbare Variable */ extern int global_var; /* global_fun ist eine extern sichtbare Funktion */ /* Wichtig: Deklaration hier, keine Definition */ extern double global_fun( double x ); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 260 Beispiel: nutzung_bsp.h /* Nutzen der exportierten Deklarationen aus bsp.c */ #include "bsp.h" int main(int argc, char **argv) { printf("Wert von global_var ist %d\n", global_var); printf("Und global_fun(3.0) ist %f\n", global_fun(3.0)); } Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 261 Beispiel: Stack Stack.h: typedef int * Stack; extern Stack stack_create(int maxele); extern void stack_push(Stack s, int x); extern int stack_pop(Stack s); Stacktest.c: Stack.c: #include "Stack.h" /* keine Fehlerüberprüfung hier! */ Stack stack_create(int maxele) { return (Stack)malloc(maxele*sizeof(*s)); } #include "Stack.h" int main(int argc, char **argv) { Stack s; int ele; void stack_push(Stack s, int x) { *(s++) = x; } s = stack_create(10); stack_push(s, 10); ele = stack_pop(s); int stack_pop(Stack s) { return *(s--); } /* hier wird es spannend */ stack_push(s, 20.0); Fachbereich Angewandte Informatik } Programmieren in C Prof. Dr. Rudolf Berrendorf 262 Makefiles Problem: 1. Projekt besteht z.B. aus 300 .c-Dateien und 200 .h-Dateien 2. Komplexe Abhängigkeiten zwischen .c und .h-Dateien 3. Bei Änderungen an .c-Dateien möchte man nur geänderte Dateien neu übersetzen 4. Bei Änderungen an .h-Dateien möchte man alle abhängigen Dateien neu übersetzen Lösung: Makefiles sind ein Hilfsmittel, um solche (und andere) Probleme zu lösen Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 263 Makefiles Wesentliches Element: Abhängigkeitsregel Ziel : Abhängigkeiten Kommando Falls eine der Abhängigkeiten (Dateien) jünger (im Sinne von modifiziert) als das Ziel ist, so wird das Kommando ausgeführt. Beispiel: datei.o: datei.c datei.h xyz.h cc -c datei.c Wichtige Syntaxregel: Vor dem Kommando muss ein TAB-Zeichen sein (keine Leerzeichen)! Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 264 Beispiel # Makefile prog: test1.o test2.o cc -o prog test1.o test2.o Aufruf: > make test1.o: test1.c test1.h cc -c -O test1.c oder > make prog test2.o: test2.c test1.h cc -c -O test2.c /* Dies ist test1.c */ #include "test1.h" /* Dies ist test2.c */ #include "test1.h" void fun(void) { printf("Hallo\n"); } int main(int argc, char **argv) { fun(); } /* Dies ist test1.h */ void fun(void); Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 265 Variablen # Makefile CC = cc CFLAGS = -O OBJECTS = test1.o test2.o prog: $(OBJECTS) $(CC) -o prog $(OBJECTS) test1.o: test1.c test1.h $(CC) -c $(CFLAGS) test1.c test2.o: test2.c test1.h $(CC) -c $(CFLAGS) test2.c clean: -rm $(OBJECTS) prog Packet: tar -cf alles.tar test1.c test2.c test1.h Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 266 Automatische Variablen # Makefile CC = cc CFLAGS = -O OBJECTS = test1.o test2.o prog: $(OBJECTS) $(CC) -o $@ $^ test1.o: test1.c test1.h $(CC) -c $(CFLAGS) $< test2.o: test2.c test1.h $(CC) -c $(CFLAGS) $< clean: -rm $(OBJECTS) prog Packet: tar -cf alles.tar test1.c test2.c test1.h $@ $^ $< Ziel der Regel Namen aller Abhängigkeiten Name der 1. Abhängigkeit Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 267 Implizite Regeln # Makefile CC = cc CFLAGS = -O OBJECTS = test1.o test2.o prog: $(OBJECTS) $(CC) -o $@ $^ test1.o: test1.c test1.h test2.o: test2.c test1.h clean: -rm $(OBJECTS) prog Packet: tar -cf alles.tar test1.c test2.c test1.h Implizite Regel: .o: .c $(CC) -c $(CPPFLAGS) $(CFLAGS) $< Weitere implizite Regeln für viele Dateiendungen. Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 268 Anmerkungen • • • • • • make kann wesentlich mehr, als hier vorgestellt wurde make ist wesentlich flexibler, als hier vorgestellt wurde Nicht nur zum Übersetzen von Programmen geeignet Sollte man generell zu jedem Projekt anlegen Hierarchisch organisierte Projekte durch rekursiven Aufruf von make mit Makefiles in Unterverzeichnissen möglich autoconf und automake zur Erstellung portabler Software (Fortgeschrittene) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 269 Programmierwerkzeuge • • Umfangreiche Entwicklungsumgebungen (Microsoft, SUN, IBM, SGI, ...) Neben dem reinen Compiler und Linker werden Programmierwerkzeuge angeboten: – – – – – • • Projektunterstützung Dokumentation Fehlersuche Laufzeituntersuchung ... Hier sollen prinzipiellen Möglichkeiten gezeigt werden Schon behandelt: Projektunterstützung mit Makefiles Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 270 Fehlersuche • Einfache (und sehr beschränkte) Möglichkeiten: – assert(x != 0); a = a / x; – Signal-Handler • Werkzeug zur Fehlersuche: Debugger • Kommandozeilenorientierte Debugger (z.B. gdb) oder grafisches Frontend (z.B. ddd) • Vorgehensweise: – Übersetzen und Linken des Quellcodes mit -g (Optimierung evtl. ausschalten). Beispiel: cc -g meintest.c – Starten des Debuggers mit dem Namen des ausführbaren Programms als Argument. Beispiel: gdb a.out – Starten des ausführbaren Programms unter der Aufsicht des Debuggers. Beispiel: > run – Analysieren der Fehler Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 271 Debugger-Kommandos Debugger-Kommandos können nur eingegeben werden, wenn das Programm nicht läuft. 1. Breakpoint Programm hält an, wenn es während des Programmlaufs an diese Stelle gelangt. Man kann dann im Debugger weitere Kommandos ausführen (z.B. Variableninhalte sich anschauen). Syntax: break [Dateiname:]Zeilennummer Beispiel: break meintest.c:75 2. Einzelschrittausführung Nur der nächste Befehl (bezogen auf das Source-Programm) wird ausgeführt. Unterscheidung: next: Es werden Funktionsaufrufe nicht verfolgt. step: Bei einem Funktionsaufruf wird in die Funktion gesprungen. 3. Anzeigen der Aufrufhierarchie mit where 4. Anzeigen des Source-Codes mit list Syntax: list [Dateiname:]Zeilennummer Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 272 Debugger-Kommandos 1. Anschauen von Variableninhalten Mit print kann man sich Variableninhalte anschauen. Es sind auch komplexe Angaben möglich. Beispiel: print i print *p print p->daten[i] 2. Verändern eines Variableninhalts Beispiel: set i=2 set p->daten[i]=5 Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 273 Programmoptimierung Nachdem (!) man eine korrekte Programmversion hat, sollte man bei oft und lang laufenden Programmen eine Optimierungsphase einfügen. Bei großen Programmpaketen ist eine Analyse des Laufzeitverhaltens allerdings schwierig. Ein einfacher und in vielen Fällen hilfreicher Ansatz ist das Profiling, eine statistische Methode. Während des Programmlaufs wird in bestimmten Zeitintervallen (z.B. 10 ms) vom Laufzeitsystem überprüft, an welcher Stelle im Programm man sich zu diesem Zeitpunkt befindet. Diese Daten werden gesammelt. Diese Methode liefert für länger laufende Programme eine gute Basis zur Programmoptimierung. Vorgehensweise: 1. 2. 3. 4. Übersetzen und Linken des Programm mit -pg Ausführen des Programms mit repräsentativen Eingabedaten Nach dem Programmlauf existiert eine Datei gmon.out Analysieren des Laufzeitverhaltens mit: gprof a.out Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 274 Beispiel Übersetzen und Ausführen: cc -O -pg meintest.c a.out gprof a.out >meintest.daten Analysieren der Ergebnisse: ... flat profile: % cumulative time seconds 55.17 10.68 44.42 19.28 0.36 19.35 0.05 19.36 0.00 19.36 0.00 19.36 ... Fachbereich Angewandte Informatik self self total seconds calls ns/call ns/call 10.68 640000 16687.50 30125.00 8.60 187841276 45.78 45.78 0.07 640000 109.38 109.38 0.01 0.00 1 0.00 0.00 0.00 1 0.00 0.00 Programmieren in C Prof. Dr. Rudolf Berrendorf name BerechnePunkt c_abs_ohne_wurzel ZeichnePunkt main hole_argumente init_display 275 Inhalt • • • • • • • • • • • • Einführung Grundelemente der Sprache Präprozessor Anweisungen Funktionen Typen Deklarationen Ausdrücke Ein-/Ausgabe Standardbibliotheken Modulare Programmierung C++ Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 276 C++ • • • • • • • • 1980-1984: Bjarne Stroustrup (AT&T Labs): C with Classes 1983 Umbenennung C++ C++ zuletzt standardisiert in ISO/IEC 14882:1998 Dateiendung für C++:.cpp C++ ist sehr (!) komplex C++ ist eine Erweiterung von C (kleine Nichtschnittmenge), Programmierung in C++ unterscheidet sich aber wesentlich von der Programmierung in C Objektorientierte Programmierung möglich (aber nicht zwingend): Klassen, Vererbung, Überladen Standard Template Library STL (Container, Iteratoren) Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 277 Wesentliche Erweiterungen von C++ zu C • • • • • • • • • • Klassen: class Complex {...}; Templates: array<int> ia(20); array <char> ic[10]; Namespaces (zusätzliche Hierarchie für Sichtbarkeit von Namen) Überladen von Funktionen, Operatoren: feld1 = feld2 + feld3; Exceptions Run Time Type Information (RTTI) Referenztypen (call-by-value, call-by-reference) Statisches/dynamisches Binden von Funktionen Ein-/Ausgabe mit <<, >> Speicherverwaltung mit new, delete Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 278 Beispiel using namespace std; class Complex { public: // Konstruktor Complex(double r=0.0, double i=0.0) {re=r; im=i;} // Destruktor ~Complex(void) {} // Überladen von Funktionen double real(void) { return re; } double imag(void) { return im; } double real(double r) { re = r; } double imag(double i) { im = i; } using namespace std; int main(...) { Complex c1, c2(3.0, 4.0), c3; c3 = c1 + c2; cout << c3.real() << c3.im(); } // Überladen von Operatoren const Complex operator+ (const Complex& rhs) { return Complex( real()+rhs.real(), imag()+rhs.imag());} private: double re, im; }; Fachbereich Angewandte Informatik Programmieren in C Prof. Dr. Rudolf Berrendorf 279