Beispiel - Prof. Dr. Rudolf Berrendorf
Beispiel - Prof. Dr. Rudolf Berrendorf
Beispiel - Prof. Dr. Rudolf Berrendorf
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