06.01.2015 Aufrufe

Beispiel - Prof. Dr. Rudolf Berrendorf

Beispiel - Prof. Dr. Rudolf Berrendorf

Beispiel - Prof. Dr. Rudolf Berrendorf

MEHR ANZEIGEN
WENIGER ANZEIGEN

Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.

YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

rudolf.berrendorf@fh-bonn-rhein-sieg.de<br />

Fachbereich Angewandte Informatik<br />

Fachhochschule Bonn-Rhein-Sieg


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

2


Sie denken Sie kennen schon C<br />

Wie wär‘s mit westley.c aus dem IOCCC 1988 (kein Vorbild!):<br />

#define _ -F


Literatur<br />

• R.<strong>Berrendorf</strong>:<br />

Einführung in die Programmiersprache C<br />

Benutzerhandbuch KFA-ZAM-BHB-0095, Forschungszentrum Jülich<br />

http://www.fz-juelich.de/zam/docs/<br />

http://www.inf.fh-bonn-rhein-sieg.de/ lokale Kopie<br />

• S.P.Harbison, G.L.Steele Jr.:<br />

C: A Reference Manual<br />

5. Ausgabe, Prentice Hall, 2002<br />

• B.W.Kernighan, D.M.Ritchie:<br />

Programmieren in C<br />

2. Ausgabe, Hanser, 1990<br />

• Bjarne Stroustrup:<br />

Die C++ Programmiersprache<br />

4. Auflage, Addison Wesley, 2000<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

4


Historie von C<br />

• 1960: ALGOL60<br />

• CPL (Combined Programming Language)<br />

• 1967: BCPL (Based Combined Programming Language)<br />

Martin Richards (MIT), Systemprogrammierung<br />

• 1970: B<br />

Ken Thompson (AT&T Bell Labs), Implementierungssprache für Unix<br />

• 1972: C<br />

Dennis M. Ritchie (AT&T Bell Labs), Implementierungssprache für Unix<br />

• 1978: C-Sprachdefinition im Buch The C Programming Language von<br />

Brian W. Kernighan und Dennis M. Ritchie (K&R-C)<br />

• 1983: Bildung des ANSI-Komitees X3J11 zur Standardisierung von C<br />

• 1989: ANSI C (C89)<br />

• 1999: kleinere Erweiterungen zum internationalen Standard ISO/IEC<br />

9899:1999 (C99)<br />

(Viele Compiler kennen noch nicht die in C99 hinzugekommenen<br />

Erweiterungen)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

5


Eigenschaften von C<br />

• Universelle Programmiersprache: einsetzbar für Anwendungen in (fast)<br />

allen Bereichen<br />

• Hardware-nahe Programmierung möglich<br />

• Modulares Programmieren möglich<br />

• Möglichkeiten zur Formulierung effizienter Programme<br />

• C-Compiler erzeugen effizienten Code<br />

• Viele Datentypen, viele Kontrollstrukturen, viele Operatoren<br />

• Hohe Portabilität möglich<br />

(notwendige Voraussetzung: Beschränkung auf Standard-C)<br />

• Standardbibliothek moderat umfangreich (verglichen mit Java), jedoch<br />

keine Änderungen am existierenden Interface über viele Jahre (voll<br />

aufwärtskompatibel)<br />

• Sprache hat keine Konstrukte zum objektorientierten Programmieren<br />

• Grafikbibliothek nicht Teil von C<br />

• Fehlerquellen (== und =, Typumwandlung usw.)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

6


Advanced Hello World<br />

Datei hello.c:<br />

/* Füge Definitionen der Standard-I/O-Bibliothek ein */<br />

#include <br />

/* Füge Definitionen der Standard-Bibliothek ein */<br />

#include <br />

/* hier folgt die Standard-Kopfzeile des Hauptprogramms */<br />

int main (int argc, char **argv)<br />

{<br />

/* Deklariere eine Integer-Variable mit Namen i */<br />

int i;<br />

/* Weise i einen Wert zu */<br />

i = 4711;<br />

/* Gebe den Wert von i auf dem Terminal aus */<br />

printf ("i = %d\n", i);<br />

}<br />

/* Beende das Programm */<br />

return EXIT_SUCCESS;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

7


Dateien<br />

• .c: C Source-Code; Teil eines C-Programms (C-Source)<br />

• .h: Extern sichtbare Schnittstelle einer oder mehrerer .c-Dateien (C-Source)<br />

• .o: Übersetzte Programmdatei (nicht-ausführbarer Objektcode)<br />

• a.out: Ausführbare Programmdatei (ausführbarer Objektcode;<br />

Name der Datei beliebig)<br />

• .a: Programmbibliothek (nicht-ausführbarer Objektcode)<br />

C Source<br />

nicht-ausführbarer<br />

Objektcode<br />

ausführbarer<br />

Objektcode<br />

x.c<br />

Übersetzen<br />

x.o<br />

x.h<br />

Übersetzen<br />

y.o<br />

Linken<br />

a.out<br />

y.c<br />

libgr.a<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

8


Übersetzen auf Unix-Systemen<br />

• Programm in einer Datei abspeichern.<br />

Konvention:<br />

Programmdateien:<br />

name.c<br />

Dateien mit ext. Schnittstellendef.: name.h<br />

• Übersetzen:<br />

cc –c name.c<br />

Erzeugt, falls fehlerfrei übersetzt werden kann, Datei name.o<br />

• Linken:<br />

cc –o name name.o<br />

Erzeugt, falls keine Fehler auftreten, die ausführbare Datei name<br />

• Ausführen:<br />

./name<br />

• Übersetzen und Linken in einem Schritt:<br />

cc name.c<br />

Erzeugt a.out als ausführbare Datei<br />

• Linken mehrerer Objektdateien<br />

cc –o name name_1.o name_2.o ... name_n.o<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

9


Compiler-Optionen<br />

• -c<br />

nur Übersetzen, nicht Linken<br />

• -O<br />

Optimierung einschalten (evtl. –O0, –O1, -O2 –O3 usw. möglich)<br />

• -g<br />

Zusätzliche Informationen für Debugger in Objektdatei erzeugen.<br />

Optimierung und Debugger-Informationen schließen sich oft aus.<br />

• -ISuchpfad<br />

Suchpfad für Include-Dateien angeben (mehrere Suchpfade durch :<br />

getrennt). Mehrere –I Optionen hintereinander sind möglich.<br />

• Informationen zum C-Compiler wie gewohnt über "man cc"<br />

Die Auswertung erfolgt üblicherweise von links nach rechts!<br />

<strong>Beispiel</strong>:<br />

cc –c –O2 –I./include:/usr/local/produkt/include prog.c<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

10


Linker-Optionen<br />

• -o name<br />

Ausführbare Datei hat Namen name (Default: a.out)<br />

• -LSuchpfad<br />

Suchpfad für Libraries angeben (Mehrere Suchpfade durch : getrennt).<br />

Mehrere –L Optionen hintereinander sind möglich.<br />

• -lname<br />

Library libname.a (bzw. dyn. Library libname.so) dazulinken.<br />

Mehrere Libraries können durch Komma getrennt werden. Die Libraries<br />

werden nach dem Suchpfad gesucht. In den angegebenen Libraries<br />

werden alle Symbole gesucht, die zu diesem Zeitpunkt noch offen sind<br />

(d.h. zu der es eine Referenz aber keine Definition gibt).<br />

Die Auswertung erfolgt üblicherweise von links nach rechts!<br />

<strong>Beispiel</strong>:<br />

cc –o prog –L./lib prog.o –ltollelib,lib2<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

11


Zusammenfassung Syntax<br />

Syntax<br />

<strong>Beispiel</strong><br />

cc<br />

cc<br />

-o executable_name<br />

Compiler-Flags<br />

Dateinamen (.c oder .o)<br />

Linker-Flags<br />

-o my_executable<br />

-O -I/usr/local/include<br />

datei1.c datei2.o<br />

-I/usr/local/lib -lmeinelib<br />

obligatorische Angaben<br />

optionale Angaben<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

12


<strong>Beispiel</strong>e<br />

cc -c x.c y.c<br />

x.c<br />

Übersetzen<br />

x.o<br />

y.c<br />

Übersetzen<br />

y.o<br />

cc -o ausfuehr x.c y.c<br />

x.c<br />

y.c<br />

Übersetzen<br />

Übersetzen<br />

x.o<br />

y.o<br />

Linken<br />

ausfuehr<br />

cc x.o y.c<br />

y.c<br />

Übersetzen<br />

x.o<br />

y.o<br />

Linken<br />

a.out<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

13


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

14


Zeichensatz<br />

• Source-Zeichensatz (aus dem Programme bestehen)<br />

– Alphanumerischen Zeichen<br />

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<br />

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<br />

0 1 2 3 4 5 6 7 8 9<br />

– Leerzeichen und Zeilenendezeichen<br />

– Sonderzeichen<br />

( ) [ ] { } < > + - * / % ^ ~ & | _ = ! # \ , . ; : ` "<br />

– Steuerzeichen:<br />

horizontaler Tabulator (TAB), vertikaler Tabulator, Seitenvorschub<br />

– Oft zusätzliche Zeichen möglich (nicht in Standard spezifiziert):<br />

• <strong>Beispiel</strong>: @, $, Backspace<br />

• (Eigentlich) nur in Zeichenkonstanten, Strings, Kommentaren, Dateinamen<br />

• Ausführbarer Zeichensatz<br />

– Evtl. andere Codierung als Source-Zeichensatz (<strong>Beispiel</strong>:Zeilenende)<br />

– Interessant (für den Compiler) bei Cross-Compilierung<br />

• Trigraphs (aus prähistorischer Zeit; vermeiden!)<br />

– Alternativschreibweise für einige Symbole, die früher nicht auf allen Tastaturen<br />

vorhanden waren<br />

– <strong>Beispiel</strong>: = für #, ( für [ usw.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

15


Aufbau von C-Dateien<br />

• Formatfrei<br />

• Leerzeichen, Zeilenendezeichen, vertikaler und horizontaler Tabulator,<br />

Seitenvorschub und Kommentar (s.u.) sind Trennzeichen (außer in<br />

Zeichenkonstanten und Strings)<br />

• Mehrere Trennzeichen werden logisch als ein Trennzeichen<br />

angesehen<br />

• Backslash \ direkt gefolgt von Zeilenende wird ignoriert, so dass eine<br />

logische Zeile entsteht. Dies ist z.B. bei Präprozessor-Direktiven<br />

notwendig, die sich nur über eine logische Zeile erstrecken können<br />

• Groß- und Kleinschreibung ist signifikant<br />

<strong>Beispiel</strong>e:<br />

Bezeichner1 Bezeichner2 Bezeichner3<br />

Diese beiden (es folgt eine Backslash-Newline-Kombination) \<br />

Zeilen werden als eine logische Zeile aufgefasst.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

16


Kommentare<br />

• Dienen der besseren Lesbarkeit von Programmen<br />

• Können überall im Programm erscheinen, außer in Zeichenkonstanten<br />

und Strings<br />

• Sind Trennzeichen<br />

• Können nicht geschachtelt werden!<br />

• Können über mehrere Zeilen gehen<br />

• Beginnen mit /* und enden mit */ über mehrere Zeilen möglich<br />

• // bis Zeilenende<br />

<strong>Beispiel</strong>:<br />

// Dies ist ein Kommentar<br />

/* Dies ist /* ebenfalls ein Kommentar */<br />

/* Dies ist ein Kommentar,<br />

der über mehrere Zeilen geht.<br />

*/<br />

/* Dies ist /* noch<br />

ein Kommentar */ der hier leider nicht mehr weitergeht (Fehler) */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

17


Grundelemente der Sprache<br />

• Bezeichner, z.B. meine_Variable<br />

• Schlüsselwörter, z.B. const, int<br />

• Operatoren, z.B. +, -<br />

• Separatoren oder Punktsymbole, z.B. [ ]<br />

• Konstanten, z.B. 5, 3.14<br />

• Strings, z.B. "string"<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

18


Bezeichner<br />

• Bezeichner werden für Variablen, Funktionen, Makros usw. verwendet<br />

• Bezeichner sind eine Folge von Buchstaben, Ziffern und dem Zeichnen<br />

_, wobei das erste Zeichen keine Ziffer sein darf<br />

• Längste mögliche Erweiterung bildet Bezeichner<br />

• Klein-/Großschreibung wird unterschieden<br />

• Keine reservierten Schlüsselwörter erlaubt<br />

• Bezeichner können beliebig lang sein, wobei jedoch nur eine bestimmte<br />

Länge signifikant sein muss (C89 hat stärkere Restriktionen):<br />

– Mindestens die ersten 63 Zeichen bei internen Namen<br />

– Mindestens 31 Zeichen bei externen Namen<br />

<strong>Beispiel</strong>:<br />

I i<br />

A5 _Bezeichner_1_<br />

Dies_Ist_ein_sehr_langer_Bezeichner_der_aber_gueltig_ist<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

19


Bezeichner<br />

Bezeichner<br />

Buchstabe<br />

_<br />

Buchstabe<br />

Ziffer<br />

_<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

20


Vereinbarung<br />

• Variablen- und Funktionsnamen werden (überwiegend) klein<br />

geschrieben<br />

• Makronamen werden komplett aus Großbuchstaben gebildet<br />

• Namen mit _ als erstem Buchstaben sind für Namen von (internen)<br />

Bibliotheksfunktionen oder vordefinierten Makros „reserviert“<br />

<strong>Beispiel</strong>:<br />

/* Makros */<br />

#define PI 3.1415<br />

#define DIMENSION_1 100<br />

/* Variablen */<br />

int i,j;<br />

/* Funktionen */<br />

int MeineTolleFunktion();<br />

/* "reservierte" Namen */<br />

__LINE__, __DATE__, _exit();<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

21


Reservierte Schlüsselwörter<br />

• C kennt die folgenden reservierten Schlüsselwörter mit vordefinierter<br />

Bedeutung, die z.B. nicht als Variablennamen oder Funktionsnamen<br />

verwendet werden können (wohl aber als Makronamen).<br />

• auto do inline switch<br />

_Bool double int typedef<br />

break else long union<br />

case enum register unsigned<br />

char extern return void<br />

_Complex float short volatile<br />

const for signed while<br />

continue goto sizeof<br />

default if static<br />

restrict _Imaginary struct<br />

• Blau gekennzeichnete Namen sind neu in C99<br />

• Vordefinierter Bezeichner (ist aber kein Schlüsselwort)<br />

– Für jede Funktion findet implizit eine Vereinbarung statt:<br />

static const char __func__ [] = "Name der Funktion";<br />

– Nutzung:<br />

if(failed) printf("Function %s failed\n", __func__);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

22


Operatoren und Separatoren<br />

• Operatoren (z.B. +) werden in Ausdrücken benutzt<br />

• Separatoren dienen der Trennung von Symbolen<br />

• Bestehen Operatoren oder Separatoren aus mehreren Zeichen (z.B. ++ -- ><br />

= == != && ||<br />

– Separatoren: ( ) [ ] { }<br />

, ; : ...<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

23


Konstanten<br />

• Zu den verschiedenen Basistypen in C gibt es Konstanten:<br />

– Integer-Konstanten<br />

– Fließkommakonstanten<br />

– Zeichenkonstanten<br />

– String-Konstanten (String ist kein Basistyp)<br />

– Aufzählungskonstanten (werden wie Konstanten behandelt)<br />

• Jede Konstante hat einen Typ und einen Wert.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

24


Ganzzahlige Konstanten (Integer)<br />

• Dezimale Angabe:<br />

Dezimalzahl ohne Vorzeichen und führende 0, gebildet aus den<br />

Dezimalziffern 0-9.<br />

<strong>Beispiel</strong>:<br />

5 19 32 /* Werte: 5, 19, 32 */<br />

• Oktale Angabe:<br />

Eine Oktalzahl (zur Basis 8) mit führender 0, gebildet aus den<br />

Oktalziffern 0-7.<br />

<strong>Beispiel</strong>:<br />

05 023 040 /* Werte: 5, 19, 32 */<br />

• Hexadezimale Angabe:<br />

Eine Hexadezimalzahl (zur Basis 16) mit führendem 0x oder 0X,<br />

gebildet aus den Hexadezimalziffern 0-9 und a-f oder A-F.<br />

<strong>Beispiel</strong>:<br />

0x5 0x13 0x20 /* Werte: 5, 19, 32 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

25


Typzusätze bei Integer-Konstanten<br />

Bei Integer-Typen wird noch zwischen verschiedenen „Untertypen“<br />

unterschieden (später dazu mehr im Detail). Um solche Konstanten<br />

angeben zu können, gibt es Zusätze bei Angabe einer Integer-Konstanten:<br />

u oder U für Integer-Konstanten ohne Vorzeichen (unsigned)<br />

l oder L für lange Integer-Konstanten (long)<br />

ll oder LL für sehr lange Integer-Konstanten (long long)<br />

<strong>Beispiel</strong>:<br />

10L /* Wert: long int 10 */<br />

10LL /* Wert: long long int 10 */<br />

10u /* Wert: unsigned int 10 */<br />

10ul /* Wert: unsigned long int 10 */<br />

10Ul /* Wert: unsigned long int 10 */<br />

10ull /* Wert: unsigned long long int 10 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

26


Integer-Konstanten<br />

Integer-Konstante<br />

Dezimalkonstante<br />

Oktalkonstante<br />

Int-Suffix<br />

Hexadezimalkonstante<br />

Int-Suffix<br />

u-Suffix<br />

ll-Suffix<br />

u-Suffix<br />

u<br />

U<br />

l-Suffix<br />

l-Suffix<br />

l-Suffix<br />

l<br />

u-Suffix<br />

L<br />

ll-Suffix<br />

Fachbereich Angewandte Informatik<br />

u-Suffix<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

ll-Suffix<br />

ll<br />

LL<br />

27


Integer-Konstanten<br />

Dezimalkonstante<br />

Ziffer ≠ 0<br />

Ziffer<br />

Oktalkonstante<br />

0<br />

oktale Ziffer<br />

Hexadezimalkonstante<br />

0<br />

x<br />

hexadezimale Ziffer<br />

X<br />

oktale Ziffer<br />

...<br />

0 7<br />

hexadezimale Ziffer<br />

... a ... f A ... F<br />

0 7<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

28


Typzusätze bei Integer-Konstanten<br />

Der Typ einer Integer-Konstanten ist der erste „passende“ Typ nach<br />

folgendem Schema (C99; in anderen C-Versionen anders!):<br />

Konstante<br />

Dezimalzahl ohne Anhang<br />

Oktal-/Hexadezimalzahl ohne<br />

Anhang<br />

Anhang u oder U<br />

Dezimalzahl mit Anhang l oder L<br />

Oktal-/Hexadezimalzahl mit l oder L<br />

Anhang sowohl u/U als auch l/L<br />

Dezimalzahl mit ll/LL<br />

Oktal-/Hexadezimalzahl mit ll/LL<br />

Anhang sowohl u/U als auch ll/LL<br />

Typ<br />

int, long, long long<br />

int, unsigned, long, unsigned long,<br />

long long, unsigned long long<br />

unsigned int, unsigned long,<br />

unsigned long long<br />

long, long long<br />

long, unsigned long, long long,<br />

unsigned long long<br />

unsigned long, unsigned long long<br />

long long<br />

long long, unsigned long long<br />

unsigned long long<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

29


<strong>Beispiel</strong><br />

<strong>Beispiel</strong>:<br />

Rechner, auf dem int=16 Bit, long=32 Bit (bei signed 1 Bit Vorzeichen).<br />

32000<br />

32000u<br />

33000<br />

int (-2 15 ≤ x ≤ 2 15 -1)<br />

unsigned int (0 ≤ x < 2 16 , ohne Vorzeichen-Bit)<br />

long (Umwandlung nach long, da als int nicht darstellbar)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

30


Fließkommakonstanten<br />

Es gibt drei reelle Fließkommatypen (float, double, long double).<br />

Fließkommakonstanten ohne Zusatz sind vom Typ double! Durch<br />

Anhängen des Zusatzes f oder F entsteht eine float-Konstante, durch<br />

Anhängen des Zusatzes l oder L eine Konstante vom Typ long double.<br />

Durch Einfügen von complex.h sind auch komplexe Konstanten<br />

möglich in der Form 1.0+1.0*I für die komplexe Zahl (1,1).<br />

<strong>Beispiel</strong>:<br />

3.14 /* double */<br />

3.1415926356f /* float */<br />

3.14L /* long double */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

31


Fließkommakonstanten<br />

• Dezimalzahl mit Punkt<br />

<strong>Beispiel</strong>:<br />

3.14 .5 3.<br />

• Ganzzahl mit Exponent e oder E:<br />

<strong>Beispiel</strong>:<br />

5E10 5e10 3e-1<br />

• Kombination aus beiden Alternativen<br />

<strong>Beispiel</strong>:<br />

0.31415e1 1.0e-10<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

32


Fließkommakonstanten<br />

Fließkommakonstante<br />

. Ziffer Exponent f-Suffix<br />

Ziffer<br />

Ziffer .<br />

Ziffer<br />

Exponent<br />

Exponent<br />

e<br />

E<br />

+<br />

-<br />

Ziffer<br />

f-Suffix<br />

f<br />

F<br />

l<br />

Hexadezimale Angaben ebenfalls möglich (hier nicht)<br />

L<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

33


Character-Konstanten<br />

• Eine Character-Konstante ist ein Zeichen des ausführbaren<br />

Zeichensatzes, das in Apostophe (keine Anführungszeichen!)<br />

eingeschlossen ist.<br />

• Ausnahmen sind (können durch Escape-Sequenzen dargestellt<br />

werden; s.u.):<br />

– Apostroph selber ′<br />

– Backslash \<br />

– Zeilenendezeichen<br />

• Der Wert einer Character-Konstanten ist die Codierung im<br />

verwendeten Zeichensatz. <strong>Beispiel</strong>: Das Leerzeichen hat im ASCII-<br />

Zeichensatz die Codierung 32, im EBCDIC-Zeichensatz die Codierung<br />

64.<br />

• Der Typ einer Character-Konstanten (ein Ausdruck) ist int!<br />

<strong>Beispiel</strong>:<br />

′a′ ′$′ ′.′<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

34


Escape-Sequenzen für Zeichen<br />

• ′\a′ Gong-Zeichen<br />

• ′\b′ Backspace<br />

• ′\f′ Seitenvorschub (Formfeed)<br />

• ′\n′ Newline<br />

• ′\r′ Zeilrücklauf (Carriage Return)<br />

• ′\t′ horizontaler Tabulator (Tab)<br />

• ′\v′ vertikaler Tabulator<br />

• ′\\′ Backslash \ selber<br />

• ′\′′ Apostroph-Zeichen<br />

• ′\“′ Anführungszeichen<br />

• ′\′ Fragezeichen<br />

• ′\ooo′ oktale Angabe des Zeichencodes<br />

(ooo sind 1-3 oktale Ziffern). <strong>Beispiel</strong>: ′\0′ für Codierung 0<br />

• ′\xh′ Hexadezimale Angabe des Zeichencodes<br />

h ist eine beliebige Anzahl von Hexadezimalziffern, die<br />

durch eine Nicht-Hexadezimalziffer beendet wird.<br />

<strong>Beispiel</strong>: ′x20′ steht im ASCII-Zeichensatz für das Leerzeichen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

35


Unterstützung großer Zeichensätze<br />

• Zur Zeit geringe Verbreitung<br />

• Hier nur am Rande erwähnt<br />

• Codierung von großen Zeichensätzen (z.B. Japanisch) über:<br />

– <strong>Beispiel</strong> für Multibyte-Character: ′abc′<br />

– <strong>Beispiel</strong> für Wide Character: L′a′<br />

• Universal Character Names (C99) in Character- oder String-Konstanten<br />

sowie Bezeichnern:<br />

– \u hex-digit 4<br />

– \U hex-digit 8<br />

– <strong>Beispiel</strong>: \u0024 ($)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

36


String-Konstanten<br />

• Ein String (Zeichenfolge) ist kein Basistyp in C.<br />

• In C wird ein String-Konstante angegeben als eine Folge von Zeichen in<br />

Anführungszeichen.<br />

• Escape-Sequenzen für Zeichen im String sind erlaubt.<br />

• Der Typ einer String-Konstanten ist ein Feld von Zeichen.<br />

• Wird eine String-Konstante über mehrere Zeilen definiert, so muss direkt<br />

vor einem Zeilenende ein Backslash \ stehen (siehe Präprozessor).<br />

• Stehen zwei Strings unmittelbar hintereinander (nur durch Leerzeichen,<br />

Zeilenende etc. getrennt), so fügt der Compiler diese beiden String-<br />

Konstanten zu einer String-Konstanten zusammen.<br />

<strong>Beispiel</strong>e:<br />

″String″<br />

″Auch \a Escape-Sequenzen sind erlaubt!″<br />

″Dieser String geht über \<br />

zwei Zeilen″<br />

″Hieraus erzeugt″ ″der Compiler″<br />

″einen String″<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

37


String-Konstanten<br />

• Für eine String-Konstante mit n Zeichen legt der Compiler automatisch<br />

einen Speicherbereich (char-Feld) der Länge n+1 an.<br />

• Die ersten n Zeichen enthalten die Zeichen des angegebenen Strings.<br />

• Das n+1-te Zeichen ist ein Null-Character (′\0′), das<br />

vereinbarungsgemäß jeden String in C abschließen muss!<br />

• So angelegte String-Konstanten dürfen nicht modifiziert werden!<br />

<strong>Beispiel</strong>e:<br />

″abc″ wird abgespeichert als (jedes Kästchen ist ein Byte):<br />

a b c \0<br />

"" wird abgespeichert als:<br />

\0<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

38


Aufzählungskonstanten<br />

• In C gibt es Aufzählungstypen, Mengen von benannten, ganzzahligen<br />

Konstanten.<br />

<strong>Beispiel</strong>:<br />

enum Tage {Montag, Dienstag, Mittwoch, Donnerstag, Freitag,<br />

Samstag, Sonntag};<br />

• Der Typ einer Aufzählungskonstanten ist int!<br />

• Der Wert einer Aufzählungskonstanten ist die Position in der<br />

Aufzählungsmenge (bei 0 beginnend). Im <strong>Beispiel</strong> 0 für Montag, 1 für<br />

Dienstag usw.<br />

• Details später bei der Beschreibung von Aufzählungstypen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

39


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

40


Präprozessor<br />

• Übersetzt man ein Programm mit einem C-Compiler, so wird<br />

automatisch ein Präprozessor vorgeschaltet.<br />

• Präprozessor ist ein reiner Makroprozessor, der textuellen Ersatz<br />

vornimmt und eine neue Datei produziert, die an den eigentlichen C-<br />

Compiler weitergegeben wird!<br />

• Aufgaben des Präprozessors:<br />

– Einfügen weiterer Dateien innerhalb einer Datei (#include)<br />

– Bedingte Übersetzung (#if ...)<br />

– Makrodefinitionen und –ersetzung<br />

– Zusätzliche Compiler-Steuerung über #pragma<br />

• Gesteuert wird der Präprozessor über Direktiven, die alle mit einem #<br />

als erstem Nichtleerzeichen einer Zeile anfangen.<br />

• Direktiven müssen in eine logische Zeile passen. Längere Direktiven<br />

mit Backslash-Newline-Kombination über mehrere physikalische Zeilen<br />

möglich.<br />

• Zwischen # und Direktivenbezeichner können Leerzeichen stehen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

41


Ausführungsstufen des Präprozessors<br />

• Der Präprozessor durchläuft mehrere logische Stufen hintereinander.<br />

• In der Praxis führen Präprozessoren dies alles in einem Durchlauf aus.<br />

• Die logischen Stufen sind:<br />

– Ersetzen von Trigraph-Sequenzen<br />

– Löschen von Backslash-Newline-Kombinationen<br />

– Aufspalten des Programms in Grundelemente (Bezeichner, Zahlen, etc.),<br />

die durch Trennzeichen separiert sind. Kommentare werden logisch durch<br />

ein Leerzeichen ersetzt.<br />

– Ausführen von Präprozessor-Direktiven inkl. Makroersetzung<br />

– Ersetzen von Escape-Sequenzen in Character- und String-Konstanten.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

42


Einfügen von Dateien<br />

• Mittels der #include-Direktive kann man andere Dateien in den Code<br />

einfügen.<br />

• Schachtelung möglich, d.h. in eingefügten Dateien können wiederum<br />

#include-Direktiven stehen.<br />

• Wird fast ausschließlich zum Einfügen von .h-Dateien genutzt.<br />

• Mehrere Formen möglich:<br />

– #include ″Dateiangabe″<br />

Die Datei wird relativ zum aktuellen Verzeichnis gesucht.<br />

– #include <br />

Die Datei wird an systemspezifischen Verzeichnissen gesucht. Auf Unix-<br />

Systemen in /usr/include und evtl. /usr/local/include.<br />

– #include Makroname 1 ...Makroname n<br />

Nach ihrer Ersetzung sollten die Makronamen eine der beiden obigen<br />

Formen ergeben.<br />

• Die Dateiangabe ist relativ zum jeweiligen Bezugspunkt, d.h. Angaben<br />

wie ../Datei.h sind möglich.<br />

• Der Bezugspunkt wird mit Pfadangaben in den Dateiangaben<br />

verändert.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

43


Einfügen von Dateien<br />

<strong>Beispiel</strong> 1:<br />

#include ″test.h″ /* im lokalen Verzeichnis */<br />

#include ″../test2.h″ /* im Vaterverzeichnis des lokalen Verzeichnisses */<br />

<strong>Beispiel</strong> 2:<br />

#include /* wird zuerst als /usr/include/stdlib.h gesucht */<br />

<strong>Beispiel</strong> 3:<br />

Datei test.c:<br />

#include ″../test.h″ /* Im Vaterverzeichnis suchen */<br />

Datei ../test.h:<br />

#include ″test1.h″ /* Im Vaterverzeichnis weitersuchen nach test1.h */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

44


<strong>Beispiel</strong> für den Einsatz<br />

Datei prog.c:<br />

#include <br />

#include ″mydefs.h″<br />

int main(int argc, char **argv)<br />

{<br />

double umfang, radius, flaeche;<br />

}<br />

radius = 20.0;<br />

umfang = 2 * PI * radius;<br />

flaeche = flaechenberechnung(radius);<br />

Datei mydefs.h:<br />

/* Makrodefinition */<br />

#define PI 3.1415<br />

/* Funktionsdefinition (Typangabe) */<br />

double flaechenberechnung (double);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

45


Makro<br />

• Makros dienen:<br />

– der Abkürzung häufig benutzter Zeichenfolgen<br />

– der Definition logischer Konstanten (Konsistenz im Programm)<br />

– dem „Umdefinieren“ existierender Namen<br />

– der bedingten Übersetzung von Programmteilen<br />

• Makros gibt es mit und ohne Parameter<br />

• Nach einer Makrodefinition wird jedes Erscheinen dieses<br />

Makronamens im weiteren Text durch den Ersatztext textuell (!)<br />

ersetzt.<br />

• Im Ersatztext können selbst wieder Makros verwendet werden.<br />

• Prinzipiell lassen sich z.B. auch reservierte Wörter umdefinieren -><br />

schlechter Programmierstil.<br />

• Makronamen werden vereinbarungsgemäß aus Großbuchstaben<br />

gebildet.<br />

• Mit der Compiler-Option –E kann man sich (auf Standardausgabe) den<br />

Programmtext nach Durchlauf des Präprozessors anschauen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

46


Makrodefinition ohne Parameter<br />

Definition eines parameterlosen Makros:<br />

#define Makroname Ersatztext<br />

Makroname muss ein gültiger Bezeichner sein. Ersatztext kann ein beliebiger<br />

Text sein, insbesondere auch leer. Der Ersatztext fängt direkt hinter dem<br />

Makronamen an und geht bis zum Ende der (logischen) Zeile.<br />

<strong>Beispiel</strong>:<br />

#define PI 3.1415<br />

#define VEKTOR_DIM 100<br />

#define MATRIX_GROESSE (VEKTOR_DIM * VEKTOR_DIM)<br />

double matrix[MATRIX_GROESSE];<br />

/* wird textuell ersetzt durch:<br />

double matrix[(100 * 100)];<br />

*/<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

47


Makrodefinition mit Parameter<br />

Definition eines Makros mit Parametern:<br />

#define Makroname(p 1<br />

,...,p n<br />

) Ersatztext<br />

Die öffnende Klammer hinter dem Makronamen muss unmittelbar folgen,<br />

sonst wäre dies ein Makrodefinition ohne Parameter. p 1<br />

,...,p n<br />

sind formale<br />

Parameter. Jedes weitere Vorkommen des Makronames im Text bewirkt<br />

Folgendes:<br />

1. Identifizierung der aktuellen Parameter des Makroaufrufes, die durch<br />

Kommas getrennt sind<br />

2. Ersetzen weiterer Makroaufrufe in diesen aktuellen Parametern<br />

3. Ersetzen der formalen Parameter durch die aktuellen Parameter<br />

4. Ersetzen des Makroaufrufes durch den Ersatztext mit den aktuellen<br />

statt den formalen Parametern.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

48


<strong>Beispiel</strong>e zu Makrodefinition mit Parameter<br />

#define SQUARE(x) x*x<br />

#define LOOP(start, test, inc, body) \<br />

for (start ; inc; test) \<br />

body;<br />

int main(int argc, char **argv)<br />

{<br />

int i,j;<br />

LOOP( i=0 , i < 100 , ++i , j = SQUARE(j) )<br />

}<br />

Nach Makroexpansion:<br />

int main(int argc, char **argv)<br />

{<br />

int i,j;<br />

for ( i = 0; i < 100; ++i)<br />

j = j*j;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

49


Potentielle Fehler<br />

<strong>Beispiel</strong> 1: Prioritäten von Operatoren<br />

#define SQUARE(x) x * x<br />

b = SQUARE(a+1);<br />

Erzeugter Code:<br />

b = a+1 * a+1<br />

<strong>Beispiel</strong> 2: Nebeneffekte<br />

#define SQUARE(x) ((x) * (x))<br />

b = SQUARE(a++);<br />

Erzeugter Code:<br />

b = ((a++) * (a++));<br />

Tips:<br />

1. Großzügig im Ersatztext Klammern verwenden. Jedes Vorkommen<br />

eines Argumentes im Ersatztext klammern.<br />

2. Als aktuelle Argumente von Makroaufrufen nur Ausdrücke ohne<br />

Nebeneffekte verwenden.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

50


Makrodefinition mit variabler Parameteranzahl<br />

Definition eines Makros mit einer festen Anzahl (kann auch 0 sein) von<br />

Parametern und möglichen weiteren Parametern:<br />

#define Makroname(p 1<br />

,p 2,<br />

... ) Ersatztext<br />

Mit ... werden syntaktisch alle nachfolgenden Parameter bezeichnet, die<br />

bei einem Aufruf übergeben werden. Im Ersatztext kann man auf diese<br />

Parameter mit __VA_ARGS__ zugreifen<br />

<strong>Beispiel</strong>:<br />

#if defined(DEBUG)<br />

#define my_printf(...) fprintf(stderr, __VA_ARGS__)<br />

#else<br />

#define my_printf(...) printf(__VA_ARGS__)<br />

#endif<br />

...<br />

my_printf("Hier kracht es, weil i=%d\n", i);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

51


undef eines Makros<br />

Oft ist es wünschenswert, dass ab einem bestimmten Punkt im<br />

Programm ein Makro nicht länger definiert sein soll. Dazu gibt es die<br />

Direktive<br />

#undef Makroname<br />

Ab diesem Punkt ist kein Makro unter Makroname mehr bekannt.<br />

<strong>Beispiel</strong>:<br />

#define TEST_CODE<br />

...<br />

#undef TEST_CODE<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

52


Bedingte Übersetzung<br />

Mit Hilfe des Präprozessors ist es möglich, nur bestimmte Teile des<br />

Programms übersetzen zu lassen. Dies ist z.B. dann nützlich, wenn man<br />

aus einem Source-Programm verschiedene Versionen erzeugen möchte,<br />

z.B. für Unix und Windows.<br />

<strong>Beispiel</strong>:<br />

#if defined(UNIX)<br />

# include <br />

#elif defined(WINDOWS)<br />

# include <br />

#endif<br />

Durch Definieren des Makros UNIX bzw. WINDOWS werden<br />

unterschiedliche Teile des Programms vom Präprozessor an den<br />

eigentlichen Compiler weitergereicht. Mit der Compiler-Option<br />

–DMakroname<br />

kann man gezielt Makros auch von der Kommandozeile definieren.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

53


Direktiven zur bedingten Übersetzung<br />

• #if const_expr<br />

Das nächste Programmstück bis zur nächsten bedingten Direktive auf<br />

gleichem Niveau wird nur übersetzt, wenn const_expr (ein konstanter<br />

Integer-Ausdruck) ungleich 0 ist.<br />

• #ifdef Makroname<br />

Übersetzen, wenn Makroname an dieser Stelle bekannt ist.<br />

• #ifndef Makroname<br />

Übersetzen, wenn Makroname an dieser Stelle nicht bekannt ist.<br />

• #elif const_expr<br />

Nachfolgendes bis zur nächsten bedingten Direktive übersetzen, falls<br />

vorangehende bedingte Direktiven auf gleichem Niveau nicht genommen<br />

wurde und const_expr ungleich 0 ist.<br />

• #else<br />

Bis zum entsprechenden #endif übersetzen, falls keine vorangehende<br />

bedingte Direktive auf gleichem Niveau genommen wurde.<br />

• #endif<br />

Abschließende Direktive eines bedingten Blocks.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

54


<strong>Beispiel</strong>e<br />

<strong>Beispiel</strong> 1:<br />

#define UNIX<br />

#ifdef UNIX<br />

# include <br />

#else<br />

# include „meinedatei.h“<br />

#endif<br />

<strong>Beispiel</strong> 2:<br />

#if 0<br />

Programmstück, das nicht<br />

übersetzt werden soll<br />

#endif<br />

<strong>Beispiel</strong> 3:<br />

#if !defined(TEST_H_INCLUDED)<br />

# define TEST_H_INCLUDED<br />

# include <br />

...<br />

#endif<br />

<strong>Beispiel</strong> 4:<br />

#define DEBUG 2<br />

#if (DEBUG > 0)<br />

printf(″Kontrollausgabe″);<br />

# if (DEBUG > 1)<br />

printf(″Mehr Details″);<br />

# endif<br />

#endif<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

55


Sonstige Direktiven<br />

• #pragma Compilerpragma<br />

Compiler-abhängige Informationen können an den Compiler übergeben<br />

werden<br />

• #<br />

Leere Direktive ohne Effekt.<br />

• #line Zeilennummer [″Dateiname″]<br />

Nützlich im Zusammenhang mit Programmierwerkzeugen, die<br />

Programmtransformationen ausführen (Rückbezug zur Originaldatei).<br />

• #error token<br />

Compiler gibt eine Meldung aus, die aus token besteht.<br />

<strong>Beispiel</strong> 1:<br />

#pragma optimize<br />

#pragma 27 ″orignaldatei.c″<br />

<strong>Beispiel</strong> 2:<br />

#if defined(SYSTEM)<br />

...<br />

#else<br />

#error ″das sollte nicht passieren″<br />

#endif<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

56


Makro-Operatoren<br />

• defined(Makroname)<br />

Liefert 1, falls Makroname an diesem Punkt bekannt ist, ansonsten 0.<br />

• #<br />

Der Präfixoperator kann nur im Zusammenhang mit #define<br />

verwendet werden. Er bewirkt das Einschließen des nachfolgenden<br />

Argumentes in Anführungszeichen.<br />

<strong>Beispiel</strong>:<br />

#define tempfile(dir) #dir<br />

name = tempfile(/usr/include);<br />

erzeugt<br />

name = ″/usr/include″;<br />

• ##<br />

Der Infixoperator bewirkt eine Konkatenation beider Operanden.<br />

<strong>Beispiel</strong>:<br />

#define cat(x,y) x ## y<br />

cat(var, 123)<br />

erzeugt<br />

var123<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

57


Vordefinierte Makros<br />

• __LINE__<br />

Dezimalkonstante mit der aktuellen Zeilennummer während der<br />

Übersetzung.<br />

• __FILE__<br />

String-Konstante mit dem aktuellen Dateinamen während der<br />

Übersetzung.<br />

• __DATE__<br />

String-Konstante mit dem aktuellen Datum der Übersetzung.<br />

• __TIME__<br />

• String-Konstante mit dem aktuellen Zeitpunkt der Übersetzung.<br />

• __STDC__<br />

Liefert 1, wenn der Compiler konform zum ANSI-Standard ist.<br />

<strong>Beispiel</strong>:<br />

printf(″Programmversion 1.0, uebersetzt am %s um %s″, __DATE__, __TIME__);<br />

if( i == j )<br />

printf(″In Zeile %d in Datei %s stimmt was nicht″, __LINE__, __FILE__);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

58


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

59


Anweisungen<br />

• Anweisungen steuern Kontrollfluss<br />

• Jede Anweisung wird durch ein Semikolon abgeschlossen<br />

(Ausnahme: zusammengesetzte Anweisung)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

60


Leere Anweisung<br />

Syntax:<br />

;<br />

Beschreibung:<br />

Wird üblicherweise dort eingesetzt, wo aus syntaktischen Gründen eine<br />

Anweisung erforderlich ist.<br />

<strong>Beispiel</strong>:<br />

for ( i = 0; (i < 100) && (a[i] != 5); ++i)<br />

;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

61


Ausdrucksanweisung<br />

Syntax:<br />

Ausdruck ;<br />

Beschreibung:<br />

Durch Anhängen eines Semikolons an einen Ausdruck (z.B. 3+4) erhält<br />

man eine Ausdrucksanweisung. Der Wert des Ausdrucks wird berechnet<br />

und das Ergebnis ignoriert. Eine Ausdrucksanweisung macht nur Sinn,<br />

wenn in dem Ausdruck Nebenwirkungen vorhanden sind.<br />

<strong>Beispiel</strong>:<br />

/* Ein Funktionsaufruf ist ist ein Ausdruck */<br />

myfun(10);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

62


Sprungzielanweisung<br />

Syntax:<br />

Bezeichner : Anweisung<br />

Beschreibung:<br />

Jeder Anweisung kann ein Label vorangestellt werden, wobei ein Label<br />

ein Bezeichner gefolgt von einem Doppelpunkt ist. Ein Label kann nur<br />

über eine goto-Anweisung (später) angesprochen werden. Ausnahme:<br />

Label im Zusammenhang mit switch-Anweisungen (später). Ein Label-<br />

Bezeichner kann auch schon vor seiner Definition benutzt werden (d.h.<br />

goto Bezeichner). Der Gültigkeitsbereich ist die umschließende<br />

Funktion.<br />

<strong>Beispiel</strong>:<br />

/* Das Label heißt hier Fehler */<br />

Fehler: i = 2;<br />

...<br />

goto Fehler;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

63


Zusammengesetzte Anweisung<br />

Syntax:<br />

{<br />

Deklaration }<br />

Anweisung<br />

Beschreibung:<br />

Oft bezeichnet man dies auch als Block. In C89 muss die Reihenfolge<br />

zwingend eingehalten werden: Zuerst alle Deklarationen, dann erst<br />

Anweisungen.<br />

<strong>Beispiel</strong>:<br />

int main(int argc, char **argv)<br />

{<br />

int i;<br />

i = 2;<br />

{ /*


while-Schleife<br />

Syntax:<br />

while ( Ausdruck ) Anweisung<br />

Beschreibung:<br />

Die Anweisung im Schleifenrumpf wird solange ausgeführt, solange der<br />

Ausdruck Ausdruck wahr ist (d.h. ungleich 0). Alternative<br />

Abbruchmöglichkeiten werden später behandelt.<br />

<strong>Beispiel</strong>:<br />

int fun1(int arg)<br />

{<br />

int i;<br />

i = 1;<br />

while(arg != 0)<br />

{<br />

i = i * 2;<br />

arg = arg – 1;<br />

}<br />

return i;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

65


do-while-Schleife<br />

Syntax:<br />

do Anweisung while ( Ausdruck ) ;<br />

Beschreibung:<br />

Die Anweisung im Schleifenrumpf wird solange ausgeführt, bis der<br />

Ausdruck Ausdruck falsch ist (d.h. gleich 0). Im Gegensatz zur while-<br />

Schleife wird die Anweisung im Schleifenrumpf mindestens einmal<br />

ausgeführt. Alternative Abbruchmöglichkeiten werden später behandelt.<br />

<strong>Beispiel</strong>:<br />

int fun2(int arg)<br />

{<br />

int i = 1;<br />

do {<br />

i = i * arg;<br />

arg = arg – 1;<br />

} while(arg != 0);<br />

return i;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

66


for-Schleife<br />

Syntax:<br />

for ( init ; expr2 ; expr3 ) Anweisung<br />

Beschreibung:<br />

Zuerst wird init ausgewertet und das Resultat ignoriert. Dann wird expr2<br />

ausgewertet. Falls der Wert falsch (d.h. 0) ist, bricht die Schleife ab. Ansonsten<br />

wird die Anweisung ausgeführt und vor dem nächsten Test von expr2 wird<br />

expr3 ausgewertet und das Resultat ignoriert. Alle Angaben init, expr2, expr3<br />

sind optional und können weggelassen werden. init kann ein Ausdruck oder<br />

eine Deklaration sein.<br />

<strong>Beispiel</strong>:<br />

int fun3(int arg)<br />

{<br />

for ( int i = 1; arg != 0; arg = arg – 1 )<br />

i = i * arg;<br />

return i;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

67


goto-Anweisung<br />

Syntax:<br />

goto Bezeichner ;<br />

Beschreibung:<br />

Es wird zu dem angegeben Bezeichner eines Labels verzweigt. Springt<br />

man in einen Block hinein, so wird zwar Speicherplatz für die Blocklokalen<br />

Variablen angelegt, jedoch werden keine<br />

Initialisierungsausdrücke für diese Variablen ausgewertet!<br />

<strong>Beispiel</strong>:<br />

int fun4(int a[100], int suchwert)<br />

{<br />

int i;<br />

for ( i = 1; i < 100; i = i + 1 )<br />

if ( a[i] == suchwert)<br />

goto gefunden;<br />

}<br />

gefunden: return i;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

68


eak-Anweisung<br />

Syntax:<br />

break ;<br />

Beschreibung:<br />

Mit der break-Anweisung verlässt man die innerste Iterations- (for, while,<br />

do-while) oder switch-Anweisung (später).<br />

<strong>Beispiel</strong>:<br />

int fun5(int a[100], int suchwert)<br />

{<br />

int i;<br />

for ( i = 1; i < 100; i = i + 1 )<br />

if ( a[i] == suchwert)<br />

break;<br />

}<br />

return i;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

69


continue-Anweisung<br />

Syntax:<br />

continue ;<br />

Beschreibung:<br />

Mit der continue-Anweisung bricht man innerhalb einer Iterations-<br />

Anweisung (for, while, do-while) die aktuelle Iteration ab und verzweigt<br />

zum Ende des Schleifenrumpfes, bricht aber nicht die Schleife ab. D.h.<br />

es würde in einer for-Schleife als nächstes wieder überprüft, ob die<br />

Abbruchbedingung erfüllt ist.<br />

<strong>Beispiel</strong>:<br />

double logsum( double a[100], int max ) {<br />

int i;<br />

double sum = 0.0;<br />

for ( i = 0; i < max; i = i + 1 ) {<br />

if ( a[i] == 0.0 )<br />

continue;<br />

sum += log(a[i]);<br />

}<br />

return sum;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

70


eturn-Anweisung<br />

Syntax:<br />

return Ausdruck ;<br />

Beschreibung:<br />

Die return-Anweisung bewirkt ein sofortiges Verlassen der Funktion und<br />

Rückkehr zur aufrufenden Funktion. Der Ausdruck Ausdruck ist optional.<br />

Falls man den Ausdruck weglässt, muss die Funktion vom Resultattyp<br />

void sein.<br />

<strong>Beispiel</strong>:<br />

int const5( void )<br />

{<br />

return 5;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

71


if-Anweisung<br />

Syntax:<br />

if ( Ausdruck ) Anweisung1 else Anweisung2 ;<br />

Beschreibung:<br />

Zuerst wird Ausdruck ausgewertet. Ist der Ausdruck wahr (d.h. ungleich 0), wird<br />

Anweisung1, ansonsten Anweisung2 ausgeführt. Die Angabe des else-Zweiges<br />

ist optional.<br />

<strong>Beispiel</strong>:<br />

int fun6( int arg )<br />

{<br />

if ( arg < 0)<br />

return –1;<br />

else<br />

return 1;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

72


if-Anweisung<br />

Durch eine Schachtelung von if-Anweisungen kann es zu<br />

Mehrdeutigkeiten kommen. Im Fall<br />

if ( arg1 < 0)<br />

if ( arg2 == 0 )<br />

return 0<br />

else<br />

return 1;<br />

Worauf bezieht sich das letzte else In C (und anderen Sprachen) ist es<br />

so definiert, dass sich ein else immer auf das letzte offene if bezieht.<br />

Man sollte in solchen Fällen der besseren Lesbarkeit einen Block mit {}<br />

einführen:<br />

if ( arg1 < 0)<br />

{<br />

if ( arg2 == 0 )<br />

return 0<br />

else<br />

return 1;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

73


switch-Anweisung<br />

Syntax:<br />

switch ( Ausdruck ) Anweisung<br />

und in diesem Zusammenhang:<br />

case const_expr :<br />

default :<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

74


switch-Anweisung<br />

Die allgemeine Anwendungsform einer switch-Anweisung ist:<br />

switch (Ausdruck )<br />

{<br />

case Ausdruck_1:<br />

}<br />

....<br />

case Ausdruck_n:<br />

default:<br />

Anweisung_1;<br />

break;<br />

Anweisung_n;<br />

break;<br />

Anweisung_n+1;<br />

Der Ausdruck Ausdruck wird ausgewertet. Dann werden nacheinander<br />

die konstanten Ausdrücke Ausdruck_1,... ausgewertet, bis Ausdruck<br />

gleich einem Ausdruck_i ist. In diesem Fall wird Anweisung_n<br />

ausgeführt und mit der anschließenden break-Anweisung die switch-<br />

Anweisung verlassen.<br />

Trifft kein Fall zu und ist ein default-Label vorhanden, so wird diese<br />

Anweisung ausgeführt. Ist nach einer Anweisung_i keine break-<br />

Anweisung vorhanden, wird mit Anweisung_i+1 weitergemacht!<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

75


<strong>Beispiel</strong><br />

void fehlermeldung ( int fehlercode )<br />

{<br />

switch ( fehlercode )<br />

{<br />

case 1: printf("Fehler kritisch");<br />

break;<br />

case 2: printf("Fehler fatal");<br />

break;<br />

default: printf("Fehler ignorieren");<br />

}<br />

}<br />

switch ( fehlercode )<br />

{<br />

case 1:<br />

case 2: exit(EXIT_FAILURE);<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

76


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

77


Funktionen<br />

• (später im Detail mehr)<br />

• Funktionsdefinitionen bestehen aus<br />

– Funktionskopf (header), der die Schnittstelle angibt<br />

– Rumpf (body), der die Berechnungsvorschrift angibt<br />

• Ein Funktionsprototyp spezifiziert lediglich die Schnittstelle und enthält<br />

statt einem Rumpf ein Semikolon. Funktionsprototypen ermöglichen<br />

u.a. dem Compiler Typüberprüfungen und automatische<br />

Typumwandlungen bei Funktionsaufrufen.<br />

// header<br />

int myfun ( int arg1 )<br />

{<br />

// body<br />

return arg1 + 1;<br />

}<br />

// Funktionsprototyp<br />

int myfun ( int arg1 ) ;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

78


Funktionen<br />

• In C ausschließlich call-by-value Parameterübergabe<br />

• Ändern von Variablen in aufrufender Funktion kann durch Übergabe<br />

von Zeigerwerten (Adressen der Variablen) erreicht werden<br />

void swap (int *x, int *y)<br />

{<br />

// tausche den Inhalt an den beiden Adressen,<br />

// die durch x und y angegeben sind<br />

int tmp = *x;<br />

*x = *y;<br />

*y = tmp;<br />

}<br />

int swaptest()<br />

{<br />

int i=1, j=2;<br />

// rufe swap mit den Adressen der beiden Variablen i und j auf<br />

swap(&i, &j);<br />

printf("i=%d, j=%d\n", i,j); // Ausgabe: i=2, j=1<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

79


Hauptprogramm<br />

• Für jedes ausführbare Programm muss eine Funktion mit Namen main<br />

existieren, das der Loader als Programmstart aufruft<br />

• Funktionsprototyp:<br />

int main ( int argc, char ** argv );<br />

• Der Funktionswert von main ist ein int. Dieser Wert wird an das<br />

Betriebssystem zurückgegeben und man kann z.B. diesen Wert in<br />

einer Shell abfragen. Es gibt zwei logische Konstanten (in ),<br />

EXIT_SUCCESS und EXIT_FAILURE, die man in Zusammenhang mit<br />

einer return-Anweisung im Hauptprogramm nutzen kann, um einen<br />

fehlerfreien bzw. mit Fehlern behafteten Programmablauf zu<br />

kennzeichnen.<br />

• Über den Wert argc kann man abfragen, wie viele Argumente (z.B. in<br />

der Kommandozeile) an das Programm übergeben wurden. In argv<br />

stehen als Strings die Argumente.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

80


<strong>Beispiel</strong><br />

Programm:<br />

#include <br />

int main ( int argc, char **argv )<br />

{<br />

int i;<br />

}<br />

for ( i=0; i < argc; ++i )<br />

printf (″Argument %d war %s\n″, i, argv[i] );<br />

Aufruf aus der Kommandozeile:<br />

Prompt> a.out test1 4711 test4<br />

Argument 0 war a.out<br />

Argument 1 war test1<br />

Argument 2 war 4711<br />

Argument 3 war test4<br />

Prompt><br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

81


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

82


Typen<br />

• Typen sind Wertemengen mit den darauf definierten Operationen.<br />

• Jedes Objekt in C (Konstante, Variable, Funktion) hat einen Typ.<br />

• Durch Typüberprüfung kann der Compiler viele Fehler erkennen.<br />

• Typumwandlung zwischen manchen Typen möglich<br />

• Basistypen:<br />

– Integer-Typen<br />

– Character-Typen<br />

– _Bool<br />

– Aufzählungstypen<br />

– Fließkommatypen<br />

• Leerer Typ<br />

• Zusammengesetzte Typen<br />

– Zeigertypen<br />

– Feldtypen<br />

– Strukturtypen<br />

– Vereinigungstypen<br />

– Funktionstypen<br />

• Typzusätze<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

83


Einordnung von Typen<br />

C Typ<br />

Kategorie<br />

short, int, long, long long<br />

(signed und unsigned)<br />

char (signed und unsigned)<br />

integrale<br />

Typen<br />

arithmetische<br />

Typen<br />

skalare<br />

Typen<br />

_Bool<br />

enum {...}<br />

float, double, long double<br />

float/double/long double _Complex<br />

Fließkommatypen<br />

T *<br />

T [...]<br />

struct {...}<br />

union {...}<br />

T (...)<br />

void<br />

Zeigertypen<br />

Feldtypen<br />

Strukturtypen<br />

Vereinigungstypen<br />

Funktionstypen<br />

Typ void<br />

aggregierte<br />

Typen<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

84


Integer-Typen<br />

• Integer-Typen dienen zur Repräsentation von:<br />

– Ganzzahligen Werten mit und ohne Vorzeichen<br />

– Bit-Vektoren<br />

– Boolschen Werten (0=falsch, alles andere=wahr)<br />

– Character-Werten (Unterschied Character-Wert und Character-Typ!)<br />

• Integer-Typen mit Vorzeichen (Präfix signed optional):<br />

short int, int, long int, long long int<br />

alternative Schreibweise: short, long, long long<br />

• Integer-Typen ohne Vorzeichen (unsigned):<br />

unsigned short int, unsigned int, unsigned long int,<br />

unsigned long long int<br />

alternativ: unsigned short, unsigned long, unsigned long long<br />

• Character-Typen:<br />

char, signed char, unsigned char<br />

• Aufzählungstypen<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

85


Integer-Typen<br />

• Größenverhältnisse bzgl. Speicherbelegung und damit Wertebereich:<br />

– short int ≤ int ≤ long int<br />

– unsigned short int ≤ unsigned int ≤ unsigned long int<br />

• Es können auch alle drei Typen bzgl. Größe zusammenfallen<br />

• int entspricht im Allgemeinen der Wortlänge des Rechners<br />

• signed-Versionen sind gleich groß wie unsigned-Versionen<br />

• unsigned-Versionen unterscheiden sich von signed-Versionen durch<br />

die Interpretation des Speicherplatzes (Vorzeichenbit, größere positive<br />

Zahlen darstellbar mit unsigned)<br />

• Vermeiden Sie das Mischen/die Verwendung vieler verschiedener<br />

Integer-Typen (wann immer es geht: int)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

86


Mindestanforderungen an Wertebereich<br />

Die folgenden logischen Konstanten sind in definiert und geben<br />

jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen<br />

an die Wertebereiche der int-Typen sind dahinter angegeben:<br />

Konstante<br />

Mindestanforderung<br />

Bedeutung<br />

SHRT_MIN<br />

SHRT_MAX<br />

USHRT_MAX<br />

INT_MIN<br />

INT_MAX<br />

UINT_MAX<br />

LONG_MIN<br />

LONG_MAX<br />

ULONG_MAX<br />

LLONG_MAX<br />

-32767<br />

32767<br />

65535<br />

-32767<br />

32767<br />

65535<br />

-2 31<br />

2 31 -1<br />

2 32 -1<br />

2 63 -1<br />

Min. Wert von short<br />

Max. Wert von short<br />

Max. Wert von unsigned short<br />

Min. Wert von int<br />

Max. Wert von int<br />

Max. Wert von unsigned int<br />

Min. Wert von long<br />

Max. Wert von long<br />

Max. Wert von unsigned long<br />

Max. Wert von long long (Rest analog)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

87


Extended Integer Typen<br />

• Oft benötigt man zur korrekten Programmierung exakte<br />

Größenangaben zu einem Typ, z.B. int64 (wie in Java direkt definiert)<br />

• In C war das bisher nicht möglich!<br />

• Ausweg: Extended Integer Typen definiert in<br />

– inttypes.h<br />

– stdint.h<br />

• Dort sind zusätzliche (abgeleitete) int-Typen definiert.<br />

• Syntax dieser Typen:<br />

– intN_t und uintN_t, wobei N=8, 16, 32 oder 64<br />

(implementierungsabhängig auch mehr) für genau N Bits<br />

– int_leastN_t, uint_leastN_t analog für mindestens N Bits<br />

– int_fastN_t, uint_fastN_t analog für mindestens N Bits und<br />

möglichst schnell<br />

• <strong>Beispiel</strong>e:<br />

– int8_t // int, genau 8 Bits<br />

– uint16_t // unsigned int, genau 16 Bits<br />

– int_fast32_t // int, mindestens 32 Bits, schnell<br />

– uint_least64_t // unsigned int, mindestens 64 Bit<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

88


<strong>Beispiel</strong>e für Variablendeklarationen<br />

int x;<br />

unsigned int ux;<br />

unsigned long int ulx;<br />

signed int sx;<br />

short shx;<br />

signed short int shx2;<br />

uint64_t counter;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

89


Character-Typ<br />

• Character-Typen belegen genau ein Byte<br />

• Können genau ein Zeichen des Zeichensatzes aufnehmen<br />

• Es gibt char, signed char und unsigned char<br />

• Verwendet man ein Character-Objekt (Konstante, Variable) in einem<br />

Ausdruck, so ist der Wert des (Teil-)Ausdrucks ein int !<br />

Fehlerquelle: Manche Bibliotheksfunktione zum Einlesen von Zeichen<br />

liefern die logische Konstante EOF=-1 zurück. Weist man den<br />

Resultatwert (int) einer char-Variablen zu und testet anschließend die<br />

char-Variable, ob diese gleich EOF (int) ist, so kann es durch implizite<br />

Typumwandlungen zu unerwünschten Ergebnissen kommen.<br />

<strong>Beispiel</strong>:<br />

char c;<br />

signed char sc;<br />

unsigned char uc;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

90


Mindestanforderungen an Wertebereich<br />

Die folgenden logischen Konstanten sind in definiert und geben<br />

jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen<br />

an die Wertebereiche der char-Typen sind dahinter angegeben:<br />

Konstante<br />

CHAR_BIT<br />

SCHAR_MIN<br />

SCHAR_MAX<br />

UCHAR_MAX<br />

CHAR_MIN<br />

CHAR_MAX<br />

MBLEN_MAX<br />

Mindestanforderungen an Wert<br />

8<br />

-127<br />

127<br />

255<br />

0 oder SCHAR_MIN<br />

UCHAR_MAX oder SCHAR_MAX<br />

1<br />

Bedeutung<br />

Bits pro Byte<br />

Min. Wert signed char<br />

Max. Wert signed char<br />

Max. Wert unsigned char<br />

Min. Wert von char<br />

Max. Wert von char<br />

Max. Anzahl Byte in<br />

Multibyte-Character<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

91


Aufzählungstyp<br />

Ein Aufzählungstyp ist eine Anzahl von int-Werten, die jeweils mit einem<br />

Bezeichner benannt sind.<br />

Die Syntax zur Definition eines Aufzählungstyps in C ist:<br />

enum typ_name { const_def_list };<br />

wobei typ_name ein optionaler Typname ist und const_def_list eine durch<br />

Kommas getrennte Liste von Bezeichnern mit optionalen Wertangaben.<br />

Mit jedem Bezeichner in const_def_list wird ein int-Wert assoziiert, ohne<br />

weitere Angabe beginnend bei 0 und jeweils um 1 inkrementiert.<br />

<strong>Beispiel</strong>:<br />

enum Jahreszeiten { Fruehjahr, Sommer, Herbst, Winter, Karneval=5 };<br />

enum Jahreszeiten stuermisch; /* Variable stuermisch deklarieren */<br />

stuermisch = Herbst; /* Zuweisen eines Aufzählunswertes */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

92


Aufzählungstyp<br />

• Explizites Setzen eines Aufzählungswertes durch Bezeichner=Wert<br />

• Durch explizites Setzen eines Aufzählungswertes beginnt das<br />

Weiterzählen bei diesem Wert.<br />

• Es ist erlaubt, dass zwei Bezeichner den gleichen Wert haben.<br />

• Bereits definierte Bezeichner kann man als Wertangabe verwenden.<br />

<strong>Beispiel</strong>:<br />

enum aufzaehl1 { eins=1, zwei = eins+1, fuenf=zwei*zwei+eins };<br />

enum aufzaehl2 { wert1=4, wert2=4, wert3, wert4};<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

93


Boolean Typ<br />

• Neu in C99<br />

• <br />

• Dort ist ein Makro bool definiert, das den Wert _Bool hat<br />

• _Bool mit Werten 0 und 1<br />

• Wird implementiert durch einen unsigned int Typ<br />

<strong>Beispiel</strong>:<br />

#include <br />

_Bool b1;<br />

bool b2;<br />

// b1 ist vom Typ _Bool<br />

// ditto b2<br />

bool less(int x, int y)<br />

{<br />

bool b = x < y;<br />

return b;<br />

}<br />

// Teste, ob x kleiner als y ist<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

94


Reelle Fließkommatypen<br />

• <strong>Dr</strong>ei reelle Fließkommatypen: float, double, long double<br />

• Größenverhältnis: float ≤ double ≤ long double<br />

• Alle Fließkommakonstanten ohne expliziten Typzusatz sind vom Typ<br />

double!<br />

• In C89 liefern die meisten mathematischen Bibliotheksfunktionen (sin<br />

etc.) einen double-Wert, auch wenn das Argument ein float war.<br />

<strong>Beispiel</strong>:<br />

float f1, f2;<br />

double d;<br />

long double ld;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

95


Mindestanforderungen an Wertebereich<br />

Die folgenden logischen Konstanten sind in definiert und geben<br />

jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen<br />

an die Wertebereiche der Fließkommatypen sind dahinter angegeben<br />

(DBL_ und LDBL_ Konstanten existieren ebenfalls):<br />

Konstante<br />

FLT_ROUNDS<br />

FLT_EPSILON<br />

FLT_DIGITS<br />

FLT_MIN<br />

FLT_MAX<br />

Mindestanforderung an<br />

Wert<br />

1e-5<br />

6<br />

1e-37<br />

1e37<br />

Bedeutung<br />

Rundungsart:<br />

0: nach 0 (Abschneiden)<br />

1: zum Nächsten<br />

2: nach +∞<br />

3: nach - ∞<br />

Minimales x, s.d. 1.0+x ≠1<br />

Anz. Dezimalziffern Genauigkeit<br />

Min. positive Zahl<br />

Max. darstellbare Zahl<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

96


Komplexe Fließkommatypen<br />

• Neu in C99<br />

• Typ _Complex mit den Prefixen float, double, long double<br />

• Komplexer Wert wird intern repräsentiert als Feld des Basistyps mit 2<br />

Elementen (reeller und imaginärer Teil)<br />

• #include <br />

• In complex.h ist ein Makro complex definiert, hinter dem _Complex<br />

steht (sollte man verwenden)<br />

<strong>Beispiel</strong>:<br />

float _Complex fc;<br />

double complex dc;<br />

long double complex ldc;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

97


Leerer Typ (void)<br />

• Der leere Typ void hat keine Werte und auf ihm sind keine<br />

Operationen definiert.<br />

• Er dient als Statthalter für nicht-existente Objekte.<br />

• Verwendung im Zusammenhang mit:<br />

<strong>Beispiel</strong>e:<br />

– Generischen Zeigern:<br />

void *malloc(size_t size);<br />

– Funktionsdefinition einer Funktion, die kein Resultat liefert:<br />

void f(int x);<br />

– Typangabe in einer parameterlosen Funktion:<br />

int f(void);<br />

void *ptr; /* Zeiger auf nichts, generischer Zeiger */<br />

void f1(int x); /* Funktion liefert keinen Ergebniswert */<br />

int f2(void); /* Funktion hat keine Parameter */<br />

int i;<br />

i = 3+4;<br />

(void) 3+4; /* explizites „Wegschmeißen“ eines Wertes */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

98


Zusammengesetzte Typen<br />

Zusammengesetzte Typen sind induktiv über den bisher definierten<br />

Basistypen definiert. Seien T, T 1<br />

,...,T n<br />

Typen. Dann sind in C auch Typen:<br />

1. Zeiger auf T<br />

2. Feld von T<br />

3. Struktur von T 1<br />

,...,T n<br />

4. Vereinigung von T 1<br />

,...,T n<br />

5. Funktion, die Argumenttypen T 1<br />

,...,T n<br />

hat und einen<br />

Resultattyp T besitzt<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

99


Zeigertyp<br />

• Zu einem Typ T (<strong>Beispiel</strong>: int) kann man einen Typ Zeiger auf T<br />

definieren (<strong>Beispiel</strong>: Zeiger auf int).<br />

• Syntax für Variablendeklaration: T *varname;<br />

• Zeiger = Adresse im (virtuellen) Adressraum<br />

• Ein Zeiger kann dann auf ein Objekt des Basistyps zeigen<br />

• Der spezielle Zeigerwert NULL (in definiert) gibt an, dass<br />

der Zeiger momentan auf kein Objekt zeigt.<br />

• Zeiger auf T1 und Zeiger auf T2 sind nicht typkompatibel!<br />

• Generischer Zeigertyp void * ist typkompatibel zu allen Zeigertypen,<br />

d.h. man kann einen Wert vom Typ void * zu einem beliebigen<br />

anderen Zeigertyp umwandeln und umgekehrt.<br />

• die zwei wichtigsten Operatoren für Zeiger sind:<br />

– Adressoperator &: Angewandt auf ein Objekt vom Typ T liefert es die<br />

Adresse dieses Objektes als Typ Zeiger auf T<br />

– Dereferenzierungsoperator *: Angewandt auf einen Zeigerwert p vom Typ<br />

Zeiger auf T liefert es den Wert vom Typ T, auf den der Zeiger zeigt (falls<br />

dies ein zulässiger Zeigerwert ist)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

100


<strong>Beispiel</strong><br />

int i, j;<br />

int *ptr_i;<br />

i = 5;<br />

ptr_i = NULL;<br />

ptr_i = &i;<br />

j = *ptr_i;<br />

*ptr_i = 7;<br />

ptr_i = &j;<br />

Adresse = 100 104 108<br />

i j ptr_i<br />

5 <br />

5 0<br />

5 100<br />

5 5 100<br />

7 5 100<br />

7 5 104<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

101


Feldtyp<br />

• Feld ist Ansammlung von Objekten gleichen Typs, die hintereinander<br />

im Speicher abgelegt sind<br />

• Für jeden Typ T (außer void und Funktionstypen) kann man einen Typ<br />

Feld von T definieren: T feldname[Dimensionsangabe]<br />

• Mehrdimensionale Felder möglich: T feldname[dim 1<br />

]...[dim n<br />

]<br />

• Feldelemente:<br />

– Indizierung über feld[index]<br />

– Index beginnt bei 0 bis dim-1<br />

– Bei mehrdimensionalen Feldern: feldname[index 1<br />

]...[index n<br />

]<br />

– Feldname[index 1<br />

,...,index n<br />

] ist syntaktisch erlaubt, bedeutet aber<br />

etwas vollkommen verschiedenes!<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

102


Speicherung von Feldern<br />

• Im Gegensatz zu Java wird in C ein zweidimensionales Feld nicht als<br />

Vektor von Zeigern implementiert, sondern alle Feldelemente<br />

hintereinander im Speicher abgelegt (d.h. dim1*dim2 Elemente).<br />

• Zeilenweise Abspeicherung, d.h. letzter Index ändert sich schneller.<br />

• Optimierung des Speicherzugriffs (und damit der Laufzeit) bei<br />

mehrdimensionalen Feldern: Letzten Index schneller laufen lassen!<br />

// C<br />

int x[2][3];<br />

// Zugriff<br />

for (int i=0; i


Dimensionsangaben<br />

• Explizite Dimensionsangabe in eckigen Klammern (Normalfall)<br />

<strong>Beispiel</strong>:<br />

int a[10]; /* int-Feld der Dimension 10 */<br />

int b[3][4]; /* int-Feld der Dimension 3x4 */<br />

• Implizite Dimensionsangabe durch Angabe eines Initialisierungswertes<br />

<strong>Beispiel</strong>:<br />

char str[] = "Init.wert"; /* char-Feld der Dim.10 (inkl \0) */<br />

int c[] = {1,2,3,4}; /* int-Feld der Dimension 4 */<br />

• Weglassen der Dimensionsangabe (nur innerste Dimension möglich<br />

bei mehrdimensionalen Feldern):<br />

– Bei formalen Funktionsparametern (Typ T wird automatisch in Zeiger<br />

auf T umgewandelt)<br />

– Objekt hat external linkage (später) und Definition geschieht anderswo<br />

– Feld wird im gleichen Modul (Datei mit allen includes) später deklariert<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

104


<strong>Beispiel</strong>e zum Weglassen der Dimensionsangabe<br />

/* Funktionsparameter */<br />

void myfun ( int a[] );<br />

/* external linkage */<br />

int d[]; /* in Datei x.c */<br />

int d[10]; /* in Datei y.c */<br />

/* gleiches Modul */<br />

int e[][4]; /* Matrix der Dimension x4 */<br />

int e[2][4]; /* Matrix der Dimension 2x4 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

105


Dimensionsangaben<br />

• Dimensionsangaben müssen zur Übersetzungszeit auswertbar d.h.<br />

konstant sein.<br />

• Ausnahmen (C99):<br />

– variable length arrays<br />

• Größe wird erst zur Laufzeit festgelegt<br />

• Nur für lokale Feldvariablen in Blöcken erlaubt (d.h. kein static oder extern<br />

möglich)<br />

• Feld wird jedes mal bei Eintritt in den Block angelegt am Deklarationspunkt<br />

und wird gelöscht, wenn der Block verlassen wird<br />

– Formale Feldparameter in Funktionen können durch vorangehende Parameter<br />

dimensioniert werden oder in Funktionsprototypen mit * angegeben werden<br />

/* Feldparameter mit variabler Länge */<br />

void myfun ( int n, int m, int a[n][m] )<br />

{<br />

/* Feld mit variabler Länge */<br />

int dim = n*n, b=m+5;<br />

int feld[dim];<br />

}<br />

Fachbereich Angewandte Informatik<br />

/* Funktionsprototyp */<br />

void yourfun ( int a[*][*] ) ;<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

106


Zusammenhang Felder-Zeiger<br />

Es besteht ein enger Zusammenhang zwischen Feldern und Zeigern.<br />

Schreibt man den Feldnamen in einem Ausdruck, dann wird dies implizit<br />

umgewandelt in einen Zeiger auf die Adresse des ersten Feldelementes<br />

(Startadresse des Feldes)<br />

<strong>Beispiel</strong>:<br />

int a[100];<br />

int *ptr;<br />

ptr = a;<br />

// ptr zeigt auf erstes Element von a, d.h. a[0]<br />

ptr = &(a[0]); // äquivalent zu letzter Zeile<br />

*ptr = 5; // ptr zeigt auf die absolute (virtuelle) Adresse 5<br />

*ptr = 7;<br />

// In die Speicherstelle 5 wird der Wert 7 geschrieben<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

107


Strukturtyp<br />

Mehrere benannte Komponenten lassen sich in C zu einem neuen<br />

Strukturtyp zusammenfassen. Die einzelnen Komponenten werden<br />

hintereinander im Speicher abgelegt, es können jedoch „Löcher“ im<br />

Speicher zwischen den einzelnen Komponenten entstehen (Padding).<br />

Syntax:<br />

struct Bezeichner { Komponentenliste };<br />

Der Bezeichner ist optional. Der Name des neu eingeführten Typs ist struct<br />

Bezeichner, d.h. inkl. struct. Die Komponenten haben die Form von<br />

Deklarationen, im Normalfall in der einfachen Form<br />

Typangabe Komponentenname ;<br />

Zur Definition von rekursiven Datenstrukturen kann man innerhalb der<br />

Komponentenliste schon auf den neueingeführten Typ T über einen<br />

Zeigertyp Zeiger auf T verweisen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

108


<strong>Beispiel</strong>e<br />

/* Typdefinition */<br />

struct complex<br />

{<br />

float re;<br />

float im;<br />

};<br />

/* Variablendeklarationen mit dem neuen Typen */<br />

struct complex complexe_variable = {3.14, 2.71},<br />

*ptr_cvar = &complexe_variable;<br />

complexe_variable<br />

ptr_cvar<br />

re<br />

im<br />

3.14<br />

2.71<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

109


<strong>Beispiel</strong>e<br />

/* Typdefinition */<br />

struct complex<br />

{<br />

float re;<br />

float im;<br />

};<br />

/* "Konstruktor" für diesen Datentyp in C */<br />

struct complex new_complex ( float re, float im )<br />

{<br />

struct complex c;<br />

}<br />

c.re = re;<br />

c.im = im;<br />

return c;<br />

// Zuweisung an die Komponenten der Struktur<br />

// Hier wird der Wert der lokalen Variablen c zurückgegeben!<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

110


<strong>Beispiel</strong>e<br />

/* Typdefinition und Variablendeklaration in einem Schritt */<br />

struct Binaerbaum /* Typ struct Binaerbaum */<br />

{<br />

int daten;<br />

struct Binaerbaum *linker_Sohn; // Zeiger linker Sohn<br />

struct Binaerbaum *rechter_Sohn; // Zeiger rechter Sohn<br />

}<br />

Baum1, Baum2,<br />

// 2 Variablen dieses Typs<br />

*ptr_Baum;<br />

// 1 Variable mit Zeigertyp<br />

Baum1<br />

Baum2<br />

ptr_Baum<br />

daten<br />

linker_Sohn<br />

rechter_Sohn<br />

0<br />

0<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

111


Referenzierung der Komponenten<br />

Der Zugriff auf die einzelnen Komponenten einer Strukturvariablen<br />

erfolgt über<br />

Variablenname . Komponentenname<br />

<strong>Beispiel</strong>:<br />

complexe_variable.re = 1.0;<br />

complexe_variable.im = 0.0;<br />

Für Variablen, die den Typ Zeiger auf Struktur haben, gibt es eine<br />

abkürzende Schreibweise (diese wird praktisch nur verwandt!). Statt<br />

(*Variablenname).Komponentenname:<br />

Variablenname -> Komponentenname<br />

<strong>Beispiel</strong>:<br />

ptr_cvar->re = 1.0;<br />

(*ptr_cvar).re = 1.0;<br />

// äquivalent zu letzter Zeile<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

112


Weiteres <strong>Beispiel</strong><br />

struct Binaerbaum baum1 = {1, NULL, NULL},<br />

baum2 = {2, NULL, NULL},<br />

baum3 = {3, NULL, NULL},<br />

ptr_baum = &baum1;<br />

Baum1<br />

Baum2<br />

Baum3<br />

ptr_Baum<br />

daten<br />

1<br />

2<br />

3<br />

linker_Sohn<br />

rechter_Sohn<br />

ptr_Baum->linker_Sohn = &Baum2;<br />

ptr_Baum->rechter_Sohn = &Baum3;<br />

Baum2<br />

2<br />

Baum1<br />

1<br />

Baum3<br />

3<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

113


Padding<br />

Auf vielen Rechner gibt es Einschränkungen bzw. Leistungseinbußen,<br />

wenn Daten bestimmten Basistyps nicht an Wortgrenzen im Speicher<br />

abgelegt sind (Alignment).<br />

<strong>Beispiel</strong>:<br />

float x;<br />

double y;<br />

Angenommen, ein float-Wert belegt 4 Bytes und ein double-Wert 8 Bytes.<br />

Würden die beiden Variablen hintereinander (ohne Lücke) im Speicher<br />

abgelegt, so würde die Variable y nicht auf einer Adresse abgelegt, die ein<br />

Vielfaches ihrer Größe ist.<br />

Aus diesem Grunde kann es sein, dass ein Compiler automatisch eine<br />

(kleinen) Zwischenraum zwischen den Komponenten einfügt.<br />

0 4<br />

8<br />

x frei y<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

114


Operationen auf Strukturen<br />

Es ist möglich<br />

1. Eine Strukturvariable einer anderen Strukturvariablen gleichen Typs<br />

zuzuweisen<br />

2. Dass eine Funktion eine Struktur als Ergebnistyp hat<br />

3. Dass eine Struktur als Parameter an eine Funktion übergeben wird<br />

Es ist nicht möglich zwei Strukturvariablen auf Gleichheit zu testen.<br />

Wieso<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

115


Bit-Felder<br />

Innerhalb einer Struktur, und nur dort, ist die Definition von Bit-Feldern<br />

erlaubt. Die Strukturkomponenten, die das Bit-Feld ausmachen, müssen<br />

einen der Typen int, unsigned int oder signed int haben.<br />

<strong>Beispiel</strong>:<br />

struct RX_opcode<br />

{<br />

unsigned int opcode:8; /* Operationscode */<br />

unsigned int R1:4; /* Register 1 */<br />

unsigned int X2:4; /* Segmentregister */<br />

unsigned int B2:4; /* Basisregister */<br />

unsigned int D2:12; /* Displacement */<br />

unsigned int :0; /* Alignment */<br />

};<br />

Hinter dem Typnamen der Komponenten steht der (optionale) Name der<br />

Komponenten gefolgt von einem Doppelpunkt und der Anzahl (nichtnegativ,<br />

konstant) der Bits. Ist die Anzahl 0, bewirkt dies das Ablegen einer<br />

eventuell folgenden Bit-Komponenten auf einer Wortgrenze.<br />

Maximalgröße eines Bit-Feldes: Größe eines int<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

116


Vereinigungstyp<br />

Die Syntax zur Definition eines Vereinigungstyps ist fast identisch mit der<br />

eines Strukturtyps, lediglich das Schlüsselwort ist union statt struct:<br />

union Bezeichner { Komponentenliste };<br />

Der Zugriff auf die Komponenten erfolgt ebenfalls durch<br />

Variablenname.Komponentenname<br />

In der Abspeicherung der Komponenten besteht jedoch ein grundlegender<br />

Unterschied. Beim struct werden die Komponenten hintereinander im<br />

Speicher abgelegt, beim union übereinander! Die Größe des<br />

Vereinigungstyps (d.h. die Ausdehnung im Speicher) ist die Größe der<br />

größten Komponenten.<br />

Ein Vereinigungstyp sollte nur eingesetzt werden, wenn dies wirklich<br />

erforderlich ist.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

117


<strong>Beispiel</strong><br />

struct Datum<br />

{<br />

enum {double_Datum, int_Datum } Datumtyp; /* 1. Komponente struct */<br />

union<br />

{<br />

double dwert;<br />

int iwert;<br />

} Datumwert; /* 2. Komponente des struct */<br />

} datum; /* Variable vom Typ struct Datum */<br />

datum.Datumtyp = int_datum;<br />

datum.Datumwert.iwert = 13657;<br />

datum.Datumtyp = double_datum;<br />

datum.Datumwert.dwert = 13657.0;<br />

/* Erlaubt, macht aber hier keinen Sinn */<br />

datum.Datumwert.dwert = 13657.0;<br />

... = datum.Datumwert.iwert;<br />

Adresse= 100 104<br />

Datumtyp<br />

dwert<br />

iwert<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

118


Funktionstyp<br />

Zu einem Typ T (außer Feld- oder selbst Funktionstyp) kann man einen<br />

Typ Funktion, die T als Resultat liefert konstruieren. Die "normale" Form<br />

dafür sieht aus (später mehr):<br />

<strong>Beispiel</strong>:<br />

Resultattyp Funktionsname ( Parameterangaben )<br />

int myfun (int x, float y);<br />

3 Operationen sind mit Funktionsbezeichnern möglich:<br />

• Aufruf der Funktion: myfun(1, 3.14);<br />

• Benutzen der Funktion als aktuellen Parameter: anderefun(1, myfun);<br />

• Zuweisung an Variable entsprechenden Typs: funvariable = myfun;<br />

Erscheint ein Funktionsname außerhalb eines Funktionsaufrufes, d.h. nicht<br />

myfun(...), so wird der Typ des Ausdrucks automatisch in Zeiger auf eine<br />

Funktion, die T liefert umgewandelt (siehe Operation 3 oben).<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

119


Funktionsdeklaration und -definition<br />

Unterschied zwischen Funktionsdefinitionen und -deklarationen:<br />

1) Funktionsdefinition:<br />

Es wird ein Objekt dieses Typs erzeugt (Speicher verbraucht).<br />

<strong>Beispiel</strong>:<br />

int square( int x )<br />

{<br />

return x*x;<br />

}<br />

2) Funktionsdeklaration:<br />

Es werden nur Angaben zum Typ gemacht, es wird kein Speicher<br />

verbraucht.<br />

<strong>Beispiel</strong>:<br />

int square( int x ) ;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

120


<strong>Beispiel</strong>e<br />

/* Funktion, die kein Argument hat und ein int-Resultat liefert */<br />

int fun1(void);<br />

/* Funktion, die ein int-Argument erwartet und kein Ergebnis liefert */<br />

void fun2( int x );<br />

/* Strukturtyp */<br />

struct complex {float x; float y;};<br />

/* Funktion, die 3 Argumente erwartet:<br />

- ein int<br />

- ein float<br />

- ein struct complex<br />

und einen Zeiger auf ein int als Ergebnis liefert.<br />

*/<br />

int * fun3(int x, float y, struct complex z);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

121


Typdeklaration<br />

Mit einer typedef-Deklaration kann man ein Synonym (Abkürzung) für einen<br />

anderen Typen einführen, es entsteht dadurch kein neuer Typ!<br />

Vorteile:<br />

• Komplizierte Typen sind lesbarer<br />

• Bedeutungsvolle Namen für Typen<br />

Die allgemeine Syntax ist:<br />

typedef Deklaration;<br />

Deklaration wird später besprochen. Im einfachen Fall sieht dies so aus:<br />

typedef Typ typedef-name ;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

122


<strong>Beispiel</strong>e<br />

/* Counter (der neu eingeführte Bezeichner) ist ein Synonym für int */<br />

typedef<br />

int /* Typangabe */<br />

Counter; /* neuer Name */<br />

/* Unter Complex ist der struct-Typ ab jetzt ansprechbar */<br />

typedef<br />

struct {float re; float im;} /* Typangabe */<br />

Complex ; /* neuer Name */<br />

Complex c;<br />

c.re = 5.0;<br />

c.im = 0.0;<br />

/* Hier wird es komplizierter und stimmt nicht mehr mit dem einfachen<br />

Schema "typedef Typ Name" überein. Hier wird der Name "Cfun"<br />

eingeführt, der ein Synonym ist für den Typ "Zeiger auf eine Funktion,<br />

die kein Argument nimmt und ein Complex als Resultat liefert.<br />

*/<br />

typedef Complex (*Cfun)(void);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

123


Typzusätze<br />

Typzusätze (type qualifier) helfen einem Compiler effizienten und korrekten<br />

Code in Spezialfällen zu erzeugen.<br />

Durch den Zusatz const gibt man an, dass auf dieses Objekt nur lesen<br />

zugegriffen wird.<br />

Durch den Zusatz volatile gibt man an, dass dieses Objekt auch<br />

außerhalb des Programms verändert werden kann (z.B. in parallelen<br />

Programmen). Die Position der Typzusätze in einer Definition oder<br />

Deklaration ist wichtig (s.u.)!<br />

Der Zusatz restrict teilt dem Compiler im Zusammenhang mit Zeigern mit,<br />

dass dieser Zeigerwert zur Zeit die einzige Möglichkeit ist, auf das Objekt<br />

zuzugreifen (d.h. kein anderer Zeiger zeigt auf das gleiche Objekt).<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

124


<strong>Beispiel</strong><br />

const int x = 5;<br />

// Wert von x wird nicht mehr modifiziert<br />

int fun(const int x) {...} // Argument x wird nicht verändert<br />

const int *const_ptr;<br />

int * const ptr_const;<br />

// Zeiger auf int-Konstante<br />

// konstanter Zeiger auf int<br />

volatile int x;<br />

// Synchronisationsvariable<br />

void synchronisieren(void)<br />

{<br />

/* Frage solange Wert ab, bis dieser (von außen) auf einen Wert ungleich<br />

0 gesetzt wird.<br />

*/<br />

while(x == 0)<br />

;<br />

}<br />

extern char * restrict ptr;<br />

void alloc(void)<br />

{<br />

ptr = malloc(sizeof(*ptr));<br />

}<br />

// kein anderer Zeiger zeigt auf das Objekt,<br />

// auf das ptr zeigt<br />

// nur ptr zeigt auf dieses Objekt<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

125


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

126


Deklarationen<br />

• Deklaration = Assoziation eines Bezeichners mit einem Objekt<br />

• Definition = Deklaration + Speicherbereich<br />

• Bezeichner für folgende Objekte möglich:<br />

– Variablen<br />

– Funktionen<br />

– Typen<br />

– Typbezeichner<br />

– Komponenten von Struktur- oder Vereinigungstypen<br />

– Aufzählungskonstanten<br />

– Anweisungslabel<br />

– Präprozessor-Makros<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

127


Deklarationen<br />

Deklarationen bestehen aus:<br />

1. Optionaler Speicherklassenangabe<br />

2. Optionaler Typangabe<br />

3. Einem Deklarator mit dem Namen des zu deklarierenden Objektes<br />

4. Optionale Initialisierungswerte für dieses Objekt (nur in Definition)<br />

<strong>Beispiel</strong>:<br />

static int x = 5;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

128


Programmhierarchie<br />

• Ein C-Programm kann aus mehreren Programmdateien bestehen<br />

• Eine Programmdatei (xyz.c) kann aus mehreren top-level-<br />

Deklarationen bestehen, in denen globale Variablen und Funktionen<br />

definiert werden<br />

• Jede Funktion kann Deklarationen von formalen Parametern der<br />

Funktion haben, sowie einen Funktionsrumpf<br />

• Jeder Funktionsrumpf kann mehrere evtl. geschachtelte Blöcke<br />

enthalten<br />

• Jeder Block besteht aus optionalen Deklarationen gefolgt von<br />

optionalen Anweisungen<br />

• Eine Übersetzungseinheit ist eine Datei inkl. aller eingefügten<br />

Headerdateien<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

129


Programmhierarchie<br />

C-Programm<br />

x.c y.c z.c<br />

Top-level Deklarationen:<br />

- globale Variablen<br />

- Typdeklarationen<br />

- Funktionsdefinitionen<br />

Formale Parameter<br />

Funktionsdefinition:<br />

Rumpf (äußerer Block)<br />

Innerer Block<br />

Fachbereich Angewandte Informatik<br />

int f ( int x, float y )<br />

{<br />

int i;<br />

i = x;<br />

{<br />

int j;<br />

j = i;<br />

}<br />

}<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

130


<strong>Beispiel</strong><br />

datei1.c<br />

datei2.c<br />

int i;<br />

// Variablendefinition<br />

int i;<br />

// Variablendefinition<br />

// Typdeklaration<br />

struct mystruct<br />

{<br />

int x;<br />

float y;<br />

};<br />

// Funktionsdefinition<br />

int main( int argc, char **argv )<br />

{<br />

int i = 0; // keine top-level Deklaration<br />

}<br />

printf("myfun1=%d\n", myfun(i);<br />

printf("myfun1=%d\n", myfun2(i));<br />

// Funktionsdefinition<br />

int myfun( int x )<br />

{<br />

return x+1;<br />

}<br />

int j;<br />

// Variablendefinition<br />

// Funktionsdefinition<br />

int myfun2( int x )<br />

{<br />

return x+2;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

131


Gültigkeitsbereich von Bezeichnern<br />

Bezeichner haben einen Gültigkeitsbereich (scope), innerhalb dessen die<br />

Deklaration aktiv (bekannt) ist. In C existieren 5 mögliche Gültigkeitsbereiche:<br />

1. Bezeichner einer top-level-Deklaration: vom Deklarationspunkt bis<br />

zum Ende der Datei<br />

2. Bezeichner in einer Deklaration von formalen Parametern einer<br />

Funktion: vom Deklarationspunkt bis zum Ende der Funktion bzw.<br />

des Funktionsprototypen<br />

3. Bezeichner zu Beginn eines Blocks: Deklarationspunkt bis Ende<br />

Block<br />

4. Anweisungslabel: innerhalb der umschließenden Funktion<br />

5. Makroname: vom #define bis zum Ende der Datei bzw. bis zu einem<br />

entsprechenden #undef<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

132


<strong>Beispiel</strong><br />

/* TEST_CODE ist bis zum Ende der Datei gültig */<br />

#define TEST_CODE<br />

/* global_var ist gültig bis zum Ende der Datei */<br />

int global_var;<br />

/* Parameter x ist nur innerhalb der Klammern gültig */<br />

double sin( double x );<br />

int main( int argc, char **argv )<br />

{<br />

/* local_var ist nur innerhalb dieses Blocks gültig */<br />

int local_var;<br />

}<br />

/* Das Label ende ist innerhalb dieser Funktion gültig */<br />

ende: ;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

133


Überladen von Bezeichnern<br />

In C existieren 5 Namensklassen. Ein Bezeichner x kann gleichzeitig in allen<br />

5 Klassen existieren. Aus dem Kontext ist definiert, welcher Bezeichner<br />

welcher Namensklasse gemeint ist.<br />

Die Nutzung des gleichen Bezeichners in mehr als einer Namensklasse ist<br />

schlechter Programmierstil!<br />

Die Namensklassen sind:<br />

1. Präprozessorname (nach Präprozessorphase nicht mehr)<br />

2. Anweisungslabel<br />

3. Name eines Struktur-, Vereinigungs-, Aufzählungstypen<br />

4. Komponente eines Struktur-, Vereinigungstypen<br />

5. Alle anderen Bezeichner<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

134


<strong>Beispiel</strong><br />

#define x y /* Präprozessorname */<br />

int main( int argc, char **argv)<br />

{<br />

struct x /* Name eines Strukturtyps */<br />

{<br />

int x; /* Name einer Komponenten */<br />

} x; /* Name einer Variablen */<br />

x: ; /* Label */<br />

}<br />

/* alles klar */<br />

x.x = 4;<br />

goto x;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

135


Sichtbarkeit<br />

Zusätzlich zum Gültigkeitsbereich eines Bezeichners und den<br />

Namensklassen kann es jedoch durch eine (erlaubte) Mehrfachverwendung<br />

des gleichen Bezeichners zu Konflikten kommen:<br />

<strong>Beispiel</strong>:<br />

int f; /* globale Variable */<br />

int main(int argc, char **argv)<br />

{<br />

double f; /* Block-lokale Variable */<br />

}<br />

f = 5.0; /* welches f */<br />

Namenskonflikte treten dann auf, wenn gleiche Bezeichner zur gleichen<br />

Namensklasse gehören, aber in zwei verschiedenen (geschachtelten)<br />

Gültigkeitsbereichen. Prioritätsegeln:<br />

• Formale Funktionsparameter überschreiben globale Deklarationen<br />

• Deklaration am Blockanfang überschreiben äußere Deklarationen<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

136


Vorwärtsreferenzen<br />

Normalerweise muss vor Benutzung eines Bezeichners dieser deklariert sein<br />

(Compiler muss Größe etc. wissen).<br />

Zwei Ausnahmen:<br />

1. Anweisungslabel in goto-Anweisung darf vor der Label-Deklaration<br />

genutzt werden<br />

2. Ein Typbezeichner eines Struktur, Vereinigungs oder Aufzählungstypen<br />

kann schon vor seiner Deklaration in einer typedef-Deklaration oder in<br />

Verbindung mit einem Zeigertypen benutzt werden.<br />

<strong>Beispiel</strong>:<br />

struct type_a {<br />

struct type_b *x;<br />

};<br />

struct type_b {<br />

struct type_a *y;<br />

};<br />

<strong>Beispiel</strong>:<br />

int main(int argc, char **argv)<br />

{<br />

goto ende;<br />

}<br />

ende: ;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

137


Lebensdauer von Speicherobjekten<br />

Objekte (Variablen, Funktionen) existieren zur Laufzeit eines Programms.<br />

Die Lebensdauer (storage duration) eines Objektes ist die Zeitdauer, die der<br />

Speicherbereich angelegt ist, der das Objekt aufnimmt. Man unterscheidet:<br />

1. Statische Lebensdauer: Speicher wird vor dem Start des Programms<br />

ein mal angelegt und wird erst wieder freigegeben, wenn das<br />

Programm beendet wird. (Funktionen, top-level Variablen, static-<br />

Variablen)<br />

2. Automatische Lebensdauer: Der Speicher wird jedes mal zu Beginn<br />

eines Blocks angelegt und am Ende des Blocks wieder frei gegeben.<br />

(Lokale Variablen eines Blocks, Parameter einer Funktion)<br />

3. Dynamische Lebensdauer: Der Speicherbereich wird explizit durch<br />

den Programmierer verwaltet (angelegt, freigegeben).<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

138


<strong>Beispiel</strong><br />

Codesegment<br />

int global_j;<br />

int f( int arg )<br />

{<br />

int local_j;<br />

local_j = arg;<br />

global_j = arg;<br />

return global_j + local_j;<br />

}<br />

int main(int argc, char **argv)<br />

{<br />

int i,j;<br />

}<br />

global_j = 6;<br />

for(i = 0; i < 2; ++i)<br />

j = f(i);<br />

main<br />

f<br />

Datensegment<br />

global_j<br />

Stacksegement<br />

argc<br />

argv<br />

i<br />

J<br />

arg<br />

local_j<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

139


<strong>Beispiel</strong><br />

int main(int argc, char **argv)<br />

{<br />

static int x;<br />

auto int y;<br />

int *p;<br />

Codesegment<br />

main<br />

Datensegment<br />

x<br />

}<br />

p = (int *) malloc(sizeof(*p));<br />

free(p);<br />

Stacksegement<br />

argc<br />

argv<br />

y<br />

p<br />

*p<br />

Heap<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

140


Bindung von Bezeichnern<br />

Teilt man das gesamte Programm auf mehrere Dateien auf (Datei_1.c, ...,<br />

Datei_n.c), so ist weiterhin die Bindung von Bezeichnern von Interesse:<br />

1. Externe Bindung: Der gleiche Bezeichner bezeichnet in allen Dateien<br />

das gleiche Objekt.<br />

2. Interne Bindung: Nur innerhalb einer Datei bezeichnet ein Bezeichner<br />

das gleiche Objekt.<br />

3. Keine Bindung: Objekte, die nur einmal vorkommen.<br />

Regeln (Speicherklasse=auto, register, static, extern):<br />

Keine Speicherklassenangabe<br />

Speicherklassenangabe<br />

static<br />

Speicherklassenangabe<br />

extern<br />

Top-level<br />

Variable: externe Bindung<br />

Funktion: wie mit extern<br />

Interne Bindung<br />

a) Bindung wie gleicher Bezeichner auf top-level<br />

b) Falls nicht vorhanden: externe Bindung<br />

Innerhalb Block<br />

Variable: ohne Bindung<br />

Funktion: wie mit extern<br />

Variable: ohne Bindung<br />

Funktion: interne Bindung<br />

a) Bindung wie top-level<br />

b) Nicht vorhanden: extern<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

141


<strong>Beispiel</strong><br />

Keine Speicherklassenangabe<br />

Speicherklassenangabe<br />

static<br />

Speicherklassenangabe<br />

extern<br />

Top-level<br />

Variable: externe Bindung<br />

Funktion: wie mit extern<br />

Interne Bindung<br />

a) Bindung wie gleicher Bezeichner auf top-level<br />

b) Falls nicht vorhanden: externe Bindung<br />

Innerhalb Block<br />

Variable: ohne Bindung<br />

Funktion: wie mit extern<br />

Variable: ohne Bindung<br />

Funktion: interne Bindung<br />

a) Bindung wie top-level<br />

b) Nicht vorhanden: extern<br />

int glo_var; /* externe Bindung */<br />

int getchar(); /* externe Bindung */<br />

static int datei_var; /* interne Bindung */<br />

static int f(); /* interne Bindung */<br />

extern int glo_var; /* externe Bindung (s.o.) */<br />

extern int glo_var2; /* externe Bindung */<br />

int main(int argc, char **argv)<br />

{<br />

int x; /* keine Bindung */<br />

int getchar(); /* externe Bindung (s.o.) */<br />

static int y; /* keine Bindung */<br />

static int g(); /* interne Bindung */<br />

extern int glo_var; /* externe Bindung (s.o.) */<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

142


Speicherklasse<br />

Die Speicherklasse eines Objektes hat Einfluss auf:<br />

1. Bindung<br />

2. Lebensdauer<br />

3. Gültigkeitsbereich<br />

Es existieren 4 Speicherklassen:<br />

1. auto<br />

2. register<br />

3. static<br />

4. extern<br />

Die Angabe einer Speicherklasse ist optional bei einer Definition / Deklaration<br />

eines Objektes möglich. Dann muss sie an erster Stelle stehen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

143


Speicherklasse auto<br />

Nur in Deklarationen am Anfang von Blöcken erlaubt<br />

Dort ist dies auch der Default und wird deshalb meist weggelassen<br />

Lebensdauer:<br />

automatische Lebensdauer (bei Blockeintritt wird Objekt erzeugt und bei<br />

Verlassen des Blocks vernichtet)<br />

Gültigkeit:<br />

Das Objekt ist bis zum Ende des Blocks bekannt.<br />

<strong>Beispiel</strong>:<br />

int myfun()<br />

{<br />

int x;<br />

}<br />

return 3;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

144


Speicherklasse register<br />

Ursprüngliche Idee: Hinweis für Compiler (hat heute nur Nachteile; s.u.)<br />

Nur für lokale Variablen und Funktionsparameter möglich<br />

Kein Adressoperator zusammen mit register-Variablen möglich<br />

Sonst wie auto<br />

Lebensdauer:<br />

automatische Lebensdauer (bei Blockeintritt wird Objekt erzeugt und bei<br />

Verlassen des Blocks vernichtet<br />

Gültigkeit:<br />

Das Objekt ist bis zum Ende des Blocks bekannt.<br />

<strong>Beispiel</strong>:<br />

int myfun(register int y)<br />

{<br />

register int x;<br />

}<br />

return 3 * y;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

145


Speicherklasse static<br />

In Funktionsdefinition: keine externe Bindung<br />

In Funktionsdeklaration: Funktion wird später in Datei noch definiert (dann<br />

ebenfalls mit static-Angabe)<br />

Lebensdauer:<br />

Statische Lebensdauer (Wert bleibt auch bei Verlassen des Blocks<br />

erhalten)<br />

Gültigkeit:<br />

Das Objekt ist nur innerhalb des Blocks (Variablen) bzw. innerhalb der<br />

Datei (Funktionen, top-level Variablen) bekannt.<br />

<strong>Beispiel</strong>:<br />

int myfun(int y)<br />

{<br />

static int x;<br />

}<br />

return x * y;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

146


Speicherklasse extern<br />

In top-level Deklarationen und am Anfang eines Blocks<br />

Lebensdauer:<br />

Statische Lebensdauer (Wert bleibt auch bei Verlassen des Blocks<br />

erhalten)<br />

Gültigkeit:<br />

Das Objekt einer top-level Deklaration ist global bekannt. Objekte, die am<br />

Anfang eines Blocks deklariert werden, sind nur innerhalb des Blocks<br />

bekannt.<br />

<strong>Beispiel</strong>:<br />

extern int yourfun(int x);<br />

extern int x;<br />

int myfun(int y)<br />

{<br />

return x * y;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

147


Default-Speicherklasse<br />

Ist keine Speicherklassenangabe in einer Deklaration vorhanden, gilt<br />

folgender Default:<br />

1. Top-level Deklarationen haben Speicherklasse extern<br />

2. Funktionsdefinitionen haben Speicherklasse extern<br />

3. Parameterdeklarationen ohne Angabe register (nur diese Angabe wäre<br />

dort erlaubt) bedeuten "kein register"<br />

4. Deklarationen am Anfang eines Blocks haben die Default-<br />

Speicherklasse extern für Funktionen und auto für alle anderen<br />

Objekte<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

148


<strong>Beispiel</strong><br />

void myfun(void)<br />

{<br />

extern int lock; /* global bekannte Variable */<br />

register int i; /* lokale bekannte Variable */<br />

auto int j; /* lokal bekannte Variable */<br />

static int k; /* lokal bekannte Variable; behält Wert */<br />

}<br />

static int f(void); /* lokal bekannte Funktion */<br />

extern int g(void); /* global bekannte Funktion */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

149


Deklarationen<br />

Allgemeine Form einer Deklaration:<br />

Wobei:<br />

Typangabe Deklarator<br />

1. Typangabe die Typspezifikation einschließlich<br />

Speicherklassenbezeichner und Typzusätze ist<br />

2. Deklarator den Bezeichner des Objektes enthält, das deklariert<br />

werden soll<br />

<strong>Beispiel</strong>:<br />

int x; /* Typangabe=int; Bezeichner=x */<br />

const int y; /* Typangabe=const int; Bezeichner=y */<br />

int *const z; /* Typangabe=int; Bezeichner=z (Rest gehört zu Dekl.) */<br />

float a[3]; /* Typangabe=float; Bezeichner=a */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

150


Art der Deklaration<br />

Deklarator hat die Form:<br />

Bezeichner<br />

Dann ist der Typ des Bezeichners Typangabe.<br />

<strong>Beispiel</strong>:<br />

int x; /* x hat Typ int */<br />

struct { float re; float im; } c1; /* c1 hat den Strukturtyp */<br />

typedef struct {float re; float im; } Complex;<br />

Complex c2; /* c2 hat Typ Complex */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

151


Art der Deklaration<br />

Deklarator hat die Form:<br />

( Deklaration )<br />

Dann ist der Typ des Bezeichners der, der durch Deklaration spezifiziert<br />

wird. Die Klammern dienen der besseren Lesbarkeit oder zum Setzen von<br />

Bindungsprioritäten.<br />

<strong>Beispiel</strong>:<br />

int (x); /* x hat Typ int */<br />

int *(*p); /* entspricht **p */<br />

int (*q)[]; /* Zeiger auf Feld von int's */<br />

int *(q[]); /* Feld von Zeigern auf int */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

152


Art der Deklaration<br />

Deklarator hat die Form:<br />

* Deklaration<br />

* type_qualifier_list Deklaration<br />

Dann ist der Typ des Bezeichners ejn Zeiger auf den Typ, der durch<br />

Typangabe Deklaration spezifiziert wird, unter Berücksichtigung der<br />

type_qualifier_list. type_qualifier_list ist eine optionale Liste aus const<br />

und/oder volatile.<br />

<strong>Beispiel</strong>:<br />

int *x; /* x hat Typ: Zeiger auf int */<br />

const int *ptr_const_ptr; /* Zeiger auf int-Konstante */<br />

int * const constr_ptr; /* Konstanter int-Zeiger */<br />

int *volatile vol_ptr; /* volatiler int-Zeiger */<br />

struct { float re, im;}<br />

*complex_ptr; /* Zeiger auf Struktur */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

153


Art der Deklaration<br />

Deklarator hat die Form:<br />

Deklaration [ ]<br />

Deklaration [const_expr ]<br />

Deklaration [var_expr]<br />

Deklaration [*]<br />

C99<br />

C99<br />

Dann ist der Typ des Bezeichners ejn Feld des Basistyps, der durch<br />

Typangabe Deklaration spezifiziert wird. const_expr muss ein konstanter<br />

Ausdruck sein, dessen Wert echt größer 0 sein muss. Die Fälle, in denen<br />

die Dimensionsangabe weggelassen werden kann, wurde bereits<br />

besprochen. Ebenso Felder variabler Größe.<br />

<strong>Beispiel</strong>:<br />

float a[3]; /* float-Feld mit 3 Elementen */<br />

float *(pa[3]); /* Feld von float-Zeigern */<br />

int x[]; /* int-Feld ohne Dimensionsangabe */<br />

volatile int y[3]; /* Feld von volatilen int's */<br />

float b[3][4]; /* Feld der Dimension 3x4 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

154


Art der Deklaration<br />

Deklarator hat die Form:<br />

Deklaration ( )<br />

Deklaration ( id_list )<br />

Dann ist der Typ des Bezeichners ejne Funktion, die einen Wert von<br />

einem Typ liefert, der durch Typangabe Deklaration spezifiziert wird. Nicht<br />

zulässig als Basistyp sind Funktions- oder Feldtypen. Fehlt id_list, so<br />

bedeutet dies, dass keine Angaben zu Parametern der Funktion gemacht<br />

werden (nicht notwendigerweise, dass diese Funktion keine Parameter<br />

besitzt). Diese Form der Spezifikation eines Funktionstypen sollte nicht<br />

mehr verwendet werden. Statt dessen gibt es Funktionsprototypen.<br />

<strong>Beispiel</strong>:<br />

int f(); /* Funktion, die int liefert */<br />

int *(g()); /* Funktion, die Zeiger auf int liefert */<br />

int (*h)(); /* Zeiger auf Funktion, die int liefert */<br />

int f(x,y) /* Funktionsdefinition mit zwei formalen Parametern */<br />

float x,y; {...} /* so nicht mehr benutzen! */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

155


Art der Deklaration<br />

Deklarator hat die Form:<br />

Deklaration ( parameter_type_list )<br />

Dann ist der Typ des Bezeichners ejne Funktion, die einen Wert von<br />

einem Typ liefert, der durch Typangabe Deklaration spezifiziert wird.<br />

Nicht zulässig als Basistyp sind Funktions- oder Feldtypen.<br />

parameter_type_list ist eine durch Kommas getrennte Liste von Typen<br />

oder Deklaratoren, die Typangaben zu den den Funktionsparametern<br />

machen:<br />

• Die Typangabe void besagt, dass diese Funktion keine Parameter hat.<br />

• Fall die Liste mit ... endet, so bedeutet dies, dass keine Angaben über<br />

Anzahl und Typ weiterer Parameter gemacht wird.<br />

• Als Speicherklassenangabe ist nur register erlaubt<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

156


<strong>Beispiel</strong><br />

int ((*u) ( char (*) (long),<br />

float<br />

)<br />

)<br />

(double, ... );<br />

Um solch eine Deklaration zu verstehen, sollte man zuerst versuchen, die<br />

Klammerhierarchie zu verstehen. Anschließend sollte man den Namen des zu<br />

deklarierenden Objektes herausfinden.<br />

Die Deklaration hat die Form: int (*u()) () . Der Name des Objektes ist u. u ist ein<br />

Zeiger auf eine Funktion, die eine Funktion liefert, die wiederum ein int-Resultat<br />

liefert. u hat zwei Parameter: (1) char (*) (long) (hier fehlt der Name des<br />

formalen Parameters, also nur Typangabe) und (2) float. Der erste Parameter ist<br />

ein Zeiger auf eine Funktion, die ein long-Parameter erwartet und ein char als<br />

Ergebnis liefert.<br />

u liefert als Ergebnis eine Funktion, die ein int liefert und selber mindestens<br />

einen Parameter erwartet, dieser ist vom Typ double.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

157


Default-Typen<br />

Aus Kompatibilität mit alten C-Compilern ist das Weglassen einer<br />

Typspezifikation für Variablen- und Funktionsdefinitionen erlaubt. Der<br />

Default ist dann int.<br />

Man sollte dies nicht mehr verwenden! In C99 ist dies auch nicht mehr<br />

erlaubt, dort muss ein Typ angegeben werden!<br />

<strong>Beispiel</strong>:<br />

x;<br />

f(i)<br />

{<br />

return i;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

158


Priorität der Deklaratoren<br />

Priorität:<br />

1. Klammerung<br />

2. Funktions- und Felddeklaratoren<br />

3. Zeigerdeklaratoren<br />

<strong>Beispiel</strong>:<br />

int *sum1(); /* Funktion, die Zeiger auf int liefert */<br />

int *a[a2]; /* Feld von Zeigern auf int */<br />

int (*sum2)(void); /* Zeiger auf int-Funktion */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

159


Implizite Deklarationen<br />

Erscheint im Programmtext ein bis dahin unbekannter Bezeichner gefolgt<br />

von einer öffnenden Klammer, so wird implizit vom Compiler eine<br />

Deklaration vorgenommen, die diese Funktion als int-Funktion auf dem<br />

top-level deklariert.<br />

Vorsicht: Die Problematik wird klar, wenn man das <strong>Beispiel</strong>programm<br />

unten anschaut.<br />

<strong>Beispiel</strong>:<br />

int main(int argc, char **argv)<br />

{<br />

double x;<br />

}<br />

x = sin(3);<br />

/* sin ist eigentlich vom Typ double sin(double);<br />

Hier wird implizit die Deklaration<br />

extern int sin();<br />

auf dem top-level durchgeführt. */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

160


Deklaration / Definition bei top-level Variablen<br />

Deklaration = Typangabe<br />

Definition = Typangabe + Anlegen von Speicher<br />

Folgerung: Zu jedem Objekt kann es viele Deklarationen evtl. in<br />

verschiedenen Dateien geben, aber nur eine Definition geben.<br />

Regeln zur Unterscheidung Deklaration/Definition von Variablen auf toplevel:<br />

1. Beginnt eine Deklaration mit dem Schlüsselwort extern (ohne<br />

Initialisierung), so ist dies eine Deklaration.<br />

2. Für jede Variable mit externer Bindung muss irgendwo in einer der<br />

Programmdateien genau eine Definition erfolgen.<br />

3. Ist ein Initialisierungsausdruck vorhanden, so ist es eine Definition (mit<br />

oder ohne extern erlaubt).<br />

4. Schwebende Definition: keine Initialisierung, keine oder static-<br />

Speicherklasse. Kann zu einer Variablen mehrfach in einer Datei<br />

vorkommen. Falls keine externe Definition dieses Bezeichners in der<br />

Datei (inkl. Includes) existiert, wird daraus eine Definition mit<br />

Initialisierungswert 0 gemacht.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

161


<strong>Beispiel</strong><br />

extern int i1; /* Deklaration; externe Bindung */<br />

static int i2 = 2; /* Definition; interne Bindung */<br />

extern int i3 = 3; /* Definition; externe Bindung */<br />

int i3; /* schwebende Def. (s.o.); externe Bindung */<br />

int i4; /* schwebende Def. (s.u.); externe Bindung */<br />

int i4; /* schwebende Def. (s.o.); externe Bindung */<br />

static int i5; /* schwebende Def.; interne Bindung */<br />

/* falsch wären:<br />

int i2;<br />

int i5;<br />

*/<br />

Widerspruch zu oben (interne/externe Bindung)<br />

Widerspruch zu oben (interne/externe Bindung)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

162


Deklaration / Definition bei Funktionen<br />

Die Unterscheidung bei Funktionen ist einfach:<br />

1. Folgt auf die schließende Klammer ein Semikolon, so ist dies eine<br />

Deklaration.<br />

2. Folgt kein Semikolon, so ist dies eine Definition (und es muss ein { oder<br />

Typangaben zu Parametern folgen).<br />

<strong>Beispiel</strong>:<br />

int f1 ( int i, int j ) ; /* Deklaration */<br />

int f2 ( int i, int j ) /* Definition */<br />

{<br />

return i+j;<br />

}<br />

int f3 ( i, j ) /* Definition */<br />

int i,j;<br />

{<br />

return i-j;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

163


Inline Funktionen<br />

• Neu in C99<br />

• Voranstellen des Schlüsselworts inline in Funktionsdefinition und<br />

Funktionsdeklaration<br />

• Hinweis an den Compiler, diese Funktion an Aufrufstellen zu<br />

expandieren (Optimierung).<br />

• Eine Funktion hat eine Inline-Definition, wenn alle top-level<br />

Deklarationen der Funktion in der Übersetzungseinheit inline enthalten<br />

und kein extern vorliegt<br />

• In genau einer Übersetzungseinheit muss aber eine Definition der<br />

Funktion mit extern erfolgen (falls der Compiler keine Inline-Expansion<br />

vornimmt)<br />

• Typische Nutzung einer Inline-Definition in Headerdatei<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

164


<strong>Beispiel</strong><br />

square.h:<br />

// inline Definition<br />

// und Deklaration für externe Funktion<br />

inline double square( double x)<br />

{<br />

return x * x;<br />

}<br />

square.c:<br />

#include <br />

// externe Definition<br />

// durch extern wird inline in square.h<br />

// hier überschrieben<br />

extern double square( double x)<br />

{<br />

return x * x;<br />

}<br />

abstand.c:<br />

#include <br />

#include <br />

// berechne Abstand zweier Koordinaten<br />

// Wurzel((x2-x1)^2 + (y2-y1)^2)<br />

double abstand( double x1, double y1,<br />

double x2, double y2)<br />

{<br />

// Compiler hat 2 Möglichkeiten:<br />

// 1) inline expandieren<br />

// 2) square explizit aufrufen<br />

return sqrt( square(x1-x2)<br />

+ square(y2-y1));<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

165


Typnamen<br />

An manchen Stellen ist ein Typname verlangt, z.B. bei der Umwandlung<br />

eines Wertes von einen Typ in einen anderen (von int nach long).<br />

Ein Typname ist syntaktisch eine Deklaration ohne Bezeichner.<br />

<strong>Beispiel</strong>:<br />

int /* int */<br />

int * /* Zeiger auf int */<br />

int *[3] /* Feld mit 3 Zeigern auf int */<br />

int (*const []) (unsigned int,...)<br />

/* Feld unbestimmter Dimension mit konstanten Zeigern<br />

auf Funktionen, die ein int-Resultat liefern und<br />

die einen ersten Parametern vom Typ unsigned int<br />

haben und eine unbestimmte Anzahl und Typen<br />

weiterer Parameter<br />

Wo würde der Bezeichner in einer Deklaration<br />

hingehören<br />

*/<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

166


Initialisierungen<br />

In einer Variablendefinition kann man zusätzlich einen Initialisierungswert<br />

mit angeben, mit dem die Variable bei jeder Allokierung vorbesetzt wird.<br />

Der Wert wird jeweils neu berechnet.<br />

Syntax:<br />

Deklaration = Ausdruck<br />

Einschränkungen:<br />

1. Initialisierungsangaben sind nicht erlaubt bei einer Variablendefinition in<br />

einem Block, wenn die Variable interne oder externe Bindung hat.<br />

2. Nur konstante Ausdrücke (zur Übersetzungszeit kann der Wert<br />

ausgerechnet werden) für Objekte mit statischer Lebensdauer und<br />

wenn Typ ein Feld, Struktur oder Vereinigung ist.<br />

<strong>Beispiel</strong>:<br />

static int i = 5;<br />

static int *j = &i;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

167


Regeln zur Initialisierung: Klammerung<br />

Der Initialisierungsausdruck bzw. Teilausdrücke können bzw. müssen durch<br />

{ } geklammert werden:<br />

1. Skalare Ausdrücke können geklammert werden.<br />

<strong>Beispiel</strong>:<br />

ink k = { 5 * (j+1) };<br />

2. Ausdrücke für nicht-skalare Variablen (Feld, Struktur, Vereinigung)<br />

müssen geklammert werden.<br />

<strong>Beispiel</strong>:<br />

float Grenze[2] = { -1.0f, 1.0f };<br />

3. Ausnahme: String = Character-Felder<br />

<strong>Beispiel</strong>:<br />

char str[] = "Stringwert";<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

168


Regeln zur Initialisierung: Implizite Initialisierungen<br />

Objekte mit statischer Lebensdauer, die nicht explizit initialisiert werden,<br />

werden implizit vorbesetzt:<br />

1. Objekte mit arithmetische Typ werden mit 0 (des entsprechenden Typs)<br />

vorbesetzt<br />

2. Objektes eines Zeigertyps werden mit dem Nullzeiger vorbesetzt.<br />

<strong>Beispiel</strong>:<br />

static int i; /* bekommt Wert 0 */<br />

static float f; /* bekommt Wert 0.0f */<br />

static double d; /* bekommt Wert 0.0 */<br />

static int *ptr_i; /* bekommt Wert NULL */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

169


Regeln zur Initialisierung: Vereinigungstyp<br />

Bei Variablen eines Vereinigungstyps kann man nur Werte für die erste<br />

Komponente des Vereinigungstyps angeben.<br />

<strong>Beispiel</strong>:<br />

union<br />

{<br />

float x;<br />

int i;<br />

} uvar = 3.0; /* Es ist nicht möglich, uvar.i zu initialisieren */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

170


Benannte Initialisierer<br />

• Neu in C99<br />

• Initialisierung bestimmter Teile eines Feldes, Struktur oder Union<br />

• Syntax:<br />

<strong>Beispiel</strong>:<br />

– [expr]=val für Felder<br />

– .name=val für Strukturen und Unions<br />

// Feldelemente 1,2,7 werden explizit initialisiert (Rest implizit)<br />

int feld1[10] = { [1]=5, [7]=3, [2]=1};<br />

// Feld hat 5 Elemente [1, 2, 5, 4, 5]<br />

int feld2[] = {1, 2, 3, [2]=5, 4, 5};<br />

struct s {<br />

int i;<br />

float f;<br />

char s[4};<br />

} s1 = { .s="str", .f=3.14}; // Komponenten s und f explizit initialisiert<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

171


Regeln zur Initialisierung: Sonstiges<br />

1. Felder ohne Dimensionsangabe aber mit Initialisierungsausdruck<br />

werden entsprechend der Anzahl der Werte im Initialisierungsausdruck<br />

dimensioniert.<br />

2. Es dürfen nicht mehr Werte angegeben werden, als zu initialisierende<br />

Objekte vorhanden sind (<strong>Beispiel</strong>: nicht mehr Teilausdrücke, als Feld<br />

Elemente hat).<br />

3. Es dürfen weniger Werte angegeben werden. Dann gelten die Regeln<br />

zur impliziten Initialisierung für die restlichen Elemente (mit 0<br />

vorbesetzen).<br />

4. Die innersten Klammern bei Unterdeklarationen (z.B. 2-dim. Feld)<br />

können weggelassen werden.<br />

5. Regeln gelten rekursiv für Untertypen<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

172


<strong>Beispiel</strong>e<br />

/* impliziert dimensioniertes Feld */<br />

int x[] = { 3, 4, 5 };<br />

int y[] = { {3}, {4}, {5} };<br />

/* Strukturvariable */<br />

struct { float re, im; } cmpl = { 2.0, 1.0 };<br />

/* Geschachtelte Strukturen */<br />

struct<br />

{<br />

int x[3]; /* 1. Komponente: x */<br />

struct {float re, im; } cmpl; /* 2. Komponente: cmpl */<br />

} s[2]<br />

=<br />

{ /* es folgt s[0]<br />

{ { 1,2,3 }, /* 1. Komponente: x */<br />

{ 2.0, 1.0} /* 2. Komponente: cmpl */<br />

}<br />

, /* es folgt s[1] */<br />

{ { 4,5,6 }, /* 1. Komponente: x */<br />

{ 4.0, 2.0 } /* 2. Komponente: cmpl */<br />

}<br />

};<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

173


<strong>Beispiel</strong>e<br />

/* nicht-konstante Ausdrücke */<br />

extern int getchar(void);<br />

int f(void)<br />

{<br />

int ch = getchar() + 'a'; /* bei jedem Aufruf von f */<br />

}<br />

/* Zwei Felder der Dimension 4x3. Die ersten 3 Zeilen werden mit den<br />

vorgegebenen Zahlen explizit initialisiert, die letzte Zeile<br />

jeweils implizit mit Nullen besetzt.<br />

*/<br />

int x[4][3] = { {1,2,3}, {4,5,6}, {7,8,9} };<br />

int y[4][3] = { 1,2,3,4,5,6,7,8,9 };<br />

/* Das jeweils erste Element jeder Zeile wird explizit gesetzt, die<br />

anderen implizit mit Nullen besetzt.<br />

*/<br />

int z[4][3] = { {1}, {4}, {7}, {10} };<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

174


<strong>Beispiel</strong>e<br />

/* äquivalent: */<br />

char s[] = "abc";<br />

char t[] = { 'a', 'b', 'c', '\0' };<br />

/* Hier wird ein char-Zeiger initialisiert. Der String "abc" kann<br />

nicht (!) verändert werden. *p = 'd' ist nicht zulässig!<br />

char *p = "abc";<br />

/* Rückgriff auf schon definierte Objekte */<br />

static short sh, *ptr_sh = &sh;<br />

static double feld[100];<br />

double *ptr_f = feld;<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

175


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

176


Ausdrücke<br />

Ausdrücke berechnen einen Wert.<br />

<strong>Beispiel</strong>:<br />

3+4<br />

Ein l-Wert (links-Wert) ist ein Ausdruck, der auf ein Objekt im Speicher<br />

verweist (z.B. ein Zeigerwert, ein Variablenname). Als Faustregel gilt, dass<br />

alles, was auf der linken Seite einer Zuweisung stehen kann, ein l-Wert ist.<br />

<strong>Beispiel</strong>:<br />

int a, b;<br />

a = b; /* b ist in diesem Ausdruck ein l-Wert */<br />

a = (3+4); /* (3+4) ist kein l-Wert */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

177


Einfacher Ausdruck<br />

Bezeichnung<br />

Konstante<br />

Variablenname<br />

(arithm.Typ, Zeigertyp, enum, struct/union)<br />

Variablenname eines Feldtyps<br />

Funktionsname<br />

( expression )<br />

Wert<br />

Wert der Konstanten<br />

(Bei String-Konst. Zeiger auf 1. Zeichen)<br />

Wert der Variablen<br />

Zeiger auf 1. Element (außer bei sizeof; s.u.)<br />

Zeiger auf Funktionscode (außer bei sizeof)<br />

Klammerung Ausdruck<br />

(Auswertungsreihenfolge später)<br />

<strong>Beispiel</strong>:<br />

int i,j;<br />

char *cptr, cfeld[10];<br />

int fn( int (*fun)(void));<br />

int fn1(void);<br />

j = 1; /* Konstante 1 */<br />

cptr = "abc"; /* String-Konstante: Zeiger auf 1. Zeichen */<br />

i = j; /* Variable arithmetischen Typs */<br />

cptr = cfeld; /* Feldname: Zeiger auf 1. Element */<br />

i = fn( fn1 ); /* Funktionsname fn1: Zeiger auf Code */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

178


Einfacher Ausdruck<br />

Bezeichnung<br />

x [ expr ]<br />

x.name<br />

x -> name<br />

& x<br />

* x<br />

Wert<br />

Auswahl Feldkomponente<br />

(x Feld- oder Zeigervariable; expr int-Wert)<br />

Komponente name der Struktur/Union x<br />

Komponente name einer Struktur, auf die x (Zeigerwert) zeigt.<br />

Adresse von x. x muss l-Wert sein.<br />

Kein Bit-Feld, keine Variable der Speicherklasse register.<br />

Der Wert, worauf x zeigt. x muss Zeigertyp besitzen.<br />

<strong>Beispiel</strong>:<br />

int i, a[10];<br />

struct {int ival; } str, *ptr_str;<br />

i = a[3];<br />

// Zugriff auf 3.Komponente des Feldes<br />

str.ival = str.ival + 1; // Zugriff auf Komponente ival der Struktur<br />

ptr_str = &str;<br />

// ptr_str zeigt nun auf str<br />

(*ptr_str).ival = (*ptr_str).ival + 1; // Komponente ival inkrementieren<br />

str.ival = str.ival + ptr_str->ival; // ptr_str->ival entspricht (*ptr_str).ival<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

179


Arbeiten mit Zeigern<br />

// Wichtig in den <strong>Beispiel</strong>en: 0 entspricht false<br />

// kopiere einen String von source nach dest<br />

// (genügend Speicherplatz in dest vorausgesetzt)<br />

char *my_strcpy(char *dest, *char *source)<br />

{<br />

char *save = dest; // Anfang des Resultatstrings merken<br />

// Strings werden mit '\0' abgeschlosssen<br />

while(*dest++ = *source++)<br />

; // in letzter Zeile schon alles gemacht<br />

}<br />

return save;<br />

// Anfang des Resultatstrings als Ergebnis<br />

// finde das Ende eines Strings<br />

char *ende_string(char *str)<br />

{<br />

// Strings werden durch '\0' abgeschlossen<br />

while (*p++)<br />

;<br />

}<br />

return p;<br />

// gebe Position (=Zeiger) zurück<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

180


Einfacher Ausdruck<br />

Bezeichnung<br />

(Typname) x<br />

(Typname) { init-list }<br />

x = y<br />

sizeof expr<br />

sizeof(Typname)<br />

x (Argumentliste)<br />

Wert<br />

Wert x wird (falls möglich) in einen Wert des Typs Typname<br />

umgewandelt (cast-Operation)<br />

Cast von einem anonymen Objekt eines zusammengesetzten Typs<br />

(C99)<br />

y wird an x (l-Wert) zugewiesen. Ergebnis des Gesamtausdrucks ist<br />

Wert von y. Bei Überlappung der Speicherbereiche nicht definiert.<br />

Größe von expr bzw. Typname in Bytes. Zur Übersetzungszeit<br />

berechnet, also keine Seiteneffekte zur Laufzeit (Bsp.: sizeof ++i).<br />

Feldname: gesamtes Feld und nicht Zeigerwert<br />

Nicht erlaubt: Funktion, Bit-Feld, void, Feld ohne expl. Längenangabe<br />

Funktionsaufruf der Funktion x mit aktuellen Argumenten.<br />

Argumentwerte werden ausgerechnet und auf Laufzeitstack abgelegt<br />

(call-by-value).<br />

Beachte:<br />

Die Zuweisung ist in C eine Operation und keine Anweisung!<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

181


Einfacher Ausdruck<br />

<strong>Beispiel</strong>:<br />

int i, j, k;<br />

i = (int) 3.0; /* Typ cast */<br />

k = (j = i); /* Zuweisung j = i ist Ausdruck */<br />

j = sizeof i; /* Größe in Bytes von i */<br />

j = sizeof(int); /* Größe in Bytes vom Typ int */<br />

i = fun1( i, i+1, fun2(j)); /* 3 Argumente werden vorher ausgewertet */<br />

i = 5;<br />

j = 3;<br />

if( i = j ) ++i; /* Häufigster Fehler! Compiler-Option -Wall! */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

182


Anonyme Objekte<br />

// Berechnung von Zweierpotenzen für 0


Inkrement- / Dekrement-Ausdruck<br />

Bezeichnung<br />

++x<br />

--x<br />

x++<br />

x--<br />

Wert<br />

Wert von x (l-Wert) wird inkrementiert und wieder in x gespeichert, Der<br />

Wert des Ausdrucks ist der neue Wert von x.<br />

Wert von x (l-Wert) wird dekrementiert und wieder in x gespeichert, Der<br />

Wert des Ausdrucks ist der neue Wert von x.<br />

Wert von x (l-Wert) wird inkrementiert und wieder in x gespeichert, Der<br />

Wert des Ausdrucks ist der alte Wert von x.<br />

Wert von x (l-Wert) wird dekrementiert und wieder in x gespeichert, Der<br />

Wert des Ausdrucks ist der alte Wert von x.<br />

<strong>Beispiel</strong>:<br />

int i, j;<br />

i = 1;<br />

j = ++i + 1; /* i=2, j=3 */<br />

i = 1;<br />

j = i++ + 1; /* i=2, j=2 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

184


Arithmetischer Ausdruck<br />

Bezeichnung<br />

+x<br />

-x<br />

x + y<br />

x - y<br />

x * y<br />

x / y<br />

x % y<br />

Wert<br />

Unäres Plus<br />

Unäres Minus<br />

Addition<br />

Subtraktion<br />

Multiplikation<br />

Division. Für Ausdruck mit integralem Typ ganzzahlige Division.<br />

Modulo-Bildung (nur integrale Typen).<br />

<strong>Beispiel</strong>:<br />

float x;<br />

int i;<br />

x = 5.0 + 2.0 * 12.0; /* Wert 29 */<br />

i = 9 / 4; /* Wert 2 */<br />

i = 9 % 4; /* Wert 1 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

185


Anmerkungen<br />

1. Typanpassungen findet evtl. vor Anwendung der Operation statt<br />

(später)<br />

2. Under-/Overflow bei Operationen mit signed Argumenten:<br />

Ergebnis nicht definiert (Fehler, Modulo-Arithmetik usw. möglich)<br />

3. Under-/Overflow bei Operationen mit unsigned Argumenten:<br />

Modulo-Arithmetik<br />

<strong>Beispiel</strong>:<br />

#include <br />

int i, j;<br />

unsigned int ui, uj;<br />

i = j = INT_MAX;<br />

ui = uj = UNINT_MAX;<br />

i = i + j; /* nicht definiert */<br />

ui = ui + uj; /* definiert */<br />

<strong>Beispiel</strong>:<br />

ui 1111 = 15<br />

uj +1111 = 15<br />

--------------<br />

1110 = 30%16 = 14<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

186


Zeigerarithmetik<br />

Mit Zeigerwerten ist ebenfalls (eingeschränkt) Addition und Subtraktion<br />

möglich. Gerechnet wird nicht in Bytes, sondern in Einheiten des Basistyps,<br />

d.h. ptr+1 entspricht dem Wert von ptr (1000) + so viele Bytes, wie der<br />

Grundtyp (z.B. int=4) beansprucht (1004).<br />

Einschränkungen:<br />

1. Addition: Nur ein Operand darf Zeiger sein, der andere muss int sein.<br />

Das Resultat ist ein Zeiger.<br />

2. Subtraktion: Zwei Zeigeroperanden liefern ein int-Resultat, linker<br />

Operand ein Zeiger, rechter Operand ein int liefert Zeiger-Resultat.<br />

<strong>Beispiel</strong>:<br />

int i, *ptr1, ptr2; /* Annahme: i liegt auf Adresse 1000,<br />

Größe eines ints=4 Bytes */<br />

ptr1 = &i; /* ptr1 = 1000 */<br />

ptr2 = ptr1 + 1; /* ptr2 = 1004 */<br />

i = ptr1 - ptr2; /* i = -1 */<br />

ptr2 = ptr1 - 1; /* ptr2 = 996 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

187


Relationaler und logischer Ausdruck<br />

Bezeichnung<br />

x < y<br />

x > y<br />

x = y<br />

x == y<br />

x != y<br />

x && y<br />

x || y<br />

!x<br />

Wert<br />

kleiner<br />

größer<br />

kleiner gleich<br />

größer gleich<br />

gleich<br />

ungleich<br />

logisches Und<br />

logisches Oder<br />

logische Negation<br />

• Wahrheitswerte sind vom Typ int: 0=falsch, ungleich 0=wahr<br />

• || und && werten zweites Argument nur aus, wenn nötig!<br />

• Entweder haben beide Operanden einen arithmetischen Typ oder<br />

beide sind Zeiger auf Objekte kompatiblen Typs.<br />

• Evtl. Typanpassungen der Operanden<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

188


<strong>Beispiel</strong><br />

int i,<br />

a[2],<br />

*ptr1, *ptr2;<br />

i = (3 < 4) || f(10000); /* f(10000) wird nicht ausgewertet! */<br />

j = (3 < 4) && f(10000); /* f(10000) wird ausgewertet! */<br />

ptr1 = &a[0];<br />

ptr2 = &a[1];<br />

i = ptr1 < ptr2; /* liefert wahr */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

189


Bit-Ausdruck<br />

Bezeichnung<br />

x > y<br />

x & y<br />

x | y<br />

x ^y<br />

~x<br />

Wert<br />

Links-Shift von x um y Stellen<br />

Rechts-Shift von x um y Stellen<br />

Bit-weises Und<br />

Bit-weises Oder<br />

Bit-weises Xor (1, wenn beide Bits verschieden)<br />

Bit-weise Negation<br />

Für Shift-Operationen gilt:<br />

• Rechter Operand > 0 und < Anzahl Bits des linken Operanden<br />

• Es werden Null-Bits nachgeschoben<br />

• Falls linker Operand signed-Typ hat und negativer Wert vorliegt, so ist<br />

das Ergebnis undefiniert.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

190


<strong>Beispiel</strong><br />

unsigned int i;<br />

i = 1; /* i hat Wert 1 */<br />

i = i > 2; /* i hat Wert 0010 2<br />

= 2 */<br />

i = i | 5; /* i hat Wert 0111 2<br />

= 7: 0010 2<br />

| 0101 2<br />

= 0111 2<br />

*/<br />

i = i & 3; /* i hat Wert 0011 2<br />

= 3: 0111 2<br />

& 0011 2<br />

= 0011 2<br />

*/<br />

i = i ^ 5; /* i hat Wert 0110 2<br />

= 6: 0011 2 ^ 0101 2<br />

= 0110 2<br />

*/<br />

i = i & ~i; /* i hat Wert 0000 2<br />

= 0: 0110 2<br />

&~0110 2<br />

= 0110 2<br />

& 1001 2<br />

= 0000 2<br />

*/<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

191


Kurzformen von Zuweisungen<br />

Bezeichnung<br />

x += y<br />

x -= y<br />

x *= y<br />

x /= y<br />

x %= y<br />

x = y<br />

x &= y<br />

x |= y<br />

x ^= y<br />

Kurzform für<br />

x = x + y<br />

x = x - y<br />

x = x * y<br />

x = x / y<br />

x = x % y<br />

x = x > y<br />

x = x & y<br />

x = x | y<br />

x = x ^y<br />

Die Kurzformen sind in C ebenfalls Operationen und keine Anweisungen!<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

192


Sonstige Ausdrücke<br />

Bezeichnung<br />

( e0 ) e1 : e2<br />

e1 , e2<br />

Wert<br />

Der Ausdruck e0 wird ausgewertet. Falls dieser wahr (!=0) ist, so wird e1<br />

ausgewertet, ansonsten e2. Der Wert des Gesamtausdrucks ist der Wert<br />

von e1 bzw. e2. Beachte: Entweder e1 oder e2 wird nur ausgewertet.<br />

Zuerst wird e1 ausgewertet, das Ergebnis ignoriert und anschließend e2<br />

ausgewertet. Der Typ und das Ergebnis des Gesamtausdrucks ist der<br />

Typ bzw. das Ergebnis von e2.<br />

<strong>Beispiel</strong>:<br />

int i, i, max;<br />

k = i = (j = 1) + 1 , 2*j; /* i=2, j=1, k=2 */<br />

max = (i > j) i : j; /* max = (2 > 1) 2 : 1 = 2 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

193


Konstante Ausdrücke<br />

An manchen Stellen sind konstante Ausdrücke erforderlich:<br />

1. Testausdruck in #if-Präprozessordirektiven<br />

2. Dimensionsangaben bei Feldern<br />

3. case-Label in switch-Anweisungen<br />

4. Länge von Bit-Feldern<br />

5. Explizite Werte für Aufzählungselemente<br />

6. Initialisierungswerte für static- und extern-Variablen<br />

Bildung von konstanten Ausdrücken:<br />

1. Konstanten eines integralen Typs<br />

2. Klammern ()<br />

3. Unäre Operatoren: + - ~ ! sizeof<br />

4. Binäre Operatoren: + - * / % > == != < >= & ^ | && ||<br />

5. Ternäre Operatoren: :<br />

Der sizeof-Operator kann nicht in Präprozessor-Direktiven verwendet werden.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

194


Priorität der Operatoren<br />

Operator<br />

f() a[] -> .<br />

x++ x--<br />

++x --x ~ ! +x -x * & (type) sizeof<br />

* / %<br />

+ -<br />

><br />

< >=<br />

== !=<br />

&<br />

^<br />

|<br />

&&<br />

||<br />

:<br />

= += -= *= /= %= &= ^= |= =<br />

,<br />

Assoziativität<br />

links -> rechts<br />

links -> rechts<br />

rechts -> links<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

links -> rechts<br />

rechts -> links<br />

rechts -> links<br />

links -> rechts<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

195


Reihenfolge der Auswertung<br />

Die Reihenfolge der Auswertung eines Ausdrucks ist in C nur in wenigen<br />

Fällen vorgegeben:<br />

1. f(args)<br />

2. && und ||<br />

3. :<br />

4. ,<br />

Ansonsten ist der Compiler frei, in welcher Reihenfolge er Teilausdrücke<br />

auswertet.<br />

<strong>Beispiel</strong>:<br />

int a,b,c,d;<br />

a = (a + b) + (c + d);<br />

/* Trotz Klammerung dürfte der C-Compiler dies auswerten als:<br />

a = (a + d) + (b + c); */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

196


Typumwandlungen<br />

In manchen Fällen ist ein Umwandlung von einem Typ in einen anderen Typ<br />

nötig, was explizit oder implizit geschehen kann:<br />

1. Explizite Umwandlung in cast-Ausdruck.<br />

<strong>Beispiel</strong>: i = 3 + (int) 4.0;<br />

2. Implizite Umwandlung eines Operanden.<br />

<strong>Beispiel</strong>: i = 3 + 4.0;<br />

3. Implizite Umwandlung bei einer Zuweisung<br />

<strong>Beispiel</strong>: int i = 4.0;<br />

4. Aktuelles Argument in Funktionsaufruf wird auf den Typ des formalen<br />

Parameters angepasst.<br />

<strong>Beispiel</strong>:<br />

int fun(int arg);<br />

i = fun(4.0);<br />

5. Ergebniswert einer Funktion wird implizit auf den Ergebnistypen<br />

umgewandelt.<br />

<strong>Beispiel</strong>:<br />

int fun(int arg) { return 4.0; }<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

197


Prinzipielle Möglichkeiten der Typumwandlung<br />

↓von →nach<br />

void<br />

int<br />

float<br />

Zeiger<br />

Feld<br />

Struktur<br />

Funktion<br />

void<br />

ok<br />

Integer<br />

ok<br />

ok<br />

ok<br />

ok<br />

Fließkomma<br />

ok<br />

ok<br />

ok<br />

Zeiger<br />

ok<br />

ok<br />

ok<br />

(ok)<br />

(ok)<br />

Feld<br />

ok<br />

Struktur<br />

ok<br />

Funktion<br />

ok<br />

(ok) bedeutet implizite Umwandlung.<br />

<strong>Beispiel</strong>: Der Wert eines Feldnamens in einem Ausdruck ist der Zeiger auf<br />

das erste Element des Feldes.<br />

Bei Zeigertypen muss zwischen Zeigertypen auf Objekte und Zeigertypen auf<br />

Funktionen unterschieden werden. Ein Objektzeigertyp und ein<br />

Funktionszeigertyp sind nicht in den anderen Typ umwandelbar.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

198


Umwandlung nach void<br />

Jeder Wert kann in den Typ void umgewandelt werden. Der Effekt ist, dass<br />

damit der Wert verschwindet. Eine sinnvolle Anwendung ist der Fall, wo man<br />

explizit darauf hinweisen möchte, dass man den Wert ignorieren möchte.<br />

<strong>Beispiel</strong>:<br />

int printf(char *format, ...);<br />

/* Die printf-Funktion liefert als Ergebnis die Anzahl der<br />

ausgegebenen Zeichen. */<br />

(void) printf("hallo\n");<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

199


Umwandlung nach Integertyp<br />

1. Von int-Typ:<br />

Falls der Wert im neuen Typ darstellbar ist, dann umwandeln.<br />

Ansonsten, falls der Ergebnistyp ein unsigned int ist, so schneide<br />

obersten Bits ab (Modulo-Bildung). Ansonsten ist das Ergebnis<br />

undefiniert.<br />

2. Von Fließkommatyp:<br />

Der ganzzahlige Teil des Wertes wird umgewandelt. Falls dieser Wert<br />

nicht in dem int-Typ darstellbar ist, ist die Umwandlung undefiniert.<br />

3. Von Zeigertyp:<br />

Der Zeigerwert wird als unsigned int mit der gleichen Größe wie die des<br />

Zeigers angesehen. Darauf werden obige Regeln angewendet.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

200


<strong>Beispiel</strong><br />

unsigned int ui;<br />

int i;<br />

signed int si;<br />

int *ptr;<br />

i = 5l; /* (long int)5 ist in int darstellbar, also 5 */<br />

ui = ULONG_MAX; /* ULONG_MAX ist evtl. nicht in unsigned int darstellbar.<br />

Obersten Bits werden dann abgeschnitten. */<br />

si = ULONG_MAX; /* undefiniert */<br />

i = 3.5; /* 3.5 wird nach (int)3 umgewandelt */<br />

ptr = &i; /* z.B. Adresse 1000 */<br />

i = (int)ptr; /* i erhält den Wert (int)1000 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

201


Umwandlung nach Fließkommatyp<br />

1. Von int-Typ:<br />

Eine entsprechende Näherung wird als Wert genommen.<br />

2. Von Fließkommatyp:<br />

Von float nach double:<br />

Ist möglich.<br />

Von double nach float:<br />

Falls der Wert in float darstellbar ist, wird dieser Wert genommen<br />

(gerundet oder abgeschnitten ist impl.abhängig). Ansonsten undefiniert.<br />

<strong>Beispiel</strong>:<br />

float f;<br />

double d;<br />

d = 5; /* Umwandlung nach 5.0 */<br />

d = 5.0f; /* Umwandlung nach (double)5.0 */<br />

f = 5.0; /* Umwandlung nach (float)5.0 */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

202


Umwandlung nach Zeigertyp<br />

1. Von Zeigertyp:<br />

Zeigerwert bleibt erhalten im neuen Zeigertyp.<br />

2. Von Integertyp:<br />

Integerwert wird als Adresse interpretiert.<br />

Nicht portabel! Ausnahme: 0 (NULL).<br />

3. Von Feldtyp:<br />

Der Feldname wird als Adresse des ersten Elementes genommen<br />

(Ausnahme: sizeof)<br />

<strong>Beispiel</strong>:<br />

float f, *fptr;<br />

int i, *iptr, ifeld[10]<br />

fptr = &f; /* z.B. Adresse 1000 */<br />

iptr = (int *)fptr; /* iptr bekommt 1000 zugewiesen */<br />

i = 0x0005; /* An Adresse 5 liegt z.B. ein HW Control-Port */<br />

iptr = (int *)i; /* *iptr würde den Inhalt der Adresse 5 ergeben. */<br />

iptr = ifeld; /* iptr hat &ifeld[0] */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

203


"Übliche" Umwandlungen<br />

Bis jetzt haben wir nur prinzipiell besprochen, welche Umwandlungen möglich<br />

sind und was für Auswirkungen diese Umwandlungen haben.<br />

Wann finden die Umwandlungen statt<br />

1. Explizite Umwandlung (cast)<br />

2. Umwandlung in Zuweisungen<br />

3. Umwandlung in Ausdrücken<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

204


Umwandlungen in Casts<br />

Über Casts lässt sich explizit eine Typumwandlung erzwingen. Die<br />

erlaubten Umwandlungen in Casts sind:<br />

Cast-Typ<br />

Beliebiger arithmetischer Typ<br />

beliebiger int-Typ<br />

void *, Zeiger auf T<br />

void<br />

Ursprungstyp<br />

Beliebiger arithmetischer Typ<br />

beliebiger Zeigertyp<br />

beliebiger int-Typ, void *, beliebiger Zeigertyp<br />

beliebig<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

205


Umwandlungen in Zuweisungen<br />

Stimmt der Typ auf der linken Seite der Zuweisung nicht mit dem Typ auf der<br />

rechten Seite überein, wir der Wert rechts auf den Typ der linken Seite<br />

angepasst.<br />

Erlaubte ist dabei:<br />

Linke Seite<br />

Beliebiger arithmetischer Typ<br />

Struktur-/Union-Typ<br />

void *<br />

Zeiger auf Typ T1<br />

Rechte Seite<br />

Beliebiger arithmetischer Typ<br />

kompatibler Struktur-/Union-Typ<br />

0, void *, Zeiger<br />

0, void *, Zeiger auf T2<br />

(wobei T1 und T2 kompatibel sein müssen)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

206


<strong>Beispiel</strong><br />

int i, *ptr_i, feld_i[2];<br />

double d;<br />

short int si;<br />

void *ptr_v;<br />

i = 3.0; /* implizite Umwandlung (double)3.0 -> (int)3 */<br />

d = 3; /* implizite Umwandlung (int)3 -> (double)3.0 */<br />

si = 3; /* implizite Umwandlung (int)3 -> (short)3 */<br />

ptr_i = 0; /* 0 kann man Variablen beliebigen Zeigertyps zuweisen */<br />

ptr_v = ptr_i; /* möglich, weil ptr_v Typ void * hat */<br />

ptr_i = ptr_v; /* möglich, weil ptr_v Typ void * hat */<br />

ptr_i = feld_i; /* Adresse des ersten Elementes */<br />

/* Falsch: */<br />

const int *ptr_ci;<br />

int *ptr_i;<br />

ptr_i = ptr_ci; /* Falsch: Nicht-kompatible Typen, keiner davon void * */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

207


Unäre (einstellige) Umwandlungen<br />

Die unären Umwandlungen werden angewandt auf Operanden folgender<br />

Operationen:<br />

1. Funktionsargumente zu Funktionen, zu denen kein Funktionsprototyp<br />

bekannt ist<br />

2. Unäre Operatoren: ! - ~ *<br />

3. Binäre Operatoren: ><br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

208


Unäre (einstellige) Umwandlungen<br />

Integertypen ist ein Rang zugeordnet, der in den Umwandlungsregeln<br />

eine Rolle spielt.<br />

Rang<br />

60<br />

50<br />

40<br />

30<br />

20<br />

10<br />

Typen<br />

long long int, unsigned long long int<br />

long int, unsigned long int<br />

int, unsigned int<br />

short, unsigned short<br />

char, unsigned char, signed char<br />

_Bool<br />

Die extended Integertypen müssen implementierungsabhängig in dieser<br />

Tabelle eingeordnet sein.<br />

<strong>Beispiel</strong>:<br />

In einer Implementierung könnte int64_t den Rang 60, in einer anderen<br />

Implementierung den Rang 55 haben.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

209


Unäre (einstellige) Umwandlungen<br />

Die angewandte unäre Umwandlung ist die erste Regel der folgenden<br />

Tabelle, die anwendbar ist.<br />

Operandentyp<br />

Fließkommatyp<br />

Feld von T<br />

Funktion, die T liefert<br />

int-Typ mit Rang >= int<br />

signed int Typ mit Rang < int<br />

unsigned int Typ mit Rang < int und<br />

alle Werte dieses Typs sind darstellbar in int<br />

unsigned int Typ mit Rang < int und nicht alle<br />

Werte dieses Typs sind darstellbar in int<br />

Zieltyp<br />

keine Umwandlung<br />

Zeiger auf T<br />

Zeiger auf Funktion, die T liefert<br />

keine Umwandlung<br />

int<br />

int<br />

unsigned int<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

210


<strong>Beispiel</strong><br />

int i;<br />

char c;<br />

short s1=1, s2=4;<br />

c = 'a';<br />

i = -c; /* Umwandlung nach (int)c und dann unäres Minus */<br />

i = s1


Binäre Umwandlungen<br />

Anwendung bei den meisten binären Operatoren und auf das 2. Und 3.<br />

Argument des :-Operators.<br />

Umwandlung durch die erste anwendbare der folgenden Regeln:<br />

1. Ist ein Operand long double: Umwandlung nach long double<br />

2. Ist ein Operand double: Umwandlung nach double<br />

3. Ist ein Operand float: Umwandlung nach float<br />

4. Anwenden der unären Umwandlungen auf beide Operanden und<br />

anschließend höchstens eine der folgenden Regeln:<br />

- Ist ein Operand unsigned long int: Umwandlung nach unsigned long int<br />

- Ist ein Operand long int und der anderen unsigned int:<br />

- Sind alle unsigned int Werte in long darstellbar, dann long<br />

- Ansonsten unsigned long int<br />

- Ist ein Operand long int: Umwandlung nach long int<br />

- Ist ein Operand unsigned int: Umwandlung nach unsigned int<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

212


Binäre Umwandlungen<br />

Anwendung bei den meisten binären Operatoren und auf das 2. Und 3. Argument<br />

des :-Operators. Zuerst wird auf beiden Operanden getrennt eine unäre<br />

Umwandlung durchgeführt. Dann wird die erste der folgenden Regeln angewandt:<br />

Ein Operandentyp<br />

Andere Operandentyp<br />

Umwandlung nach<br />

long double<br />

double<br />

float<br />

unsigned Typ<br />

signed Typ<br />

unsigned Typ<br />

unsigned Typ<br />

unsigned Typ<br />

beliebiger Typ<br />

Reeller Typ<br />

reeller Typ<br />

reeller Typ<br />

unsigned Typ<br />

signed Typ<br />

signed Typ mit Rang, kann alle<br />

Werte des unsigned darstellen<br />

signed Typ mit > Rang, kann nicht<br />

alle Werte des unsigned darstellen<br />

beliebiger Typ<br />

long double<br />

double<br />

float<br />

unsigned Typ mit höherem Rang<br />

signed Typ mit höherem Rang<br />

unsigned Typ<br />

signed Typ<br />

unsigned Version von signed Typ<br />

keine Umwandlung<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

213


<strong>Beispiel</strong><br />

/* unäre Umwandlungen */<br />

/* binäre Umwandlungen */<br />

int i;<br />

short s = 1;<br />

long int il;<br />

unsigned long int iul;<br />

float f;<br />

f = 1.0 + 1.0ld; /* f = (float) ( (long double)1.0 + 1.0ld ) */<br />

f = 1 + 1.0f; /* f = (float)1 + 1.0f */<br />

i = s + 1; /* i = (int)s + 1 */<br />

i = s + 1u; /* i = (int) ( (unsigned int)(int)s + 1u ) */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

214


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

215


Ein/Ausgabe<br />

Dateien sind eine Folge von Bytes ohne jegliche Struktur. Lediglich<br />

Textdateien (s.u.) haben eine Zeilenstruktur dadurch, dass dem Zeichen '\n'<br />

eine besondere Bedeutung zukommt.<br />

Um mit einer Datei zu arbeiten braucht man einen Dateibeschreiber vom<br />

Typ FILE *, der in definiert ist. Solch einen Beschreiber bekommt<br />

man beim Öffnen einer Datei.<br />

Es gibt drei bereits vorhandene und geöffnete Dateien ():<br />

1. stdin Standardeingabe<br />

2. stdout Standardausgabe<br />

3. stderr Standardfehlerausgabe<br />

Eine Datei kann in einer von zwei Modi geöffnet werden:<br />

1. Formatierte Ein-/Ausgabe<br />

2. Unformatierte Ein-/Ausgabe<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

216


Öffnen einer Datei<br />

FILE *fopen(const char *filename, const char *mode);<br />

filename ist der Name der zu öffnenden Datei in der Notation des jeweiligen<br />

Systems (Unix: "/tmp/datei.dat", Windows: "C:\\tmp\\datei.dat").<br />

mode ist der Öffnungsmodus mit einer Kombination der Buchstaben (s.u.):<br />

r Von Datei nur lesen<br />

w Auf Datei nur schreiben<br />

a Auf Datei nur schreibend anfügen<br />

b Binärdatei (Default: Textdatei)<br />

+ Sowohl schreiben als auch lesen möglich (erste Operation entscheidet)<br />

fopen liefert einen Dateibeschreiber vom Typ FILE * zurück. Falls ein Fehler<br />

aufgetreten ist, ist dies NULL.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

217


Öffnungsmodus<br />

"r"<br />

"w"<br />

"a"<br />

"rb"<br />

"wb"<br />

"ab"<br />

"r+"<br />

"w+"<br />

"a+"<br />

"rb+"<br />

"wb+"<br />

"ab+"<br />

Existierende Textdatei zum Lesen öffnen.<br />

Textdatei zum Schreiben öffnen. Falls Datei existiert, wird Inhalt gelöscht.<br />

Textdatei zum Anfügen öffnen. Falls keine Datei existiert, erzeugen.<br />

Binärdatei; s.o.<br />

Binärdatei; s.o.<br />

Binärdatei; s.o.<br />

Existierende Textdatei zum Lesen oder Schreiben öffnen.<br />

Textdatei zum Lesen oder Schreiben öffnen. Falls Datei existiert, wird Inhalt<br />

gelöscht.<br />

Textdatei zum Lesen oder Anfügen öffnen.<br />

Binärdatei; s.o.<br />

Binärdatei; s.o.<br />

Binärdatei; s.o.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

218


Öffnungsmodus<br />

Eigenschaft<br />

Modus<br />

r<br />

w<br />

a<br />

r+<br />

w+<br />

a+<br />

Datei muss existieren<br />

ja<br />

nein<br />

nein<br />

ja<br />

nein<br />

nein<br />

Inhalt einer existierenden Datei verloren<br />

nein<br />

ja<br />

nein<br />

nein<br />

ja<br />

nein<br />

Lesen erlaubt<br />

ja<br />

nein<br />

nein<br />

ja<br />

ja<br />

ja<br />

Schreiben erlaubt<br />

nein<br />

ja<br />

ja<br />

ja<br />

ja<br />

ja<br />

Schreiben beginnt am Ende<br />

nein<br />

nein<br />

ja<br />

nein<br />

nein<br />

ja<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

219


Weitere Dateioperationen<br />

int fclose(FILE *f);<br />

Schließt die Datei. Liefert 0, falls kein Fehler auftrat.<br />

int fflush(FILE *f);<br />

Synchronisiert interne Puffer mit der Datei.<br />

int remove(const char *filename);<br />

Löscht die Datei mit dem Namen filename. Liefert 0, wenn ok.<br />

int rename(const char *old, const char *new);<br />

Benennt Datei old in new um. Liefert 0, wenn ok.<br />

FILE *tmpfile(void);<br />

Erzeugt temporäre Datei im Modus "wb+". Datei wird automatisch gelöscht,<br />

wenn Datei geschlossen oder Programm beendet wird. Liefert 0, wenn nicht ok.<br />

char *tmpname(char *s);<br />

Erzeugt nicht-existierenden Dateinamen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

220


<strong>Beispiel</strong><br />

#include <br />

int main(int argc, char **argv)<br />

{<br />

FILE *f;<br />

/* Öffnen der Datei */<br />

f = fopen(argv[1], "w");<br />

if(f == NULL) exit(EXIT_FAILURE);<br />

/* Schreiben */<br />

fprintf(f, "Hallo\n");<br />

printf("Hallo");<br />

/* Rausschreiben des Schreibpuffers für stdout */<br />

fflush(stdout);<br />

}<br />

/* Datei schließen */<br />

if( fclose(f) != 0) exit(EXIT_FAILURE);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

221


Formatierte Ausgabe<br />

int fprintf(FILE *f, char *format, ...);<br />

int printf(char *format, ...);<br />

int sprintf(char *str, char *format, ...);<br />

Schreibt eine Anzahl von Werten auf die Datei f bzw. stdout bzw. str. Die<br />

Anzahl wird implizit durch den Formatstring vorgegeben. Liefert Anzahl<br />

geschriebener Zeichen, falls ok, ansonsten einen negativen Wert.<br />

Der Formatstring wird zeichenweise ausgegeben, bis ein % erscheint, was<br />

eine Formatangabe einleitet. Für jede Formatangabe wird eine Werteangabe<br />

in ... benötigt.<br />

Eine Formatangabe besteht aus % gefolgt von optionalen Zusatzangaben<br />

Flags<br />

Minimale Feldweite<br />

Präzision<br />

Zusatzangaben zu short/long<br />

gefolgt von genau einem Formatbuchstaben.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

222


Formatbuchstaben<br />

Buchstabe<br />

d, i<br />

x,X<br />

o<br />

u<br />

c<br />

s<br />

f<br />

e,E<br />

g,G<br />

a, A<br />

p<br />

%<br />

n<br />

Argumenttyp<br />

int<br />

unsigned int<br />

unsigned int<br />

unsigned int<br />

int<br />

char *<br />

double<br />

double<br />

double<br />

double<br />

void *<br />

-<br />

int *<br />

Ausgabe<br />

Dezimalzahl<br />

Hexadezimalzahl<br />

Oktalzahl<br />

Dezimalzahl (ohne Vorzeichen)<br />

Zeichen<br />

String<br />

Reelle Zahl im Format [-]m.dddddd<br />

Reelle Zahl im Format [-]m.dddddde[+|-]dd<br />

Wie %e, falls Exponent klein, sonst %f.<br />

Reelle Zahl in hexadezimaler Notation<br />

Zeigerwert als (impl.abhängige) Dezimalzahl<br />

%-Zeichen<br />

Keine Ausgabe! Nächstem Argument wird Anzahl<br />

der bis jetzt ausgegebenen Zeichen zugewiesen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

223


<strong>Beispiel</strong><br />

#include <br />

int main(int argc, char **argv)<br />

{<br />

int i;<br />

printf("%d %x %c %s\n", 4711, 17u, 'a', "hallo");<br />

}<br />

printf("%f %p %n\n", 3.14, &i, &i);<br />

Ausgabe:<br />

4711 11 a hallo<br />

3.140000 0xbffffa64<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

224


Optionale Zusatzangaben<br />

1) Flags (Auswahl):<br />

- linksbündige Ausgabe<br />

+ Zahlendarstellung beginnt immer mit Vorzeichen<br />

Leerzeichen Zahlendarstellung beginnt mit Leer- oder Minuszeichen<br />

0 0 statt Leerzeichen als Füllzeichen<br />

2) Minimale Feldweite in Form einer Dezimalzahl<br />

Mit Füllzeichen bis zur Feldweite auffüllen<br />

3) Präzision in der Form .Dezimalzahl<br />

Bei Fließkommaformaten: Nachkommastellen<br />

Bei int: minimale Anzahl von Ziffern<br />

Bei char *: maximale Feldweite<br />

4) Typangaben (Auswahl):<br />

ll long long bei int-Formaten erwartet<br />

h/l short bzw. long Argument werden bei int-Formaten erwartet<br />

L long double Argument bei Fließkomma-Formaten erwartet<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

225


<strong>Beispiel</strong><br />

#include <br />

int main(int argc, char **argv)<br />

{<br />

printf("%5.3f\n", 3.141592654);<br />

printf("%10d\n", 1);<br />

printf("%-10d\n", 1);<br />

}<br />

Ausgabe:<br />

3.141<br />

1<br />

1<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

226


int fscanf(FILE *f, char *format, ...);<br />

int scanf(char *format, ...);<br />

Int sscanf(char *str, char *format, ...);<br />

Formatierte Eingabe<br />

Liest eine Anzahl von Werten von der Datei f bzw. stdout bzw. str. Die Anzahl<br />

wird implizit durch den Formatstring vorgegeben. Liefert Anzahl gelesener<br />

Werte (nicht Zeichen), falls ok, ansonsten EOF.<br />

Der Formatstring wird zeichenweise durchgearbeitet. Leerzeichen werden im<br />

Format wie in der Eingabe ignoriert. Zeichen in der Formatangabe ungleich %<br />

müssen auch in der Eingabe entsprechende Zeichen haben. Ein %-Zeichen<br />

leitet eine Formatangabe ein. Für jede Formatangabe wird ein entsprechender<br />

Zeiger (!) in ... benötigt.<br />

Eine Formatangabe besteht aus % gefolgt von optionalen Zusatzangaben<br />

Maximale Feldweite (Integer-Zahl)<br />

Zusatzangaben wie z.B. short/long (h,l,L)<br />

gefolgt von genau einem Formatbuchstaben.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

227


Formatbuchstaben<br />

Buchstabe<br />

d<br />

i<br />

o<br />

x,X<br />

u<br />

c<br />

s<br />

e,f,g<br />

%<br />

p<br />

[<br />

Argumenttyp<br />

int *<br />

int *<br />

int *<br />

int *<br />

unsigned int *<br />

char *<br />

char *<br />

float *<br />

-<br />

void **<br />

char *<br />

Eingabe<br />

Dezimalzahl<br />

Dezimal-, Oktal oder Hexadezimalzahl<br />

Oktalzahl<br />

Hexadezimalzahl<br />

Dezimalzahl ohne Vorzeichen<br />

Ein Zeichen (vorangehende Leerzeichen ignorieren)<br />

String (vorangehende Leerzeichen ignorieren). String ist<br />

bei Leerzeichen beendet.<br />

Fließkommazahl (Vorsicht:: float * und nicht double *)<br />

% selber<br />

Implementierungsabhängige Zeigerdarstellung<br />

Angabe einer Menge von erlaubten Eingabezeichen<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

228


<strong>Beispiel</strong> 1<br />

#include <br />

int main(int argc ,char **argv)<br />

{<br />

int i;<br />

float f;<br />

char str[10];<br />

scanf("%d %f", &i, &f);<br />

printf("%d %f\n", i, f);<br />

}<br />

scanf("%s", str);<br />

printf("%s\n", str);<br />

Eingabe:<br />

3 -3.14<br />

abc def<br />

Ausgabe:<br />

3 -3.14<br />

abc<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

229


<strong>Beispiel</strong> 2<br />

#include <br />

int main(int argc ,char **argv)<br />

{<br />

char str[10];<br />

// Lese max. 9 Ziffern ein, '\0' wird angehängt<br />

scanf("%9[0123456789]", str);<br />

}<br />

// Lese alles außer Ziffern ein, max 9 Zeichen, '\0' wird angehängt<br />

// ^ bedeutet Negation<br />

scanf("%9[^0123456789]", str);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

230


Weitere Funktionen<br />

int fgetc(FILE *f);<br />

int fputc(int c, FILE *f);<br />

Liest bzw. schreibt nächstes Zeichen (EOF, wenn Fehler).<br />

char *fgets(char *s, int n, FILE *stream);<br />

Höchstens n-1 Zeichen werden aus einer Zeile eingelesen und mit<br />

\0 abgeschlossen<br />

char *fputs(const char *s, FILE *f);<br />

Schreibt s auf Datei.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

231


Unformatierte Ein-Ausgabe<br />

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *f);<br />

Liest nmemb Elemente jeweils der Größe size aus der Datei<br />

und speichert diese ab ptr im Speicher.<br />

size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *f);<br />

Schreibt nmemb Elemente jeweils der Größe size aus dem<br />

Speicher ab ptr auf die Datei.<br />

int fseek(FILE *f, long int offset, int whence);<br />

Positioniert den internen Dateizeiger auf die Position, die offset<br />

addiert mit whence ergibt, wobei whence sein kann:<br />

SEEK_SET: Dateianfang<br />

SEEK_CUR: aktuelle Position in Datei<br />

SEEK_END: Dateiende<br />

void rewind(FILE *f);<br />

Setzt den Dateizeiger auf den Anfang der Datei.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

232


<strong>Beispiel</strong><br />

/* ohne Fehlerüberprüfungen! */<br />

#include <br />

int main(int argc, char **argv)<br />

{<br />

FILE *f;<br />

int ifeld[10] = {,0,1,2,3,4,5,6,7,8,9};<br />

/* Schreiben des gesamten Feldes auf Datei */<br />

f = fopen("test.dat", "wb");<br />

fwrite(ifeld, sizeof(ifeld[0]), 10, f);<br />

fclose(f);<br />

}<br />

/* Lesen des gesamten Feldes von Datei */<br />

f = fopen("test.dat", "rb");<br />

fread(ifeld, sizeof(ifeld[0]), 10, f);<br />

fclose(f);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

233


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

234


Standardbibliotheken<br />

In der Bibliothek libc.a (automatisch eingebunden) und libm.a sind alle ANSI-C<br />

Funktionen enthalten. Wenn man eine Funktion nutzen will, sollte man die<br />

entsprechende Include-Datei xyz.h einbinden.<br />

Hier werden nur die wichtigsten Funktionen besprochen.<br />

Online-Dokumentation über "man abc" für Funktion abc.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

235


Headerdateien (1)<br />

Headername<br />

assert.h<br />

complex.h<br />

ctype.h<br />

errno.h<br />

fenv.h<br />

float.h<br />

inttypes.h<br />

iso646.h<br />

limits.h<br />

locale.h<br />

math.h<br />

setjmp.h<br />

Funktion<br />

Zusicherungen<br />

Komplexe Zahlen<br />

Zeichenklassen (islower, isupper etc.)<br />

letzte Fehlernummer (in errno)<br />

low-level Kontrolle über Fließkommaverhalten (z.B. Rundung)<br />

Angaben zu Fließkommawerten (z.B. FLT_MAX)<br />

Ausgabeformatierung von extended int-Typen<br />

Makros für einige Operatoren (z.B. not_eq für !=)<br />

Angaben zu int-artigen Werten (z.B. INT_MAX)<br />

Lokalisierung (z.B. Währungszeichen)<br />

Mathematikfunktionen (z.B. sin, cos)<br />

Nichtlokale Sprünge<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

236


Headerdateien (2)<br />

Headername<br />

signal.h<br />

stdarg.h<br />

stdbool.h<br />

stddef.h<br />

stdint.h<br />

stdio.h<br />

stdlib.h<br />

string.h<br />

tgmath.h<br />

time.h<br />

wchar.h<br />

wctype.h<br />

Funktion<br />

Signale<br />

Variable Argumentlisten<br />

Boolsche Werte<br />

Grundlegende Typen und Konstanten (z.B. NULL, size_t)<br />

extended int Typen<br />

Ein-/Ausgabe<br />

Nützliche Funktionen (z.B. malloc, qsort, system, getenv)<br />

String-Funktionen<br />

Typ-generische mathematische Makros (sin für float, double,...)<br />

Zeit- und Datumsfunktionen<br />

Wide und Multibyte Character<br />

Wide und Multibyte Character<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

237


Zusicherungen mit <br />

<strong>Beispiel</strong>:<br />

#include <br />

#include <br />

/* #define NDEBUG */<br />

int fun(int arg)<br />

{<br />

// Diese Funktion ist nur für Argumente mit 0 < arg < 5000 spezifiziert<br />

assert((arg > 0) && (arg < 5000));<br />

return 2*arg;<br />

}<br />

int main(int argc, char **argv)<br />

{<br />

int i, *ip;<br />

ip = (int *) malloc(sizeof(*ip));<br />

assert(ip != NULL); // Das würde man besser mit if() abprüfen<br />

}<br />

i = fun(4711);<br />

assert(i > 0);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

238


Fehlersuche mit <br />

cc -DNDEBUG prog.c<br />

cc prog.c<br />

übersetzt Programm ohne die Überprüfung.<br />

übersetzt das Programm mit Überprüfung<br />

Verwenden Sie assertions insbesondere bei umfangreichen Programmen:<br />

1. Überprüfen, ob Funktionsargumente wirklich sinnvolle Werte haben<br />

2. Überprüfen, ob Funktionsresultate wirklich sinnvolle Werte haben<br />

Übersetzen Sie die Produktionsversion ihres Programms mit -DNDEBUG<br />

und die Testversion ohne diese Option.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

239


Signale <br />

Signale dienen zur asynchronen Programmausführung, z.B. im Fehlerfall. C<br />

kennt mindestens die folgenden Signale:<br />

Signalname<br />

SIGABRT<br />

SIGFPE<br />

SIGILL<br />

SIGINT<br />

SIGSEGV<br />

SIGTERM<br />

Bedeutung<br />

Fehlerhafte Programmbeendung (z.B. über abort())<br />

Fließkommafehler (z.B. Division durch 0)<br />

Illegale Machineninstruktion<br />

Ctl-C gedrückt<br />

Illegaler Speicherzugriff<br />

Signal zur Programmbeendigung<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

240


Signale <br />

Zu jedem Signal kann man einen Signal-Handler (eine Funktion) angeben, die<br />

bei Auftreten des Signals aufgerufen wird. Angabe über:<br />

void (*signal ( int sig, void (*func)(int))) (int);<br />

int raise(int sig);<br />

<strong>Beispiel</strong>:<br />

#include <br />

#include <br />

int a, b=0, c=0;<br />

void sig_handler(int sig) {<br />

printf("Fehler erkannt\n");<br />

c = 1;<br />

}<br />

Ausgabe:<br />

> a.out<br />

Fehler erkannt<br />

Bis jetzt läuft alles gut.<br />

Floating point exception (core dumped)<br />

int main(int argc, char **argv) {<br />

(void)signal(SIGFPE,sig_handler);<br />

a = b / c;<br />

printf("Bis jetzt läuft alles gut.\n");<br />

c = 0;<br />

(void)signal(SIGFPE, SIG_DFL);<br />

a = b / c;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

241


Mathematische Funktionen (1) <br />

Prototyp<br />

double acos( double x );<br />

double asin( double x );<br />

double atan( double x );<br />

double atan2( double y, double x );<br />

double cos( double x );<br />

double sin( double x );<br />

double tan( double x );<br />

double cosh( double x );<br />

double sinh( double x );<br />

double tanh( double x );<br />

double exp( double x );<br />

Beschreibung<br />

Arcuscosinus (Hauptwert)<br />

Arcussinus (Hauptwert)<br />

Arcustangens x (Hauptwert)<br />

Arcustangens y/x (Hauptwert)<br />

Cosinus (in Radianten)<br />

Sinus (in Radianten)<br />

Tangens (in Radianten)<br />

Cosinus Hyperbolicus<br />

Sinus Hyperbolicus<br />

Tangens Hyperbolicus<br />

Exponentialfunktion e x<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

242


Mathematische Funktionen (2) <br />

Prototyp<br />

double pow( double x, double y);<br />

double sqrt( double x );<br />

double ceil( double x );<br />

double floor( double x );<br />

double fabs( double x );<br />

double fmod( double x, double y );<br />

double modf( double x, double *ptr);<br />

double log( double x );<br />

double log10( double x );<br />

double ldexp( double x, int exp );<br />

double frexp( double x, int *exp);<br />

Beschreibung<br />

x y<br />

Wurzel x<br />

Kleinster ganzzahliger Wert nicht kleiner als x<br />

größter ganzzahligert Wert nicht größer als x<br />

Absolutwert<br />

Rest von x/y<br />

Ganzzahliger Teil von x ist Ergebnis, Nachkommateil<br />

in *ptr<br />

log e (x)<br />

log 10 (x)<br />

x * 2 exp<br />

Aufspalten von x in normalisierten Bruchteil in [0.5,1)<br />

oder 0 und Potenz von 2 in *exp (entspricht ldexp -1 )<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

243


Typgenerische Mathematikmakros<br />

• Bis C89 gab es die mathematischen Bibliotheksfunktionen nur für<br />

double<br />

• Ab C99 gibt es die typgenerischen Makros für Fließkommatypen in<br />

tgmath.h, die entsprechend dem Argumenttyp eine andere Funktion<br />

aufrufen (z.B. steht hinter dem Makro sin dann sinf, sin, sinl für ein<br />

float, double bzw. long double Argument).<br />

<strong>Beispiel</strong>:<br />

#include <br />

int main(int argc, char **argv) {<br />

float f = 1.0f;<br />

double d = 1.0;<br />

long double l = 1.0L;<br />

}<br />

f = sin(f);<br />

d = sin(d);<br />

l = sin(l);<br />

// Hier wird sinf aufgerufen<br />

// hier wird sin aufgerufen<br />

// hier wird sinl aufgerufen<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

244


Zeichenfunktionen <br />

Prototyp<br />

int isalnum( int c );<br />

int isalpha( int c);<br />

int iscntrl( int c);<br />

int isdigit( int c );<br />

int isgraph( int c );<br />

int isprint( int c );<br />

int ispunct( int c );<br />

int isspace( int c );<br />

int isupper( int c );<br />

int islower( int c );<br />

int isxdigit( int c );<br />

int tolower( int c );<br />

int toupper( int c );<br />

Beschreibung<br />

Wahr, falls c Buchstabe oder Ziffer<br />

Wahr, falls c Buchstabe<br />

Wahr, falls c Kontrollzeichen<br />

Wahr, falls c Ziffer<br />

Wahr, falls c druckbares Zeichen ungleich Leerzeichen<br />

Wahr, falls c druckbares Zeichen<br />

Wahr falls c druckbares Zeichen und kein Buchstabe, Ziffer, Leerz.<br />

Wahr, falls c kein Leerzeichen<br />

Wahr, falls c Großbuchstabe<br />

Wahr, falls c Kleinbuchstabe<br />

Wahr, falls c Hexadezimalzeichen (0-9, a-f, A-F)<br />

Liefert Großbuchstaben, wenn c Kleinbuchstabe. Sonst c.<br />

Liefert Kleinbuchstaben, wenn c Großbuchstabe. Sonst c.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

245


Stringfunktionen <br />

Prototyp<br />

void *memcpy(void *s1, const void *s2, size_t n);<br />

void *memmove(void *s1, const void *s2, size_t n);<br />

int memcmp(const void *s1, const void *s2, size_t n);<br />

void *memchr(const void *s, int c, size_t n);<br />

void *memset(void *s, int c, size_t n);<br />

Beschreibung<br />

Kopiert n Zeichen von s2 nach s1<br />

Kopiert n Zeichen von s2 nach s1<br />

(Überlappung erlaubt)<br />

Vergleicht n Zeichen von s1 und s2<br />

Sucht Zeichen c ab Position s.<br />

Liefert gefundene Position/NULL<br />

Füllt Speicherbereich mit c<br />

<strong>Beispiel</strong>:<br />

#include <br />

int a[1000000], b[1000000];<br />

int main(int argc, char **argv) {<br />

int i;<br />

for(i = 0; i < 1000000; i++) a[i] = i;<br />

memcpy(a, b, 100000 * sizeof(int));<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

246


Stringfunktionen <br />

Prototyp<br />

char *strcpy(char *s1, const char *s2);<br />

char *strcat(char *s1, const char *s2);<br />

int strcmp(const char *s1, const char *s2);<br />

char *strchr(const char *s, int c);<br />

char *strstr(const char *s1, const char *s2);<br />

int strlen(const char *s);<br />

Beschreibung<br />

Kopiert s2 nach s1<br />

Fügt s2 an s1 an<br />

Vergleicht s1 mit s2. Liefert -1,0,1<br />

Sucht c in s und liefert Zeiger/NULL.<br />

Sucht s2 in s1. Liefert Zeiger/NULL.<br />

Länge des Strings (ausschließlich \0)<br />

<strong>Beispiel</strong>:<br />

#include <br />

int main(int argc, char **argv) {<br />

char s1[10], *s2 = "hallo";<br />

}<br />

printf("Laenge=%d, kommt vor:%p \n", strlen(s2), strstr(s2, "ll"));<br />

strcpy(s1, s2);<br />

/* nicht möglich: strcpy(s2, s1); */<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

247


Zeit und Datum <br />

Es existieren 3 Datentypen zur Darstellung von Datum und Zeiten:<br />

clock_t<br />

time_t<br />

struct tm<br />

Skalarer Wert für CPU-Zeit<br />

Skalarer Wert für kompakte Darstellung von struct tm<br />

Strukturierter Wert mit mindestens den folgenden Komponenten<br />

int tm_sec; Sekunden (0-59)<br />

int tm_min; Minuten (0-59)<br />

int tm_hour; Stunden seit Mitternacht (0-23)<br />

int tm_mday; Tag des Monats (1-31)<br />

int tm_mon; Monat des Jahres (0-11)<br />

int tm_year; Jahr seit 1900<br />

int tm_wday; Wochentag seit Sonntag (0-6)<br />

int tm_yday; Tag seit Jahresanfang (0-365)<br />

int tm_isdst; Sommerzeit-Flag (>0 falls Sommerzeit)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

248


Zeit und Datum <br />

Prototyp<br />

clock_t clock(void);<br />

time_t time(time_t *timer);<br />

double difftime(time_t t1, time_t t2);<br />

time_t mktime(struct tm *timeptr);<br />

char *asctime(const struct tm *t);<br />

struct tm *localtime(const time_t *t);<br />

struct tm *gmtime(const time_t<br />

*timer);<br />

Beschreibung<br />

CPU-Zeit in Ticks. Dividiert durch<br />

CLOCKS_PER_SEC liefert CPU-Zeit in s.<br />

Vorsicht: int-Division!<br />

Ermittelt aktuelle Daten. Falls timer!=NULL, wird dort<br />

ebenfalls abgespeichert.<br />

Differenz in Sekunden<br />

Umwandlung struct tm nach time_t<br />

Umwandlung Zeit nach String (in statischen Bereich)<br />

Umwandlung time_t nach struct tm mit lokaler<br />

Zeitzone<br />

Umwandlung time_t nach struct tm mit Zeitzone UTC<br />

size_t strftime(char *s, size_t<br />

maxsize, const char *format, const<br />

struct tm *t);<br />

Umwandlung von Daten mit mit<br />

Formatierungsangaben<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

249


<strong>Beispiel</strong><br />

#include <br />

#include <br />

int main(int argc, char **argv) {<br />

time_t t;<br />

clock_t t0, t1;<br />

int i; double a = 0.0;<br />

/* CPU-Zeit Start und aktuelle Zeit */<br />

t0 = clock();<br />

t = time(NULL);<br />

/* in lokale Zeit und nach String umwandeln */<br />

printf("Lokale Zeit und Datum sind: %s\n", asctime(localtime(&t)));<br />

}<br />

/* CPU-Zeit verbrauchen und Gesamt-CPI-Zeit ausgeben */<br />

for(i=0; i < 1000000; i++) a += sin(i);<br />

printf("verbrauchte CPU-Zeit: %f s\n", (clock()-t0)/(double)CLOCKS_PER_SEC);<br />

Ausgabe:<br />

Lokale Zeit und Datum sind: Fri Nov 3 09:12:49 2000<br />

Der Wert von a ist 0.232884<br />

verbrauchte CPU-Zeit: 0.260000 s<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

250


Speicherverwaltung <br />

Prototyp<br />

void *malloc(size_t size);<br />

void *calloc(size_t nmemb, size_t size);<br />

void free(void *ptr);<br />

void *realloc(void *ptr, size_t size);<br />

Beschreibung<br />

Legt neuen Speicherbereich im Heap an.<br />

Legt neuen Speicherbereich (mit 0) im Heap an<br />

Gibt Speicherbereich wieder frei<br />

Vergrößert/verkleinert Speicherbereich, evtl. an<br />

anderer Stelle im Speicher<br />

<strong>Beispiel</strong>:<br />

#include <br />

int main(int argc, char **argv)<br />

{<br />

int *ptr_i, *ptr_j;<br />

}<br />

ptr_i = (int *)malloc(sizeof(*ptr_i));<br />

*ptr_i = 4711;<br />

ptr_i = (int *)realloc(ptr_i, 2 * sizeof(*ptr_i));<br />

free(ptr_i);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

251


Schnittstelle zum Betriebssystem <br />

Prototyp<br />

void exit(int status);<br />

void abort(void);<br />

int atexit(void (*func)(void));<br />

char *getenv(const char *name);<br />

int system(const char *str);<br />

Beschreibung<br />

Beendet die Programmausführung<br />

Erzeugt SIGABRT-Signal und beendet<br />

Programm mit Fehlercode<br />

Teilt dem System mit, dass vor dem<br />

eigentlichen Programmende die<br />

Funktion func aufgerufen werden soll<br />

Liefert einen String/NULL auf den Wert<br />

der Umgebungsvariable name<br />

Ruft den Kommandointerpreter (shell)<br />

des Betriebssystems mit dem<br />

Kommando str auf.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

252


<strong>Beispiel</strong> <br />

#include <br />

#define UVAR "PS1" /* Name einer Umgebungsvariablen */<br />

/* Diese Funktion soll vor Programmende ausgeführt werden */<br />

void exit_fun(void)<br />

{<br />

printf("Jetzt geht es langsam zu Ende\n");<br />

}<br />

int main(int argc, char **argv)<br />

{<br />

char *wert;<br />

/* exit_fun soll vor Programmende ausgeführt werden */<br />

atexit(exit_fun);<br />

}<br />

/* Umgebungsvariable suchen */<br />

if( (wert = getenv(UVAR)) == NULL)<br />

printf("Keine Umgebungsvariable %s bekannt\n", UVAR);<br />

else<br />

printf("Wert von %s ist %s\n", UVAR, wert);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

253


Sortieren <br />

void qsort(const void *base, size_t nmemb, size_t size,<br />

int (*compar)(const void *, const void *));<br />

<strong>Beispiel</strong>:<br />

#include <br />

int mycompare(const void *arg1, const void *arg2) {<br />

return *(int*)arg1 - *(int *)arg2;<br />

}<br />

int main(int argc, char **argv) {<br />

int ifeld[10] = {9,8,7,6,5,4,3,2,1,0};<br />

}<br />

qsort((const void *)ifeld, 10, sizeof(ifeld[0]), mycompare);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

254


Binäres Suchen <br />

void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,<br />

int (*compar)(const void *, const void *));<br />

<strong>Beispiel</strong>:<br />

#include <br />

int mycompare(const void *arg1, const void *arg2) {<br />

return *(int*)arg1 - *(int *)arg2;<br />

}<br />

int main(int argc, char **argv) {<br />

int ifeld[10] = {0,1,2,3,4,5,6,7,8,9};<br />

int suchen = 2;<br />

}<br />

if(bsearch((const void *)&suchen, (const void *)ifeld, 10,<br />

sizeof(ifeld[0]), mycompare) == NULL)<br />

exit(EXIT_FAILURE);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

255


Zufallszahlen <br />

Prototyp<br />

int rand(void);<br />

void srand(unsigned int seed);<br />

Beschreibung<br />

Liefert eine Zufallszahl zwischen 0 und<br />

RAND_MAX. Bei Normierung auf [0,1)<br />

muss man mit der Integer-Division<br />

aufpassen.<br />

Initialisiert den Zufallszahlengenerator<br />

mit dem Startwert seed. So kann man<br />

wiederholt die gleiche Folge von<br />

Zufallszahlen erzeugen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

256


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

257


Modulare Programmierung<br />

• Nur Bezeichner global bekannt machen, die auch außerhalb der .c-<br />

Datei genutzt werden sollen. Dies kann man bei Objekten dadurch<br />

erreichen, dass alle anderen (lokalen) Objektdefinition (Variablen,<br />

Funktionen) auf dem top-level das Attribut static bekommen.<br />

• Alle global sichtbaren Bezeichner einer Datei test.c in einer Datei<br />

test.h mit vollem Typ und dem Zusatz extern deklarieren (nicht<br />

definieren!). Ebenfalls Typdefinitionen aus test.c, die global bekannt<br />

sein sollen, (nur) in test.h angeben.<br />

• In allen Dateien, die solch ein Objekt bzw. Typ nutzen (auch in der<br />

zugehörigen Definitionsdatei test.c) ein #include "test.h"<br />

einfügen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

258


<strong>Beispiel</strong>: bsp.c<br />

/* global sichtbare Deklarationen dieser Datei<br />

Auch hier ein include, damit der Compiler Inkonsistenzen prüfen kann.<br />

*/<br />

#include "bsp.h"<br />

/* global_var ist eine extern sichtbare Variable */<br />

int global_var = 5;<br />

/* local_var soll nur innerhalb der Datei bekannt sein */<br />

static int local_var = 3;<br />

/* local_fun soll nur innerhalb der Datei bekannt sein */<br />

static double local_fun(int y)<br />

{<br />

return 2.0 * local_var * global_var * y;<br />

}<br />

/* global_fun ist eine extern sichtbare Funktion */<br />

double global_fun( double x )<br />

{<br />

return local_fun(x) + x + global_var + local_var;<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

259


<strong>Beispiel</strong>: bsp.h<br />

/* global sichtbare Deklarationen der Datei bsp.c */<br />

/* global_var ist eine extern sichtbare Variable */<br />

extern int global_var;<br />

/* global_fun ist eine extern sichtbare Funktion */<br />

/* Wichtig: Deklaration hier, keine Definition */<br />

extern double global_fun( double x );<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

260


<strong>Beispiel</strong>: nutzung_bsp.h<br />

/* Nutzen der exportierten Deklarationen aus bsp.c */<br />

#include "bsp.h"<br />

int main(int argc, char **argv)<br />

{<br />

printf("Wert von global_var ist %d\n", global_var);<br />

printf("Und global_fun(3.0) ist %f\n", global_fun(3.0));<br />

}<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

261


<strong>Beispiel</strong>: Stack<br />

Stack.h:<br />

typedef int * Stack;<br />

extern Stack stack_create(int maxele);<br />

extern void stack_push(Stack s, int x);<br />

extern int stack_pop(Stack s);<br />

Stack.c:<br />

#include "Stack.h"<br />

/* keine Fehlerüberprüfung hier! */<br />

Stack stack_create(int maxele) {<br />

return (Stack)malloc(maxele*sizeof(*s));<br />

}<br />

void stack_push(Stack s, int x) {<br />

*(s++) = x;<br />

}<br />

Stacktest.c:<br />

#include "Stack.h"<br />

int main(int argc, char **argv)<br />

{<br />

Stack s;<br />

int ele;<br />

s = stack_create(10);<br />

stack_push(s, 10);<br />

ele = stack_pop(s);<br />

int stack_pop(Stack s) {<br />

return *(s--);<br />

}<br />

}<br />

/* hier wird es spannend */<br />

stack_push(s, 20.0);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

262


Makefiles<br />

Problem:<br />

1. Projekt besteht z.B. aus 300 .c-Dateien und 200 .h-Dateien<br />

2. Komplexe Abhängigkeiten zwischen .c und .h-Dateien<br />

3. Bei Änderungen an .c-Dateien möchte man nur geänderte Dateien neu<br />

übersetzen<br />

4. Bei Änderungen an .h-Dateien möchte man alle abhängigen Dateien neu<br />

übersetzen<br />

Lösung:<br />

Makefiles sind ein Hilfsmittel, um solche (und andere) Probleme zu lösen<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

263


Makefiles<br />

Wesentliches Element: Abhängigkeitsregel<br />

Ziel : Abhängigkeiten<br />

Kommando<br />

Falls eine der Abhängigkeiten (Dateien) jünger (im Sinne von modifiziert) als<br />

das Ziel ist, so wird das Kommando ausgeführt.<br />

<strong>Beispiel</strong>:<br />

datei.o: datei.c datei.h xyz.h<br />

cc -c datei.c<br />

Wichtige Syntaxregel:<br />

Vor dem Kommando muss ein TAB-Zeichen sein (keine Leerzeichen)!<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

264


<strong>Beispiel</strong><br />

# Makefile<br />

prog: test1.o test2.o<br />

cc -o prog test1.o test2.o<br />

test1.o: test1.c test1.h<br />

cc -c -O test1.c<br />

test2.o: test2.c test1.h<br />

cc -c -O test2.c<br />

Aufruf:<br />

> make<br />

oder<br />

> make prog<br />

/* Dies ist test1.c */<br />

#include "test1.h"<br />

void fun(void)<br />

{<br />

printf("Hallo\n");<br />

}<br />

/* Dies ist test2.c */<br />

#include "test1.h"<br />

int main(int argc, char **argv)<br />

{<br />

fun();<br />

}<br />

/* Dies ist test1.h */<br />

void fun(void);<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

265


Variablen<br />

# Makefile<br />

CC = cc<br />

CFLAGS = -O<br />

OBJECTS = test1.o test2.o<br />

prog: $(OBJECTS)<br />

$(CC) -o prog $(OBJECTS)<br />

test1.o: test1.c test1.h<br />

$(CC) -c $(CFLAGS) test1.c<br />

test2.o: test2.c test1.h<br />

$(CC) -c $(CFLAGS) test2.c<br />

clean:<br />

Packet:<br />

-rm $(OBJECTS) prog<br />

tar -cf alles.tar test1.c test2.c test1.h<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

266


# Makefile<br />

CC = cc<br />

CFLAGS = -O<br />

OBJECTS = test1.o test2.o<br />

prog: $(OBJECTS)<br />

$(CC) -o $@ $^<br />

test1.o: test1.c test1.h<br />

$(CC) -c $(CFLAGS) $<<br />

test2.o: test2.c test1.h<br />

$(CC) -c $(CFLAGS) $<<br />

Automatische Variablen<br />

clean:<br />

Packet:<br />

-rm $(OBJECTS) prog<br />

tar -cf alles.tar test1.c test2.c test1.h<br />

$@ Ziel der Regel<br />

$^ Namen aller Abhängigkeiten<br />

$< Name der 1. Abhängigkeit<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

267


Implizite Regeln<br />

# Makefile<br />

CC = cc<br />

CFLAGS = -O<br />

OBJECTS = test1.o test2.o<br />

prog: $(OBJECTS)<br />

$(CC) -o $@ $^<br />

test1.o: test1.c test1.h<br />

test2.o: test2.c test1.h<br />

clean:<br />

Packet:<br />

-rm $(OBJECTS) prog<br />

tar -cf alles.tar test1.c test2.c test1.h<br />

Implizite Regel:<br />

.o: .c<br />

$(CC) -c $(CPPFLAGS) $(CFLAGS) $<<br />

Weitere implizite Regeln für viele Dateiendungen.<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

268


Anmerkungen<br />

• make kann wesentlich mehr, als hier vorgestellt wurde<br />

• make ist wesentlich flexibler, als hier vorgestellt wurde<br />

• Nicht nur zum Übersetzen von Programmen geeignet<br />

• Sollte man generell zu jedem Projekt anlegen<br />

• Hierarchisch organisierte Projekte durch rekursiven Aufruf von make<br />

mit Makefiles in Unterverzeichnissen möglich<br />

• autoconf und automake zur Erstellung portabler Software<br />

(Fortgeschrittene)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

269


Programmierwerkzeuge<br />

• Umfangreiche Entwicklungsumgebungen (Microsoft, SUN, IBM, SGI, ...)<br />

• Neben dem reinen Compiler und Linker werden Programmierwerkzeuge<br />

angeboten:<br />

– Projektunterstützung<br />

– Dokumentation<br />

– Fehlersuche<br />

– Laufzeituntersuchung<br />

– ...<br />

• Hier sollen prinzipiellen Möglichkeiten gezeigt werden<br />

• Schon behandelt: Projektunterstützung mit Makefiles<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

270


Fehlersuche<br />

• Einfache (und sehr beschränkte) Möglichkeiten:<br />

– assert(x != 0); a = a / x;<br />

– Signal-Handler<br />

• Werkzeug zur Fehlersuche: Debugger<br />

• Kommandozeilenorientierte Debugger (z.B. gdb) oder grafisches<br />

Frontend (z.B. ddd)<br />

• Vorgehensweise:<br />

– Übersetzen und Linken des Quellcodes mit -g (Optimierung evtl.<br />

ausschalten). <strong>Beispiel</strong>: cc -g meintest.c<br />

– Starten des Debuggers mit dem Namen des ausführbaren Programms als<br />

Argument. <strong>Beispiel</strong>: gdb a.out<br />

– Starten des ausführbaren Programms unter der Aufsicht des Debuggers.<br />

<strong>Beispiel</strong>: > run<br />

– Analysieren der Fehler<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

271


Debugger-Kommandos<br />

Debugger-Kommandos können nur eingegeben werden, wenn das<br />

Programm nicht läuft.<br />

1. Breakpoint<br />

Programm hält an, wenn es während des Programmlaufs an diese Stelle<br />

gelangt. Man kann dann im Debugger weitere Kommandos ausführen<br />

(z.B. Variableninhalte sich anschauen).<br />

Syntax: break [Dateiname:]Zeilennummer<br />

<strong>Beispiel</strong>: break meintest.c:75<br />

2. Einzelschrittausführung<br />

Nur der nächste Befehl (bezogen auf das Source-Programm) wird<br />

ausgeführt. Unterscheidung:<br />

next: Es werden Funktionsaufrufe nicht verfolgt.<br />

step: Bei einem Funktionsaufruf wird in die Funktion gesprungen.<br />

3. Anzeigen der Aufrufhierarchie mit where<br />

4. Anzeigen des Source-Codes mit list<br />

Syntax: list [Dateiname:]Zeilennummer<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

272


Debugger-Kommandos<br />

1. Anschauen von Variableninhalten<br />

Mit print kann man sich Variableninhalte anschauen. Es sind auch<br />

komplexe Angaben möglich.<br />

<strong>Beispiel</strong>: print i<br />

print *p<br />

print p->daten[i]<br />

2. Verändern eines Variableninhalts<br />

<strong>Beispiel</strong>: set i=2<br />

set p->daten[i]=5<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

273


Programmoptimierung<br />

Nachdem (!) man eine korrekte Programmversion hat, sollte man bei oft und<br />

lang laufenden Programmen eine Optimierungsphase einfügen. Bei großen<br />

Programmpaketen ist eine Analyse des Laufzeitverhaltens allerdings<br />

schwierig.<br />

Ein einfacher und in vielen Fällen hilfreicher Ansatz ist das <strong>Prof</strong>iling, eine<br />

statistische Methode. Während des Programmlaufs wird in bestimmten<br />

Zeitintervallen (z.B. 10 ms) vom Laufzeitsystem überprüft, an welcher Stelle<br />

im Programm man sich zu diesem Zeitpunkt befindet. Diese Daten werden<br />

gesammelt. Diese Methode liefert für länger laufende Programme eine gute<br />

Basis zur Programmoptimierung.<br />

Vorgehensweise:<br />

1. Übersetzen und Linken des Programm mit -pg<br />

2. Ausführen des Programms mit repräsentativen Eingabedaten<br />

3. Nach dem Programmlauf existiert eine Datei gmon.out<br />

4. Analysieren des Laufzeitverhaltens mit: gprof a.out<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

274


<strong>Beispiel</strong><br />

Übersetzen und Ausführen:<br />

cc -O -pg meintest.c<br />

a.out<br />

gprof a.out >meintest.daten<br />

Analysieren der Ergebnisse:<br />

...<br />

flat profile:<br />

% cumulative self self total<br />

time seconds seconds calls ns/call ns/call name<br />

55.17 10.68 10.68 640000 16687.50 30125.00 BerechnePunkt<br />

44.42 19.28 8.60 187841276 45.78 45.78 c_abs_ohne_wurzel<br />

0.36 19.35 0.07 640000 109.38 109.38 ZeichnePunkt<br />

0.05 19.36 0.01 main<br />

0.00 19.36 0.00 1 0.00 0.00 hole_argumente<br />

0.00 19.36 0.00 1 0.00 0.00 init_display<br />

...<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

275


Inhalt<br />

• Einführung<br />

• Grundelemente der Sprache<br />

• Präprozessor<br />

• Anweisungen<br />

• Funktionen<br />

• Typen<br />

• Deklarationen<br />

• Ausdrücke<br />

• Ein-/Ausgabe<br />

• Standardbibliotheken<br />

• Modulare Programmierung<br />

• C++<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

276


C++<br />

• 1980-1984: Bjarne Stroustrup (AT&T Labs): C with Classes<br />

• 1983 Umbenennung C++<br />

• C++ zuletzt standardisiert in ISO/IEC 14882:1998<br />

• Dateiendung für C++:.cpp<br />

• C++ ist sehr (!) komplex<br />

• C++ ist eine Erweiterung von C (kleine Nichtschnittmenge),<br />

Programmierung in C++ unterscheidet sich aber wesentlich von der<br />

Programmierung in C<br />

• Objektorientierte Programmierung möglich (aber nicht zwingend):<br />

Klassen, Vererbung, Überladen<br />

• Standard Template Library STL (Container, Iteratoren)<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

277


Wesentliche Erweiterungen von C++ zu C<br />

• Klassen: class Complex {...};<br />

• Templates: array ia(20); array ic[10];<br />

• Namespaces (zusätzliche Hierarchie für Sichtbarkeit von Namen)<br />

• Überladen von Funktionen, Operatoren: feld1 = feld2 + feld3;<br />

• Exceptions<br />

• Run Time Type Information (RTTI)<br />

• Referenztypen (call-by-value, call-by-reference)<br />

• Statisches/dynamisches Binden von Funktionen<br />

• Ein-/Ausgabe mit <br />

• Speicherverwaltung mit new, delete<br />

Fachbereich Angewandte Informatik<br />

Programmieren in C<br />

<strong>Prof</strong>. <strong>Dr</strong>. <strong>Rudolf</strong> <strong>Berrendorf</strong><br />

278


<strong>Beispiel</strong><br />

using namespace std;<br />

class Complex {<br />

public:<br />

// Konstruktor<br />

Complex(double r=0.0, double i=0.0)<br />

{re=r; im=i;}<br />

// Destruktor<br />

~Complex(void) {}<br />

// Überladen von Funktionen<br />

double real(void) { return re; }<br />

double imag(void) { return im; }<br />

double real(double r) { re = r; }<br />

double imag(double i) { im = i; }<br />

using namespace std;<br />

int main(...) {<br />

Complex c1,<br />

c2(3.0, 4.0),<br />

c3;<br />

}<br />

c3 = c1 + c2;<br />

cout

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!