Sie sind auf Seite 1von 279

Programmieren in C

Prof. Dr. Rudolf Berrendorf


rudolf.berrendorf@fh-bonn-rhein-sieg.de

Fachbereich Angewandte Informatik


Fachhochschule Bonn-Rhein-Sieg
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 2
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Sie denken Sie kennen schon C?

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

#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_
_-_-_-_
}

Programmieren in C 3
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Literatur

• R.Berrendorf:
Einführung in die Programmiersprache C
Benutzerhandbuch KFA-ZAM-BHB-0095, Forschungszentrum Jülich
http://www.fz-juelich.de/zam/docs/
http://www.inf.fh-bonn-rhein-sieg.de/ lokale Kopie

• S.P.Harbison, G.L.Steele Jr.:


C: A Reference Manual
5. Ausgabe, Prentice Hall, 2002

• B.W.Kernighan, D.M.Ritchie:
Programmieren in C
2. Ausgabe, Hanser, 1990

• Bjarne Stroustrup:
Die C++ Programmiersprache
4. Auflage, Addison Wesley, 2000
Programmieren in C 4
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Historie von C

• 1960: ALGOL60
• CPL (Combined Programming Language)
• 1967: BCPL (Based Combined Programming Language)
Martin Richards (MIT), Systemprogrammierung
• 1970: B
Ken Thompson (AT&T Bell Labs), Implementierungssprache für Unix
• 1972: C
Dennis M. Ritchie (AT&T Bell Labs), Implementierungssprache für Unix
• 1978: C-Sprachdefinition im Buch The C Programming Language von
Brian W. Kernighan und Dennis M. Ritchie (K&R-C)
• 1983: Bildung des ANSI-Komitees X3J11 zur Standardisierung von C
• 1989: ANSI C (C89)
• 1999: kleinere Erweiterungen zum internationalen Standard ISO/IEC
9899:1999 (C99)
(Viele Compiler kennen noch nicht die in C99 hinzugekommenen
Erweiterungen)

Programmieren in C 5
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Eigenschaften von C

• Universelle Programmiersprache: einsetzbar für Anwendungen in (fast)


allen Bereichen
• Hardware-nahe Programmierung möglich
• Modulares Programmieren möglich
• Möglichkeiten zur Formulierung effizienter Programme
• C-Compiler erzeugen effizienten Code
• Viele Datentypen, viele Kontrollstrukturen, viele Operatoren
• Hohe Portabilität möglich
(notwendige Voraussetzung: Beschränkung auf Standard-C)
• Standardbibliothek moderat umfangreich (verglichen mit Java), jedoch
keine Änderungen am existierenden Interface über viele Jahre (voll
aufwärtskompatibel)

• Sprache hat keine Konstrukte zum objektorientierten Programmieren


• Grafikbibliothek nicht Teil von C
• Fehlerquellen (== und =, Typumwandlung usw.)
Programmieren in C 6
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Advanced Hello World
Datei hello.c:

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


#include <stdio.h>

/* Füge Definitionen der Standard-Bibliothek ein */


#include <stdlib.h>

/* hier folgt die Standard-Kopfzeile des Hauptprogramms */


int main (int argc, char **argv)
{
/* Deklariere eine Integer-Variable mit Namen i */
int i;

/* Weise i einen Wert zu */


i = 4711;

/* Gebe den Wert von i auf dem Terminal aus */


printf ("i = %d\n", i);

/* Beende das Programm */


return EXIT_SUCCESS;
}

Programmieren in C 7
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Dateien

• .c: C Source-Code; Teil eines C-Programms (C-Source)


• .h: Extern sichtbare Schnittstelle einer oder mehrerer .c-Dateien (C-Source)
• .o: Übersetzte Programmdatei (nicht-ausführbarer Objektcode)
• a.out: Ausführbare Programmdatei (ausführbarer Objektcode;
Name der Datei beliebig)
• .a: Programmbibliothek (nicht-ausführbarer Objektcode)

nicht-ausführbarer ausführbarer
C Source
Objektcode Objektcode

Übersetzen
x.c x.o

x.h Linken a.out


Übersetzen y.o

y.c
libgr.a
Programmieren in C 8
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Übersetzen auf Unix-Systemen

• Programm in einer Datei abspeichern.


Konvention:
Programmdateien: name.c
Dateien mit ext. Schnittstellendef.: name.h
• Übersetzen:
cc –c name.c
Erzeugt, falls fehlerfrei übersetzt werden kann, Datei name.o
• Linken:
cc –o name name.o
Erzeugt, falls keine Fehler auftreten, die ausführbare Datei name
• Ausführen:
./name
• Übersetzen und Linken in einem Schritt:
cc name.c
Erzeugt a.out als ausführbare Datei
• Linken mehrerer Objektdateien
cc –o name name_1.o name_2.o ... name_n.o
Programmieren in C 9
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Compiler-Optionen

• -c
nur Übersetzen, nicht Linken
• -O
Optimierung einschalten (evtl. –O0, –O1, -O2 –O3 usw. möglich)
• -g
Zusätzliche Informationen für Debugger in Objektdatei erzeugen.
Optimierung und Debugger-Informationen schließen sich oft aus.
• -ISuchpfad
Suchpfad für Include-Dateien angeben (mehrere Suchpfade durch :
getrennt). Mehrere –I Optionen hintereinander sind möglich.
• Informationen zum C-Compiler wie gewohnt über "man cc"
Die Auswertung erfolgt üblicherweise von links nach rechts!

Beispiel:
cc –c –O2 –I./include:/usr/local/produkt/include prog.c

Programmieren in C 10
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Linker-Optionen

• -o name
Ausführbare Datei hat Namen name (Default: a.out)
• -LSuchpfad
Suchpfad für Libraries angeben (Mehrere Suchpfade durch : getrennt).
Mehrere –L Optionen hintereinander sind möglich.
• -lname
Library libname.a (bzw. dyn. Library libname.so) dazulinken.
Mehrere Libraries können durch Komma getrennt werden. Die Libraries
werden nach dem Suchpfad gesucht. In den angegebenen Libraries
werden alle Symbole gesucht, die zu diesem Zeitpunkt noch offen sind
(d.h. zu der es eine Referenz aber keine Definition gibt).

Die Auswertung erfolgt üblicherweise von links nach rechts!

Beispiel:
cc –o prog –L./lib prog.o –ltollelib,lib2

Programmieren in C 11
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zusammenfassung Syntax

Syntax Beispiel

cc cc

-o executable_name -o my_executable

Compiler-Flags -O -I/usr/local/include

Dateinamen (.c oder .o) datei1.c datei2.o

Linker-Flags -I/usr/local/lib -lmeinelib

obligatorische Angaben
optionale Angaben
Programmieren in C 12
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

cc -c x.c y.c
Übersetzen
x.c x.o

Übersetzen
y.c y.o

cc -o ausfuehr x.c y.c


Übersetzen
x.c x.o Linken
ausfuehr
Übersetzen
y.c y.o

cc x.o y.c
x.o Linken
a.out
Übersetzen
y.c y.o

Programmieren in C 13
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 14
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zeichensatz
• Source-Zeichensatz (aus dem Programme bestehen)
– Alphanumerischen Zeichen
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9
– Leerzeichen und Zeilenendezeichen
– Sonderzeichen
( ) [ ] { } < > + - * / % ^ ~ & | _ = ! ? # \ , . ; : ` "
– Steuerzeichen:
horizontaler Tabulator (TAB), vertikaler Tabulator, Seitenvorschub
– Oft zusätzliche Zeichen möglich (nicht in Standard spezifiziert):
• Beispiel: @, $, Backspace
• (Eigentlich) nur in Zeichenkonstanten, Strings, Kommentaren, Dateinamen
• Ausführbarer Zeichensatz
– Evtl. andere Codierung als Source-Zeichensatz (Beispiel:Zeilenende)
– Interessant (für den Compiler) bei Cross-Compilierung
• Trigraphs (aus prähistorischer Zeit; vermeiden!)
– Alternativschreibweise für einige Symbole, die früher nicht auf allen Tastaturen
vorhanden waren
– Beispiel: ??= für #, ??( für [ usw.
Programmieren in C 15
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Aufbau von C-Dateien

• Formatfrei
• Leerzeichen, Zeilenendezeichen, vertikaler und horizontaler Tabulator,
Seitenvorschub und Kommentar (s.u.) sind Trennzeichen (außer in
Zeichenkonstanten und Strings)
• Mehrere Trennzeichen werden logisch als ein Trennzeichen
angesehen
• Backslash \ direkt gefolgt von Zeilenende wird ignoriert, so dass eine
logische Zeile entsteht. Dies ist z.B. bei Präprozessor-Direktiven
notwendig, die sich nur über eine logische Zeile erstrecken können
• Groß- und Kleinschreibung ist signifikant

Beispiele:

Bezeichner1 Bezeichner2 Bezeichner3

Diese beiden (es folgt eine Backslash-Newline-Kombination) \


Zeilen werden als eine logische Zeile aufgefasst.

Programmieren in C 16
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Kommentare
• Dienen der besseren Lesbarkeit von Programmen
• Können überall im Programm erscheinen, außer in Zeichenkonstanten
und Strings
• Sind Trennzeichen
• Können nicht geschachtelt werden!
• Können über mehrere Zeilen gehen
• Beginnen mit /* und enden mit */ über mehrere Zeilen möglich
• // bis Zeilenende

Beispiel:
// Dies ist ein Kommentar

/* Dies ist /* ebenfalls ein Kommentar */

/* Dies ist ein Kommentar,


der über mehrere Zeilen geht.
*/

/* Dies ist /* noch


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

Programmieren in C 17
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Grundelemente der Sprache

• Bezeichner, z.B. meine_Variable


• Schlüsselwörter, z.B. const, int
• Operatoren, z.B. +, -
• Separatoren oder Punktsymbole, z.B. [ ]
• Konstanten, z.B. 5, 3.14
• Strings, z.B. "string"

Programmieren in C 18
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Bezeichner

• Bezeichner werden für Variablen, Funktionen, Makros usw. verwendet


• Bezeichner sind eine Folge von Buchstaben, Ziffern und dem Zeichnen
_, wobei das erste Zeichen keine Ziffer sein darf
• Längste mögliche Erweiterung bildet Bezeichner
• Klein-/Großschreibung wird unterschieden
• Keine reservierten Schlüsselwörter erlaubt
• Bezeichner können beliebig lang sein, wobei jedoch nur eine bestimmte
Länge signifikant sein muss (C89 hat stärkere Restriktionen):
– Mindestens die ersten 63 Zeichen bei internen Namen
– Mindestens 31 Zeichen bei externen Namen

Beispiel:
I i
A5 _Bezeichner_1_
Dies_Ist_ein_sehr_langer_Bezeichner_der_aber_gueltig_ist

Programmieren in C 19
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Bezeichner

Bezeichner
Buchstabe

Buchstabe
_
Ziffer

Programmieren in C 20
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Vereinbarung

• Variablen- und Funktionsnamen werden (überwiegend) klein


geschrieben
• Makronamen werden komplett aus Großbuchstaben gebildet
• Namen mit _ als erstem Buchstaben sind für Namen von (internen)
Bibliotheksfunktionen oder vordefinierten Makros „reserviert“

Beispiel:

/* Makros */
#define PI 3.1415
#define DIMENSION_1 100

/* Variablen */
int i,j;

/* Funktionen */
int MeineTolleFunktion();

/* "reservierte" Namen */
__LINE__, __DATE__, _exit();

Programmieren in C 21
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Reservierte Schlüsselwörter
• C kennt die folgenden reservierten Schlüsselwörter mit vordefinierter
Bedeutung, die z.B. nicht als Variablennamen oder Funktionsnamen
verwendet werden können (wohl aber als Makronamen).
• auto do inline switch
_Bool double int typedef
break else long union
case enum register unsigned
char extern return void
_Complex float short volatile
const for signed while
continue goto sizeof
default if static
restrict _Imaginary struct
• Blau gekennzeichnete Namen sind neu in C99

• Vordefinierter Bezeichner (ist aber kein Schlüsselwort)


– Für jede Funktion findet implizit eine Vereinbarung statt:
static const char __func__ [] = "Name der Funktion";
– Nutzung:
if(failed) printf("Function %s failed\n", __func__);

Programmieren in C 22
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Operatoren und Separatoren

• Operatoren (z.B. +) werden in Ausdrücken benutzt


• Separatoren dienen der Trennung von Symbolen
• Bestehen Operatoren oder Separatoren aus mehreren Zeichen (z.B. <<),
müssen diese (ohne Trennzeichen) zusammengeschrieben werden
• Operatoren und Separatoren sind:
– Einfache Operatoren: ! % ^ & * - + =
~ | . < > / ?
– Zusammengesetzte Zuweisungsoperatoren: += -= *= /= %=
<<= >>= &= ^= |=
– Andere zusammengesetzte Operatoren: -> ++ -- << >>
<= >= == != && ||
– Separatoren: ( ) [ ] { }
, ; : ...

Programmieren in C 23
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Konstanten

• Zu den verschiedenen Basistypen in C gibt es Konstanten:


– Integer-Konstanten
– Fließkommakonstanten
– Zeichenkonstanten
– String-Konstanten (String ist kein Basistyp)
– Aufzählungskonstanten (werden wie Konstanten behandelt)

• Jede Konstante hat einen Typ und einen Wert.

Programmieren in C 24
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Ganzzahlige Konstanten (Integer)

• Dezimale Angabe:
Dezimalzahl ohne Vorzeichen und führende 0, gebildet aus den
Dezimalziffern 0-9.

Beispiel:
5 19 32 /* Werte: 5, 19, 32 */

• Oktale Angabe:
Eine Oktalzahl (zur Basis 8) mit führender 0, gebildet aus den
Oktalziffern 0-7.

Beispiel:
05 023 040 /* Werte: 5, 19, 32 */

• Hexadezimale Angabe:
Eine Hexadezimalzahl (zur Basis 16) mit führendem 0x oder 0X,
gebildet aus den Hexadezimalziffern 0-9 und a-f oder A-F.

Beispiel:
0x5 0x13 0x20 /* Werte: 5, 19, 32 */

Programmieren in C 25
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typzusätze bei Integer-Konstanten

Bei Integer-Typen wird noch zwischen verschiedenen „Untertypen“


unterschieden (später dazu mehr im Detail). Um solche Konstanten
angeben zu können, gibt es Zusätze bei Angabe einer Integer-Konstanten:

u oder U für Integer-Konstanten ohne Vorzeichen (unsigned)


l oder L für lange Integer-Konstanten (long)
ll oder LL für sehr lange Integer-Konstanten (long long)

Beispiel:
10L /* Wert: long int 10 */
10LL /* Wert: long long int 10 */
10u /* Wert: unsigned int 10 */
10ul /* Wert: unsigned long int 10 */
10Ul /* Wert: unsigned long int 10 */
10ull /* Wert: unsigned long long int 10 */

Programmieren in C 26
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Integer-Konstanten

Integer-Konstante
Dezimalkonstante

Oktalkonstante Int-Suffix

Hexadezimalkonstante

u-Suffix
Int-Suffix u
ll-Suffix
u-Suffix U

l-Suffix l-Suffix
l-Suffix l

u-Suffix L

ll-Suffix ll-Suffix
ll
u-Suffix
Programmieren in C LL 27
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Integer-Konstanten

Dezimalkonstante
Ziffer ≠ 0

Ziffer

Oktalkonstante
0

oktale Ziffer

Hexadezimalkonstante
0 x hexadezimale Ziffer

oktale Ziffer
0 ... 7

hexadezimale Ziffer
0 ... 7 a ... f A ... F

Programmieren in C 28
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typzusätze bei Integer-Konstanten

Der Typ einer Integer-Konstanten ist der erste „passende“ Typ nach
folgendem Schema (C99; in anderen C-Versionen anders!):

Konstante Typ
Dezimalzahl ohne Anhang int, long, long long

Oktal-/Hexadezimalzahl ohne int, unsigned, long, unsigned long,


Anhang long long, unsigned long long

Anhang u oder U unsigned int, unsigned long,


unsigned long long
Dezimalzahl mit Anhang l oder L long, long long

Oktal-/Hexadezimalzahl mit l oder L long, unsigned long, long long,


unsigned long long
Anhang sowohl u/U als auch l/L unsigned long, unsigned long long

Dezimalzahl mit ll/LL long long

Oktal-/Hexadezimalzahl mit ll/LL long long, unsigned long long

Anhang sowohl u/U als auch ll/LL unsigned long long

Programmieren in C 29
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

Beispiel:
Rechner, auf dem int=16 Bit, long=32 Bit (bei signed 1 Bit Vorzeichen).

32000 int (-215 ≤ x ≤ 215-1)


32000u unsigned int (0 ≤ x < 216, ohne Vorzeichen-Bit)
33000 long (Umwandlung nach long, da als int nicht darstellbar)

Programmieren in C 30
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Fließkommakonstanten

Es gibt drei reelle Fließkommatypen (float, double, long double).


Fließkommakonstanten ohne Zusatz sind vom Typ double! Durch
Anhängen des Zusatzes f oder F entsteht eine float-Konstante, durch
Anhängen des Zusatzes l oder L eine Konstante vom Typ long double.

Durch Einfügen von complex.h sind auch komplexe Konstanten


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

Beispiel:

3.14 /* double */
3.1415926356f /* float */
3.14L /* long double */

Programmieren in C 31
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Fließkommakonstanten

• Dezimalzahl mit Punkt

Beispiel:
3.14 .5 3.

• Ganzzahl mit Exponent e oder E:

Beispiel:
5E10 5e10 3e-1

• Kombination aus beiden Alternativen

Beispiel:
0.31415e1 1.0e-10

Programmieren in C 32
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Fließkommakonstanten

Fließkommakonstante
. Ziffer Exponent f-Suffix

Ziffer

Ziffer .

Ziffer Exponent

Exponent f-Suffix
e Ziffer f
E +
F
-
l

Hexadezimale Angaben ebenfalls möglich (hier nicht) L


Programmieren in C 33
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Character-Konstanten

• Eine Character-Konstante ist ein Zeichen des ausführbaren


Zeichensatzes, das in Apostophe (keine Anführungszeichen!)
eingeschlossen ist.
• Ausnahmen sind (können durch Escape-Sequenzen dargestellt
werden; s.u.):
– Apostroph selber ′
– Backslash \
– Zeilenendezeichen
• Der Wert einer Character-Konstanten ist die Codierung im
verwendeten Zeichensatz. Beispiel: Das Leerzeichen hat im ASCII-
Zeichensatz die Codierung 32, im EBCDIC-Zeichensatz die Codierung
64.
• Der Typ einer Character-Konstanten (ein Ausdruck) ist int!

Beispiel:

′a′ ′$′ ′.′

Programmieren in C 34
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Escape-Sequenzen für Zeichen
• ′\a′ Gong-Zeichen
• ′\b′ Backspace
• ′\f′ Seitenvorschub (Formfeed)
• ′\n′ Newline
• ′\r′ Zeilrücklauf (Carriage Return)
• ′\t′ horizontaler Tabulator (Tab)
• ′\v′ vertikaler Tabulator
• ′\\′ Backslash \ selber
• ′\′′ Apostroph-Zeichen
• ′\“′ Anführungszeichen
• ′\?′ Fragezeichen
• ′\ooo′ oktale Angabe des Zeichencodes
(ooo sind 1-3 oktale Ziffern). Beispiel: ′\0′ für Codierung 0
• ′\xh′ Hexadezimale Angabe des Zeichencodes
h ist eine beliebige Anzahl von Hexadezimalziffern, die
durch eine Nicht-Hexadezimalziffer beendet wird.
Beispiel: ′x20′ steht im ASCII-Zeichensatz für das Leerzeichen.

Programmieren in C 35
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Unterstützung großer Zeichensätze

• Zur Zeit geringe Verbreitung


• Hier nur am Rande erwähnt

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


– Beispiel für Multibyte-Character: ′abc′
– Beispiel für Wide Character: L′a′

• Universal Character Names (C99) in Character- oder String-Konstanten


sowie Bezeichnern:
– \u hex-digit4
– \U hex-digit8
– Beispiel: \u0024 ($)

Programmieren in C 36
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
String-Konstanten

• Ein String (Zeichenfolge) ist kein Basistyp in C.


• In C wird ein String-Konstante angegeben als eine Folge von Zeichen in
Anführungszeichen.
• Escape-Sequenzen für Zeichen im String sind erlaubt.
• Der Typ einer String-Konstanten ist ein Feld von Zeichen.
• Wird eine String-Konstante über mehrere Zeilen definiert, so muss direkt
vor einem Zeilenende ein Backslash \ stehen (siehe Präprozessor).
• Stehen zwei Strings unmittelbar hintereinander (nur durch Leerzeichen,
Zeilenende etc. getrennt), so fügt der Compiler diese beiden String-
Konstanten zu einer String-Konstanten zusammen.

Beispiele:
″String″
″Auch \a Escape-Sequenzen sind erlaubt!″
″Dieser String geht über \
zwei Zeilen″
″Hieraus erzeugt″ ″der Compiler″
″einen String″

Programmieren in C 37
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
String-Konstanten

• Für eine String-Konstante mit n Zeichen legt der Compiler automatisch


einen Speicherbereich (char-Feld) der Länge n+1 an.
• Die ersten n Zeichen enthalten die Zeichen des angegebenen Strings.
• Das n+1-te Zeichen ist ein Null-Character (′\0′), das
vereinbarungsgemäß jeden String in C abschließen muss!
• So angelegte String-Konstanten dürfen nicht modifiziert werden!

Beispiele:
″abc″ wird abgespeichert als (jedes Kästchen ist ein Byte):

a b c \0

"" wird abgespeichert als:

\0

Programmieren in C 38
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Aufzählungskonstanten

• In C gibt es Aufzählungstypen, Mengen von benannten, ganzzahligen


Konstanten.

Beispiel:
enum Tage {Montag, Dienstag, Mittwoch, Donnerstag, Freitag,
Samstag, Sonntag};

• Der Typ einer Aufzählungskonstanten ist int!


• Der Wert einer Aufzählungskonstanten ist die Position in der
Aufzählungsmenge (bei 0 beginnend). Im Beispiel 0 für Montag, 1 für
Dienstag usw.
• Details später bei der Beschreibung von Aufzählungstypen.

Programmieren in C 39
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 40
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Präprozessor

• Übersetzt man ein Programm mit einem C-Compiler, so wird


automatisch ein Präprozessor vorgeschaltet.
• Präprozessor ist ein reiner Makroprozessor, der textuellen Ersatz
vornimmt und eine neue Datei produziert, die an den eigentlichen C-
Compiler weitergegeben wird!
• Aufgaben des Präprozessors:
– Einfügen weiterer Dateien innerhalb einer Datei (#include)
– Bedingte Übersetzung (#if ...)
– Makrodefinitionen und –ersetzung
– Zusätzliche Compiler-Steuerung über #pragma
• Gesteuert wird der Präprozessor über Direktiven, die alle mit einem #
als erstem Nichtleerzeichen einer Zeile anfangen.
• Direktiven müssen in eine logische Zeile passen. Längere Direktiven
mit Backslash-Newline-Kombination über mehrere physikalische Zeilen
möglich.
• Zwischen # und Direktivenbezeichner können Leerzeichen stehen.

Programmieren in C 41
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Ausführungsstufen des Präprozessors

• Der Präprozessor durchläuft mehrere logische Stufen hintereinander.


• In der Praxis führen Präprozessoren dies alles in einem Durchlauf aus.
• Die logischen Stufen sind:
– Ersetzen von Trigraph-Sequenzen
– Löschen von Backslash-Newline-Kombinationen
– Aufspalten des Programms in Grundelemente (Bezeichner, Zahlen, etc.),
die durch Trennzeichen separiert sind. Kommentare werden logisch durch
ein Leerzeichen ersetzt.
– Ausführen von Präprozessor-Direktiven inkl. Makroersetzung
– Ersetzen von Escape-Sequenzen in Character- und String-Konstanten.

Programmieren in C 42
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Einfügen von Dateien

• Mittels der #include-Direktive kann man andere Dateien in den Code


einfügen.
• Schachtelung möglich, d.h. in eingefügten Dateien können wiederum
#include-Direktiven stehen.
• Wird fast ausschließlich zum Einfügen von .h-Dateien genutzt.

• Mehrere Formen möglich:


– #include ″Dateiangabe″
Die Datei wird relativ zum aktuellen Verzeichnis gesucht.
– #include <Dateiname>
Die Datei wird an systemspezifischen Verzeichnissen gesucht. Auf Unix-
Systemen in /usr/include und evtl. /usr/local/include.
– #include Makroname1...Makronamen
Nach ihrer Ersetzung sollten die Makronamen eine der beiden obigen
Formen ergeben.

• Die Dateiangabe ist relativ zum jeweiligen Bezugspunkt, d.h. Angaben


wie ../Datei.h sind möglich.
• Der Bezugspunkt wird mit Pfadangaben in den Dateiangaben
verändert.
Programmieren in C 43
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Einfügen von Dateien

Beispiel 1:

#include ″test.h″ /* im lokalen Verzeichnis */


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

Beispiel 2:

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

Beispiel 3:

Datei test.c:
#include ″../test.h″ /* Im Vaterverzeichnis suchen */

Datei ../test.h:
#include ″test1.h″ /* Im Vaterverzeichnis weitersuchen nach test1.h */

Programmieren in C 44
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel für den Einsatz

Datei prog.c:
#include <stdlib.h>
#include ″mydefs.h″

int main(int argc, char **argv)


{
double umfang, radius, flaeche;

radius = 20.0;
umfang = 2 * PI * radius;
flaeche = flaechenberechnung(radius);
}

Datei mydefs.h:
/* Makrodefinition */
#define PI 3.1415

/* Funktionsdefinition (Typangabe) */
double flaechenberechnung (double);

Programmieren in C 45
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Makro

• Makros dienen:
– der Abkürzung häufig benutzter Zeichenfolgen
– der Definition logischer Konstanten (Konsistenz im Programm)
– dem „Umdefinieren“ existierender Namen
– der bedingten Übersetzung von Programmteilen
• Makros gibt es mit und ohne Parameter
• Nach einer Makrodefinition wird jedes Erscheinen dieses
Makronamens im weiteren Text durch den Ersatztext textuell (!)
ersetzt.
• Im Ersatztext können selbst wieder Makros verwendet werden.
• Prinzipiell lassen sich z.B. auch reservierte Wörter umdefinieren ->
schlechter Programmierstil.
• Makronamen werden vereinbarungsgemäß aus Großbuchstaben
gebildet.
• Mit der Compiler-Option –E kann man sich (auf Standardausgabe) den
Programmtext nach Durchlauf des Präprozessors anschauen.

Programmieren in C 46
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Makrodefinition ohne Parameter

Definition eines parameterlosen Makros:

#define Makroname Ersatztext

Makroname muss ein gültiger Bezeichner sein. Ersatztext kann ein beliebiger
Text sein, insbesondere auch leer. Der Ersatztext fängt direkt hinter dem
Makronamen an und geht bis zum Ende der (logischen) Zeile.

Beispiel:

#define PI 3.1415
#define VEKTOR_DIM 100
#define MATRIX_GROESSE (VEKTOR_DIM * VEKTOR_DIM)

double matrix[MATRIX_GROESSE];

/* wird textuell ersetzt durch:


double matrix[(100 * 100)];
*/

Programmieren in C 47
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Makrodefinition mit Parameter

Definition eines Makros mit Parametern:

#define Makroname(p1,...,pn) Ersatztext

Die öffnende Klammer hinter dem Makronamen muss unmittelbar folgen,


sonst wäre dies ein Makrodefinition ohne Parameter. p1,...,pn sind formale
Parameter. Jedes weitere Vorkommen des Makronames im Text bewirkt
Folgendes:

1. Identifizierung der aktuellen Parameter des Makroaufrufes, die durch


Kommas getrennt sind
2. Ersetzen weiterer Makroaufrufe in diesen aktuellen Parametern
3. Ersetzen der formalen Parameter durch die aktuellen Parameter
4. Ersetzen des Makroaufrufes durch den Ersatztext mit den aktuellen
statt den formalen Parametern.

Programmieren in C 48
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele zu Makrodefinition mit Parameter

#define SQUARE(x) x*x


#define LOOP(start, test, inc, body) \
for (start ; inc; test) \
body;

int main(int argc, char **argv)


{
int i,j;
LOOP( i=0 , i < 100 , ++i , j = SQUARE(j) )
}

Nach Makroexpansion:

int main(int argc, char **argv)


{
int i,j;
for ( i = 0; i < 100; ++i)
j = j*j;
}

Programmieren in C 49
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Potentielle Fehler

Beispiel 1: Prioritäten von Operatoren Beispiel 2: Nebeneffekte

#define SQUARE(x) x * x #define SQUARE(x) ((x) * (x))


b = SQUARE(a+1); b = SQUARE(a++);

Erzeugter Code: Erzeugter Code:

b = a+1 * a+1 b = ((a++) * (a++));

Tips:

1. Großzügig im Ersatztext Klammern verwenden. Jedes Vorkommen


eines Argumentes im Ersatztext klammern.
2. Als aktuelle Argumente von Makroaufrufen nur Ausdrücke ohne
Nebeneffekte verwenden.

Programmieren in C 50
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Makrodefinition mit variabler Parameteranzahl

Definition eines Makros mit einer festen Anzahl (kann auch 0 sein) von
Parametern und möglichen weiteren Parametern:

#define Makroname(p1,p2,... ) Ersatztext

Mit ... werden syntaktisch alle nachfolgenden Parameter bezeichnet, die


bei einem Aufruf übergeben werden. Im Ersatztext kann man auf diese
Parameter mit __VA_ARGS__ zugreifen

Beispiel:

#if defined(DEBUG)
#define my_printf(...) fprintf(stderr, __VA_ARGS__)
#else
#define my_printf(...) printf(__VA_ARGS__)
#endif

...
my_printf("Hier kracht es, weil i=%d\n", i);
Programmieren in C 51
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
undef eines Makros

Oft ist es wünschenswert, dass ab einem bestimmten Punkt im


Programm ein Makro nicht länger definiert sein soll. Dazu gibt es die
Direktive

#undef Makroname

Ab diesem Punkt ist kein Makro unter Makroname mehr bekannt.

Beispiel:

#define TEST_CODE
...
#undef TEST_CODE

Programmieren in C 52
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Bedingte Übersetzung
Mit Hilfe des Präprozessors ist es möglich, nur bestimmte Teile des
Programms übersetzen zu lassen. Dies ist z.B. dann nützlich, wenn man
aus einem Source-Programm verschiedene Versionen erzeugen möchte,
z.B. für Unix und Windows.

Beispiel:

#if defined(UNIX)
# include <unix.h>
#elif defined(WINDOWS)
# include <windows.h>
#endif

Durch Definieren des Makros UNIX bzw. WINDOWS werden


unterschiedliche Teile des Programms vom Präprozessor an den
eigentlichen Compiler weitergereicht. Mit der Compiler-Option
–DMakroname
kann man gezielt Makros auch von der Kommandozeile definieren.

Programmieren in C 53
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Direktiven zur bedingten Übersetzung

• #if const_expr
Das nächste Programmstück bis zur nächsten bedingten Direktive auf
gleichem Niveau wird nur übersetzt, wenn const_expr (ein konstanter
Integer-Ausdruck) ungleich 0 ist.
• #ifdef Makroname
Übersetzen, wenn Makroname an dieser Stelle bekannt ist.
• #ifndef Makroname
Übersetzen, wenn Makroname an dieser Stelle nicht bekannt ist.
• #elif const_expr
Nachfolgendes bis zur nächsten bedingten Direktive übersetzen, falls
vorangehende bedingte Direktiven auf gleichem Niveau nicht genommen
wurde und const_expr ungleich 0 ist.
• #else
Bis zum entsprechenden #endif übersetzen, falls keine vorangehende
bedingte Direktive auf gleichem Niveau genommen wurde.
• #endif
Abschließende Direktive eines bedingten Blocks.

Programmieren in C 54
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

Beispiel 1: Beispiel 3:

#define UNIX #if !defined(TEST_H_INCLUDED)


# define TEST_H_INCLUDED
#ifdef UNIX # include <test.h>
# include <unix.h> ...
#else #endif
# include „meinedatei.h“
#endif

Beispiel 4:
Beispiel 2: #define DEBUG 2
#if (DEBUG > 0)
#if 0 printf(″Kontrollausgabe″);
Programmstück, das nicht # if (DEBUG > 1)
übersetzt werden soll printf(″Mehr Details″);
#endif # endif
#endif

Programmieren in C 55
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Sonstige Direktiven

• #pragma Compilerpragma
Compiler-abhängige Informationen können an den Compiler übergeben
werden
• #
Leere Direktive ohne Effekt.
• #line Zeilennummer [″Dateiname″]
Nützlich im Zusammenhang mit Programmierwerkzeugen, die
Programmtransformationen ausführen (Rückbezug zur Originaldatei).
• #error token
Compiler gibt eine Meldung aus, die aus token besteht.

Beispiel 1: Beispiel 2:

#pragma optimize #if defined(SYSTEM)


#pragma 27 ″orignaldatei.c″ ...
#else
#error ″das sollte nicht passieren″
#endif

Programmieren in C 56
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Makro-Operatoren
• defined(Makroname)
Liefert 1, falls Makroname an diesem Punkt bekannt ist, ansonsten 0.
• #
Der Präfixoperator kann nur im Zusammenhang mit #define
verwendet werden. Er bewirkt das Einschließen des nachfolgenden
Argumentes in Anführungszeichen.

Beispiel:
#define tempfile(dir) #dir
name = tempfile(/usr/include);
erzeugt
name = ″/usr/include″;

• ##
Der Infixoperator bewirkt eine Konkatenation beider Operanden.

Beispiel:
#define cat(x,y) x ## y
cat(var, 123)
erzeugt
var123

Programmieren in C 57
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Vordefinierte Makros

• __LINE__
Dezimalkonstante mit der aktuellen Zeilennummer während der
Übersetzung.
• __FILE__
String-Konstante mit dem aktuellen Dateinamen während der
Übersetzung.
• __DATE__
String-Konstante mit dem aktuellen Datum der Übersetzung.
• __TIME__
• String-Konstante mit dem aktuellen Zeitpunkt der Übersetzung.
• __STDC__
Liefert 1, wenn der Compiler konform zum ANSI-Standard ist.

Beispiel:

printf(″Programmversion 1.0, uebersetzt am %s um %s″, __DATE__, __TIME__);


if( i == j )
printf(″In Zeile %d in Datei %s stimmt was nicht″, __LINE__, __FILE__);

Programmieren in C 58
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 59
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Anweisungen

• Anweisungen steuern Kontrollfluss


• Jede Anweisung wird durch ein Semikolon abgeschlossen
(Ausnahme: zusammengesetzte Anweisung)

Programmieren in C 60
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Leere Anweisung

Syntax:
;

Beschreibung:
Wird üblicherweise dort eingesetzt, wo aus syntaktischen Gründen eine
Anweisung erforderlich ist.

Beispiel:

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


;

Programmieren in C 61
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Ausdrucksanweisung

Syntax:

Ausdruck ;

Beschreibung:
Durch Anhängen eines Semikolons an einen Ausdruck (z.B. 3+4) erhält
man eine Ausdrucksanweisung. Der Wert des Ausdrucks wird berechnet
und das Ergebnis ignoriert. Eine Ausdrucksanweisung macht nur Sinn,
wenn in dem Ausdruck Nebenwirkungen vorhanden sind.

Beispiel:

/* Ein Funktionsaufruf ist ist ein Ausdruck */


myfun(10);

Programmieren in C 62
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Sprungzielanweisung

Syntax:

Bezeichner : Anweisung

Beschreibung:
Jeder Anweisung kann ein Label vorangestellt werden, wobei ein Label
ein Bezeichner gefolgt von einem Doppelpunkt ist. Ein Label kann nur
über eine goto-Anweisung (später) angesprochen werden. Ausnahme:
Label im Zusammenhang mit switch-Anweisungen (später). Ein Label-
Bezeichner kann auch schon vor seiner Definition benutzt werden (d.h.
goto Bezeichner). Der Gültigkeitsbereich ist die umschließende
Funktion.
Beispiel:

/* Das Label heißt hier Fehler */


Fehler: i = 2;
...
goto Fehler;

Programmieren in C 63
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zusammengesetzte Anweisung
Syntax:
{ Deklaration }

Anweisung

Beschreibung:
Oft bezeichnet man dies auch als Block. In C89 muss die Reihenfolge
zwingend eingehalten werden: Zuerst alle Deklarationen, dann erst
Anweisungen.

Beispiel:

int main(int argc, char **argv)


{
int i;
i = 2;
{ /* <- hier beginnt der Block */
int i;
i = 5;
} /* <- hier endet der Block */
}
Programmieren in C 64
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
while-Schleife

Syntax:

while ( Ausdruck ) Anweisung

Beschreibung:
Die Anweisung im Schleifenrumpf wird solange ausgeführt, solange der
Ausdruck Ausdruck wahr ist (d.h. ungleich 0). Alternative
Abbruchmöglichkeiten werden später behandelt.

Beispiel:
int fun1(int arg)
{
int i;
i = 1;
while(arg != 0)
{
i = i * 2;
arg = arg – 1;
}
return i;
}

Programmieren in C 65
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
do-while-Schleife

Syntax:

do Anweisung while ( Ausdruck ) ;

Beschreibung:
Die Anweisung im Schleifenrumpf wird solange ausgeführt, bis der
Ausdruck Ausdruck falsch ist (d.h. gleich 0). Im Gegensatz zur while-
Schleife wird die Anweisung im Schleifenrumpf mindestens einmal
ausgeführt. Alternative Abbruchmöglichkeiten werden später behandelt.

Beispiel:
int fun2(int arg)
{
int i = 1;
do {
i = i * arg;
arg = arg – 1;
} while(arg != 0);
return i;
}

Programmieren in C 66
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
for-Schleife

Syntax:

for ( init ; expr2 ; expr3 ) Anweisung

Beschreibung:
Zuerst wird init ausgewertet und das Resultat ignoriert. Dann wird expr2
ausgewertet. Falls der Wert falsch (d.h. 0) ist, bricht die Schleife ab. Ansonsten
wird die Anweisung ausgeführt und vor dem nächsten Test von expr2 wird
expr3 ausgewertet und das Resultat ignoriert. Alle Angaben init, expr2, expr3
sind optional und können weggelassen werden. init kann ein Ausdruck oder
eine Deklaration sein.

Beispiel:
int fun3(int arg)
{
for ( int i = 1; arg != 0; arg = arg – 1 )
i = i * arg;
return i;
}

Programmieren in C 67
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
goto-Anweisung

Syntax:
goto Bezeichner ;

Beschreibung:
Es wird zu dem angegeben Bezeichner eines Labels verzweigt. Springt
man in einen Block hinein, so wird zwar Speicherplatz für die Block-
lokalen Variablen angelegt, jedoch werden keine
Initialisierungsausdrücke für diese Variablen ausgewertet!

Beispiel:

int fun4(int a[100], int suchwert)


{
int i;
for ( i = 1; i < 100; i = i + 1 )
if ( a[i] == suchwert)
goto gefunden;

gefunden: return i;
}

Programmieren in C 68
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
break-Anweisung

Syntax:

break ;

Beschreibung:
Mit der break-Anweisung verlässt man die innerste Iterations- (for, while,
do-while) oder switch-Anweisung (später).

Beispiel:

int fun5(int a[100], int suchwert)


{
int i;
for ( i = 1; i < 100; i = i + 1 )
if ( a[i] == suchwert)
break;

return i;
}

Programmieren in C 69
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
continue-Anweisung

Syntax:

continue ;

Beschreibung:
Mit der continue-Anweisung bricht man innerhalb einer Iterations-
Anweisung (for, while, do-while) die aktuelle Iteration ab und verzweigt
zum Ende des Schleifenrumpfes, bricht aber nicht die Schleife ab. D.h.
es würde in einer for-Schleife als nächstes wieder überprüft, ob die
Abbruchbedingung erfüllt ist.
Beispiel:
double logsum( double a[100], int max ) {
int i;
double sum = 0.0;
for ( i = 0; i < max; i = i + 1 ) {
if ( a[i] == 0.0 )
continue;
sum += log(a[i]);
}
return sum;
}
Programmieren in C 70
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
return-Anweisung

Syntax:

return Ausdruck ;

Beschreibung:
Die return-Anweisung bewirkt ein sofortiges Verlassen der Funktion und
Rückkehr zur aufrufenden Funktion. Der Ausdruck Ausdruck ist optional.
Falls man den Ausdruck weglässt, muss die Funktion vom Resultattyp
void sein.

Beispiel:
int const5( void )
{
return 5;
}

Programmieren in C 71
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
if-Anweisung

Syntax:

if ( Ausdruck ) Anweisung1 else Anweisung2 ;

Beschreibung:
Zuerst wird Ausdruck ausgewertet. Ist der Ausdruck wahr (d.h. ungleich 0), wird
Anweisung1, ansonsten Anweisung2 ausgeführt. Die Angabe des else-Zweiges
ist optional.

Beispiel:
int fun6( int arg )
{
if ( arg < 0)
return –1;
else
return 1;
}

Programmieren in C 72
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
if-Anweisung

Durch eine Schachtelung von if-Anweisungen kann es zu


Mehrdeutigkeiten kommen. Im Fall

if ( arg1 < 0)
if ( arg2 == 0 )
return 0
else
return 1;

Worauf bezieht sich das letzte else? In C (und anderen Sprachen) ist es
so definiert, dass sich ein else immer auf das letzte offene if bezieht.

Man sollte in solchen Fällen der besseren Lesbarkeit einen Block mit {}
einführen:

if ( arg1 < 0)
{
if ( arg2 == 0 )
return 0
else
return 1;
}
Programmieren in C 73
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
switch-Anweisung

Syntax:

switch ( Ausdruck ) Anweisung

und in diesem Zusammenhang:

case const_expr :

default :

Programmieren in C 74
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
switch-Anweisung

Die allgemeine Anwendungsform einer switch-Anweisung ist:

switch (Ausdruck )
{
case Ausdruck_1: Anweisung_1;
break;
....
case Ausdruck_n: Anweisung_n;
break;
default: Anweisung_n+1;
}

Der Ausdruck Ausdruck wird ausgewertet. Dann werden nacheinander


die konstanten Ausdrücke Ausdruck_1,... ausgewertet, bis Ausdruck
gleich einem Ausdruck_i ist. In diesem Fall wird Anweisung_n
ausgeführt und mit der anschließenden break-Anweisung die switch-
Anweisung verlassen.
Trifft kein Fall zu und ist ein default-Label vorhanden, so wird diese
Anweisung ausgeführt. Ist nach einer Anweisung_i keine break-
Anweisung vorhanden, wird mit Anweisung_i+1 weitergemacht!

Programmieren in C 75
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

void fehlermeldung ( int fehlercode )


{
switch ( fehlercode )
{
case 1: printf("Fehler kritisch");
break;
case 2: printf("Fehler fatal");
break;
default: printf("Fehler ignorieren");
}

switch ( fehlercode )
{
case 1:
case 2: exit(EXIT_FAILURE);
}
}

Programmieren in C 76
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 77
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Funktionen

• (später im Detail mehr)


• Funktionsdefinitionen bestehen aus
– Funktionskopf (header), der die Schnittstelle angibt
– Rumpf (body), der die Berechnungsvorschrift angibt
• Ein Funktionsprototyp spezifiziert lediglich die Schnittstelle und enthält
statt einem Rumpf ein Semikolon. Funktionsprototypen ermöglichen
u.a. dem Compiler Typüberprüfungen und automatische
Typumwandlungen bei Funktionsaufrufen.

// header // Funktionsprototyp
int myfun ( int arg1 ) int myfun ( int arg1 ) ;
{
// body
return arg1 + 1;
}

Programmieren in C 78
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Funktionen

• In C ausschließlich call-by-value Parameterübergabe


• Ändern von Variablen in aufrufender Funktion kann durch Übergabe
von Zeigerwerten (Adressen der Variablen) erreicht werden

void swap (int *x, int *y)


{
// tausche den Inhalt an den beiden Adressen,
// die durch x und y angegeben sind
int tmp = *x;
*x = *y;
*y = tmp;
}

int swaptest()
{
int i=1, j=2;
// rufe swap mit den Adressen der beiden Variablen i und j auf
swap(&i, &j);
printf("i=%d, j=%d\n", i,j); // Ausgabe: i=2, j=1
}

Programmieren in C 79
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Hauptprogramm

• Für jedes ausführbare Programm muss eine Funktion mit Namen main
existieren, das der Loader als Programmstart aufruft
• Funktionsprototyp:

int main ( int argc, char ** argv );

• Der Funktionswert von main ist ein int. Dieser Wert wird an das
Betriebssystem zurückgegeben und man kann z.B. diesen Wert in
einer Shell abfragen. Es gibt zwei logische Konstanten (in <stdlib.h>),
EXIT_SUCCESS und EXIT_FAILURE, die man in Zusammenhang mit
einer return-Anweisung im Hauptprogramm nutzen kann, um einen
fehlerfreien bzw. mit Fehlern behafteten Programmablauf zu
kennzeichnen.
• Über den Wert argc kann man abfragen, wie viele Argumente (z.B. in
der Kommandozeile) an das Programm übergeben wurden. In argv
stehen als Strings die Argumente.

Programmieren in C 80
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

Programm:
#include <stdio.h>
int main ( int argc, char **argv )
{
int i;

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


printf (″Argument %d war %s\n″, i, argv[i] );
}

Aufruf aus der Kommandozeile:


Prompt> a.out test1 4711 test4
Argument 0 war a.out
Argument 1 war test1
Argument 2 war 4711
Argument 3 war test4
Prompt>

Programmieren in C 81
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 82
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typen
• Typen sind Wertemengen mit den darauf definierten Operationen.
• Jedes Objekt in C (Konstante, Variable, Funktion) hat einen Typ.
• Durch Typüberprüfung kann der Compiler viele Fehler erkennen.
• Typumwandlung zwischen manchen Typen möglich
• Basistypen:
– Integer-Typen
– Character-Typen
– _Bool
– Aufzählungstypen
– Fließkommatypen
• Leerer Typ
• Zusammengesetzte Typen
– Zeigertypen
– Feldtypen
– Strukturtypen
– Vereinigungstypen
– Funktionstypen
• Typzusätze

Programmieren in C 83
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Einordnung von Typen

C Typ Kategorie
short, int, long, long long integrale arithmetische skalare
(signed und unsigned) Typen Typen Typen
char (signed und unsigned)
_Bool
enum {...}
float, double, long double Fließkomma-
float/double/long double _Complex typen
T * Zeigertypen
T [...] Feldtypen aggregierte
struct {...} Strukturtypen Typen
union {...} Vereinigungstypen
T (...) Funktionstypen
void Typ void

Programmieren in C 84
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Integer-Typen
• Integer-Typen dienen zur Repräsentation von:
– Ganzzahligen Werten mit und ohne Vorzeichen
– Bit-Vektoren
– Boolschen Werten (0=falsch, alles andere=wahr)
– Character-Werten (Unterschied Character-Wert und Character-Typ!)

• Integer-Typen mit Vorzeichen (Präfix signed optional):


short int, int, long int, long long int
alternative Schreibweise: short, long, long long

• Integer-Typen ohne Vorzeichen (unsigned):


unsigned short int, unsigned int, unsigned long int,
unsigned long long int
alternativ: unsigned short, unsigned long, unsigned long long

• Character-Typen:
char, signed char, unsigned char

• Aufzählungstypen
Programmieren in C 85
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Integer-Typen

• Größenverhältnisse bzgl. Speicherbelegung und damit Wertebereich:


– short int ≤ int ≤ long int
– unsigned short int ≤ unsigned int ≤ unsigned long int
• Es können auch alle drei Typen bzgl. Größe zusammenfallen
• int entspricht im Allgemeinen der Wortlänge des Rechners
• signed-Versionen sind gleich groß wie unsigned-Versionen
• unsigned-Versionen unterscheiden sich von signed-Versionen durch
die Interpretation des Speicherplatzes (Vorzeichenbit, größere positive
Zahlen darstellbar mit unsigned)
• Vermeiden Sie das Mischen/die Verwendung vieler verschiedener
Integer-Typen (wann immer es geht: int)

Programmieren in C 86
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Mindestanforderungen an Wertebereich
Die folgenden logischen Konstanten sind in <limits.h> definiert und geben
jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen
an die Wertebereiche der int-Typen sind dahinter angegeben:
Konstante Mindestanforderung Bedeutung
SHRT_MIN -32767 Min. Wert von short

SHRT_MAX 32767 Max. Wert von short

USHRT_MAX 65535 Max. Wert von unsigned short

INT_MIN -32767 Min. Wert von int


INT_MAX 32767 Max. Wert von int

UINT_MAX 65535 Max. Wert von unsigned int

LONG_MIN -231 Min. Wert von long

LONG_MAX 231-1 Max. Wert von long


ULONG_MAX 232-1 Max. Wert von unsigned long

LLONG_MAX 263-1 Max. Wert von long long (Rest analog)


Programmieren in C 87
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Extended Integer Typen

• Oft benötigt man zur korrekten Programmierung exakte


Größenangaben zu einem Typ, z.B. int64 (wie in Java direkt definiert)
• In C war das bisher nicht möglich!
• Ausweg: Extended Integer Typen definiert in
– inttypes.h
– stdint.h
• Dort sind zusätzliche (abgeleitete) int-Typen definiert.
• Syntax dieser Typen:
– intN_t und uintN_t, wobei N=8, 16, 32 oder 64
(implementierungsabhängig auch mehr) für genau N Bits
– int_leastN_t, uint_leastN_t analog für mindestens N Bits
– int_fastN_t, uint_fastN_t analog für mindestens N Bits und
möglichst schnell
• Beispiele:
– int8_t // int, genau 8 Bits
– uint16_t // unsigned int, genau 16 Bits
– int_fast32_t // int, mindestens 32 Bits, schnell
– uint_least64_t // unsigned int, mindestens 64 Bit

Programmieren in C 88
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele für Variablendeklarationen

int x;

unsigned int ux;


unsigned long int ulx;

signed int sx;

short shx;
signed short int shx2;

uint64_t counter;

Programmieren in C 89
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Character-Typ

• Character-Typen belegen genau ein Byte


• Können genau ein Zeichen des Zeichensatzes aufnehmen
• Es gibt char, signed char und unsigned char
• Verwendet man ein Character-Objekt (Konstante, Variable) in einem
Ausdruck, so ist der Wert des (Teil-)Ausdrucks ein int !

Fehlerquelle: Manche Bibliotheksfunktione zum Einlesen von Zeichen


liefern die logische Konstante EOF=-1 zurück. Weist man den
Resultatwert (int) einer char-Variablen zu und testet anschließend die
char-Variable, ob diese gleich EOF (int) ist, so kann es durch implizite
Typumwandlungen zu unerwünschten Ergebnissen kommen.

Beispiel:

char c;
signed char sc;
unsigned char uc;

Programmieren in C 90
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Mindestanforderungen an Wertebereich

Die folgenden logischen Konstanten sind in <limits.h> definiert und geben


jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen
an die Wertebereiche der char-Typen sind dahinter angegeben:

Konstante Mindestanforderungen an Wert Bedeutung


CHAR_BIT 8 Bits pro Byte
SCHAR_MIN -127 Min. Wert signed char
SCHAR_MAX 127 Max. Wert signed char
UCHAR_MAX 255 Max. Wert unsigned char
CHAR_MIN 0 oder SCHAR_MIN Min. Wert von char
CHAR_MAX UCHAR_MAX oder SCHAR_MAX Max. Wert von char
MBLEN_MAX 1 Max. Anzahl Byte in
Multibyte-Character

Programmieren in C 91
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Aufzählungstyp

Ein Aufzählungstyp ist eine Anzahl von int-Werten, die jeweils mit einem
Bezeichner benannt sind.
Die Syntax zur Definition eines Aufzählungstyps in C ist:

enum typ_name { const_def_list };

wobei typ_name ein optionaler Typname ist und const_def_list eine durch
Kommas getrennte Liste von Bezeichnern mit optionalen Wertangaben.
Mit jedem Bezeichner in const_def_list wird ein int-Wert assoziiert, ohne
weitere Angabe beginnend bei 0 und jeweils um 1 inkrementiert.

Beispiel:

enum Jahreszeiten { Fruehjahr, Sommer, Herbst, Winter, Karneval=5 };

enum Jahreszeiten stuermisch; /* Variable stuermisch deklarieren */


stuermisch = Herbst; /* Zuweisen eines Aufzählunswertes */

Programmieren in C 92
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Aufzählungstyp

• Explizites Setzen eines Aufzählungswertes durch Bezeichner=Wert


• Durch explizites Setzen eines Aufzählungswertes beginnt das
Weiterzählen bei diesem Wert.
• Es ist erlaubt, dass zwei Bezeichner den gleichen Wert haben.
• Bereits definierte Bezeichner kann man als Wertangabe verwenden.

Beispiel:

enum aufzaehl1 { eins=1, zwei = eins+1, fuenf=zwei*zwei+eins };


enum aufzaehl2 { wert1=4, wert2=4, wert3, wert4};

Programmieren in C 93
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Boolean Typ

• Neu in C99
• <stdbool.h>
• Dort ist ein Makro bool definiert, das den Wert _Bool hat
• _Bool mit Werten 0 und 1
• Wird implementiert durch einen unsigned int Typ

Beispiel:

#include <stdbool.h>

_Bool b1; // b1 ist vom Typ _Bool


bool b2; // ditto b2

bool less(int x, int y)


{
bool b = x < y; // Teste, ob x kleiner als y ist
return b;
}

Programmieren in C 94
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Reelle Fließkommatypen

• Drei reelle Fließkommatypen: float, double, long double


• Größenverhältnis: float ≤ double ≤ long double
• Alle Fließkommakonstanten ohne expliziten Typzusatz sind vom Typ
double!
• In C89 liefern die meisten mathematischen Bibliotheksfunktionen (sin
etc.) einen double-Wert, auch wenn das Argument ein float war.

Beispiel:

float f1, f2;


double d;
long double ld;

Programmieren in C 95
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Mindestanforderungen an Wertebereich

Die folgenden logischen Konstanten sind in <floats.h> definiert und geben


jeweils die gültigen Werte für den Compiler an. Die Mindestanforderungen
an die Wertebereiche der Fließkommatypen sind dahinter angegeben
(DBL_ und LDBL_ Konstanten existieren ebenfalls):

Konstante Mindestanforderung an Bedeutung


Wert
FLT_ROUNDS Rundungsart:
0: nach 0 (Abschneiden)
1: zum Nächsten
2: nach +∞
3: nach - ∞
FLT_EPSILON 1e-5 Minimales x, s.d. 1.0+x ≠1
FLT_DIGITS 6 Anz. Dezimalziffern Genauigkeit
FLT_MIN 1e-37 Min. positive Zahl
FLT_MAX 1e37 Max. darstellbare Zahl
Programmieren in C 96
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Komplexe Fließkommatypen

• Neu in C99
• Typ _Complex mit den Prefixen float, double, long double
• Komplexer Wert wird intern repräsentiert als Feld des Basistyps mit 2
Elementen (reeller und imaginärer Teil)
• #include <complex.h>
• In complex.h ist ein Makro complex definiert, hinter dem _Complex
steht (sollte man verwenden)

Beispiel:

float _Complex fc;


double complex dc;
long double complex ldc;

Programmieren in C 97
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Leerer Typ (void)

• Der leere Typ void hat keine Werte und auf ihm sind keine
Operationen definiert.
• Er dient als Statthalter für nicht-existente Objekte.
• Verwendung im Zusammenhang mit:
– Generischen Zeigern:
void *malloc(size_t size);
– Funktionsdefinition einer Funktion, die kein Resultat liefert:
void f(int x);
– Typangabe in einer parameterlosen Funktion:
int f(void);

Beispiele:

void *ptr; /* Zeiger auf nichts, generischer Zeiger */


void f1(int x); /* Funktion liefert keinen Ergebniswert */
int f2(void); /* Funktion hat keine Parameter */

int i;
i = 3+4;
(void) 3+4; /* explizites „Wegschmeißen“ eines Wertes */
Programmieren in C 98
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zusammengesetzte Typen

Zusammengesetzte Typen sind induktiv über den bisher definierten


Basistypen definiert. Seien T, T1,...,Tn Typen. Dann sind in C auch Typen:

1. Zeiger auf T
2. Feld von T
3. Struktur von T1,...,Tn
4. Vereinigung von T1,...,Tn
5. Funktion, die Argumenttypen T1,...,Tn hat und einen
Resultattyp T besitzt

Programmieren in C 99
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zeigertyp

• Zu einem Typ T (Beispiel: int) kann man einen Typ Zeiger auf T
definieren (Beispiel: Zeiger auf int).
• Syntax für Variablendeklaration: T *varname;
• Zeiger = Adresse im (virtuellen) Adressraum
• Ein Zeiger kann dann auf ein Objekt des Basistyps zeigen
• Der spezielle Zeigerwert NULL (in <stddef.h> definiert) gibt an, dass
der Zeiger momentan auf kein Objekt zeigt.
• Zeiger auf T1 und Zeiger auf T2 sind nicht typkompatibel!
• Generischer Zeigertyp void * ist typkompatibel zu allen Zeigertypen,
d.h. man kann einen Wert vom Typ void * zu einem beliebigen
anderen Zeigertyp umwandeln und umgekehrt.
• die zwei wichtigsten Operatoren für Zeiger sind:
– Adressoperator &: Angewandt auf ein Objekt vom Typ T liefert es die
Adresse dieses Objektes als Typ Zeiger auf T
– Dereferenzierungsoperator *: Angewandt auf einen Zeigerwert p vom Typ
Zeiger auf T liefert es den Wert vom Typ T, auf den der Zeiger zeigt (falls
dies ein zulässiger Zeigerwert ist)

Programmieren in C 100
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

int i, j; Adresse = 100 104 108


int *ptr_i;
i j ptr_i

i = 5; 5 ? ?

ptr_i = NULL; 5 ? 0

ptr_i = &i; 5 ? 100

j = *ptr_i; 5 5 100

*ptr_i = 7; 7 5 100

ptr_i = &j; 7 5 104

Programmieren in C 101
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Feldtyp

• Feld ist Ansammlung von Objekten gleichen Typs, die hintereinander


im Speicher abgelegt sind
• Für jeden Typ T (außer void und Funktionstypen) kann man einen Typ
Feld von T definieren: T feldname[Dimensionsangabe]
• Mehrdimensionale Felder möglich: T feldname[dim1]...[dimn]
• Feldelemente:
– Indizierung über feld[index]
– Index beginnt bei 0 bis dim-1
– Bei mehrdimensionalen Feldern: feldname[index1]...[indexn]
– Feldname[index1,...,indexn] ist syntaktisch erlaubt, bedeutet aber
etwas vollkommen verschiedenes!

Programmieren in C 102
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Speicherung von Feldern

• Im Gegensatz zu Java wird in C ein zweidimensionales Feld nicht als


Vektor von Zeigern implementiert, sondern alle Feldelemente
hintereinander im Speicher abgelegt (d.h. dim1*dim2 Elemente).
• Zeilenweise Abspeicherung, d.h. letzter Index ändert sich schneller.
• Optimierung des Speicherzugriffs (und damit der Laufzeit) bei
mehrdimensionalen Feldern: Letzten Index schneller laufen lassen!

// C // Java
int x[2][3]; int[][] x = new int[2][3];

// Zugriff // Zugriff
for (int i=0; i<2; i++) for (int i=0; i<2; i++)
for(int j=0; j<3; j++) for(int j=0; j<3; j++)
x[i][j] = i+j; x[i][j] = i+j;

Vektor mit Zeigern Daten


Daten
x x

Programmieren in C 103
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Dimensionsangaben

• Explizite Dimensionsangabe in eckigen Klammern (Normalfall)

Beispiel:
int a[10]; /* int-Feld der Dimension 10 */
int b[3][4]; /* int-Feld der Dimension 3x4 */

• Implizite Dimensionsangabe durch Angabe eines Initialisierungswertes

Beispiel:
char str[] = "Init.wert"; /* char-Feld der Dim.10 (inkl \0) */
int c[] = {1,2,3,4}; /* int-Feld der Dimension 4 */

• Weglassen der Dimensionsangabe (nur innerste Dimension möglich


bei mehrdimensionalen Feldern):
– Bei formalen Funktionsparametern (Typ T wird automatisch in Zeiger
auf T umgewandelt)
– Objekt hat external linkage (später) und Definition geschieht anderswo
– Feld wird im gleichen Modul (Datei mit allen includes) später deklariert

Programmieren in C 104
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele zum Weglassen der Dimensionsangabe

/* Funktionsparameter */
void myfun ( int a[] );

/* external linkage */
int d[]; /* in Datei x.c */
int d[10]; /* in Datei y.c */

/* gleiches Modul */
int e[][4]; /* Matrix der Dimension ?x4 */
int e[2][4]; /* Matrix der Dimension 2x4 */

Programmieren in C 105
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Dimensionsangaben

• Dimensionsangaben müssen zur Übersetzungszeit auswertbar d.h.


konstant sein.
• Ausnahmen (C99):
– variable length arrays
• Größe wird erst zur Laufzeit festgelegt
• Nur für lokale Feldvariablen in Blöcken erlaubt (d.h. kein static oder extern
möglich)
• Feld wird jedes mal bei Eintritt in den Block angelegt am Deklarationspunkt
und wird gelöscht, wenn der Block verlassen wird
– Formale Feldparameter in Funktionen können durch vorangehende Parameter
dimensioniert werden oder in Funktionsprototypen mit * angegeben werden
/* Feldparameter mit variabler Länge */
void myfun ( int n, int m, int a[n][m] )
{
/* Feld mit variabler Länge */
int dim = n*n, b=m+5;
int feld[dim];
}

/* Funktionsprototyp */
void yourfun ( int a[*][*] ) ;
Programmieren in C 106
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zusammenhang Felder-Zeiger

Es besteht ein enger Zusammenhang zwischen Feldern und Zeigern.


Schreibt man den Feldnamen in einem Ausdruck, dann wird dies implizit
umgewandelt in einen Zeiger auf die Adresse des ersten Feldelementes
(Startadresse des Feldes)

Beispiel:
int a[100];
int *ptr;

ptr = a; // ptr zeigt auf erstes Element von a, d.h. a[0]


ptr = &(a[0]); // äquivalent zu letzter Zeile
*ptr = 5; // ptr zeigt auf die absolute (virtuelle) Adresse 5
*ptr = 7; // In die Speicherstelle 5 wird der Wert 7 geschrieben

Programmieren in C 107
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Strukturtyp

Mehrere benannte Komponenten lassen sich in C zu einem neuen


Strukturtyp zusammenfassen. Die einzelnen Komponenten werden
hintereinander im Speicher abgelegt, es können jedoch „Löcher“ im
Speicher zwischen den einzelnen Komponenten entstehen (Padding).

Syntax:
struct Bezeichner { Komponentenliste };

Der Bezeichner ist optional. Der Name des neu eingeführten Typs ist struct
Bezeichner, d.h. inkl. struct. Die Komponenten haben die Form von
Deklarationen, im Normalfall in der einfachen Form

Typangabe Komponentenname ;

Zur Definition von rekursiven Datenstrukturen kann man innerhalb der


Komponentenliste schon auf den neueingeführten Typ T über einen
Zeigertyp Zeiger auf T verweisen.
Programmieren in C 108
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* Typdefinition */
struct complex
{
float re;
float im;
};

/* Variablendeklarationen mit dem neuen Typen */


struct complex complexe_variable = {3.14, 2.71},
*ptr_cvar = &complexe_variable;

complexe_variable ptr_cvar

re 3.14

im 2.71

Programmieren in C 109
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* Typdefinition */
struct complex
{
float re;
float im;
};

/* "Konstruktor" für diesen Datentyp in C */


struct complex new_complex ( float re, float im )
{
struct complex c;

c.re = re; // Zuweisung an die Komponenten der Struktur


c.im = im;

return c; // Hier wird der Wert der lokalen Variablen c zurückgegeben!


}

Programmieren in C 110
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* Typdefinition und Variablendeklaration in einem Schritt */


struct Binaerbaum /* Typ struct Binaerbaum */
{
int daten;
struct Binaerbaum *linker_Sohn; // Zeiger linker Sohn
struct Binaerbaum *rechter_Sohn; // Zeiger rechter Sohn
}
Baum1, Baum2, // 2 Variablen dieses Typs
*ptr_Baum; // 1 Variable mit Zeigertyp

Baum1 Baum2 ptr_Baum

daten 0 0
linker_Sohn

rechter_Sohn

Programmieren in C 111
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Referenzierung der Komponenten
Der Zugriff auf die einzelnen Komponenten einer Strukturvariablen
erfolgt über

Variablenname . Komponentenname

Beispiel:
complexe_variable.re = 1.0;
complexe_variable.im = 0.0;

Für Variablen, die den Typ Zeiger auf Struktur haben, gibt es eine
abkürzende Schreibweise (diese wird praktisch nur verwandt!). Statt
(*Variablenname).Komponentenname:

Variablenname -> Komponentenname

Beispiel:
ptr_cvar->re = 1.0;
(*ptr_cvar).re = 1.0; // äquivalent zu letzter Zeile

Programmieren in C 112
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Weiteres Beispiel
struct Binaerbaum baum1 = {1, NULL, NULL},
baum2 = {2, NULL, NULL},
baum3 = {3, NULL, NULL},
ptr_baum = &baum1;

Baum1 Baum2 Baum3 ptr_Baum

daten 1 2 3
linker_Sohn

rechter_Sohn

ptr_Baum->linker_Sohn = &Baum2; Baum1


ptr_Baum->rechter_Sohn = &Baum3;
1
Baum2 Baum3

2 3

Programmieren in C 113
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Padding

Auf vielen Rechner gibt es Einschränkungen bzw. Leistungseinbußen,


wenn Daten bestimmten Basistyps nicht an Wortgrenzen im Speicher
abgelegt sind (Alignment).

Beispiel:
float x;
double y;

Angenommen, ein float-Wert belegt 4 Bytes und ein double-Wert 8 Bytes.


Würden die beiden Variablen hintereinander (ohne Lücke) im Speicher
abgelegt, so würde die Variable y nicht auf einer Adresse abgelegt, die ein
Vielfaches ihrer Größe ist.

Aus diesem Grunde kann es sein, dass ein Compiler automatisch eine
(kleinen) Zwischenraum zwischen den Komponenten einfügt.
0 4 8

x frei y
Programmieren in C 114
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Operationen auf Strukturen

Es ist möglich
1. Eine Strukturvariable einer anderen Strukturvariablen gleichen Typs
zuzuweisen
2. Dass eine Funktion eine Struktur als Ergebnistyp hat
3. Dass eine Struktur als Parameter an eine Funktion übergeben wird

Es ist nicht möglich zwei Strukturvariablen auf Gleichheit zu testen.


Wieso?

Programmieren in C 115
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Bit-Felder

Innerhalb einer Struktur, und nur dort, ist die Definition von Bit-Feldern
erlaubt. Die Strukturkomponenten, die das Bit-Feld ausmachen, müssen
einen der Typen int, unsigned int oder signed int haben.

Beispiel:
struct RX_opcode
{
unsigned int opcode:8; /* Operationscode */
unsigned int R1:4; /* Register 1 */
unsigned int X2:4; /* Segmentregister */
unsigned int B2:4; /* Basisregister */
unsigned int D2:12; /* Displacement */
unsigned int :0; /* Alignment */
};

Hinter dem Typnamen der Komponenten steht der (optionale) Name der
Komponenten gefolgt von einem Doppelpunkt und der Anzahl (nicht-
negativ, konstant) der Bits. Ist die Anzahl 0, bewirkt dies das Ablegen einer
eventuell folgenden Bit-Komponenten auf einer Wortgrenze.
Maximalgröße eines Bit-Feldes: Größe eines int
Programmieren in C 116
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Vereinigungstyp

Die Syntax zur Definition eines Vereinigungstyps ist fast identisch mit der
eines Strukturtyps, lediglich das Schlüsselwort ist union statt struct:

union Bezeichner { Komponentenliste };

Der Zugriff auf die Komponenten erfolgt ebenfalls durch

Variablenname.Komponentenname

In der Abspeicherung der Komponenten besteht jedoch ein grundlegender


Unterschied. Beim struct werden die Komponenten hintereinander im
Speicher abgelegt, beim union übereinander! Die Größe des
Vereinigungstyps (d.h. die Ausdehnung im Speicher) ist die Größe der
größten Komponenten.

Ein Vereinigungstyp sollte nur eingesetzt werden, wenn dies wirklich


erforderlich ist.

Programmieren in C 117
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

struct Datum
{
enum {double_Datum, int_Datum } Datumtyp; /* 1. Komponente struct */
union
{
double dwert;
int iwert;
} Datumwert; /* 2. Komponente des struct */
} datum; /* Variable vom Typ struct Datum */

datum.Datumtyp = int_datum;
datum.Datumwert.iwert = 13657;

datum.Datumtyp = double_datum;
datum.Datumwert.dwert = 13657.0;

/* Erlaubt, macht aber hier keinen Sinn */


datum.Datumwert.dwert = 13657.0;
... = datum.Datumwert.iwert;
Adresse= 100 104
Datumtyp dwert

iwert

Programmieren in C 118
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Funktionstyp

Zu einem Typ T (außer Feld- oder selbst Funktionstyp) kann man einen
Typ Funktion, die T als Resultat liefert konstruieren. Die "normale" Form
dafür sieht aus (später mehr):

Resultattyp Funktionsname ( Parameterangaben )

Beispiel:
int myfun (int x, float y);

3 Operationen sind mit Funktionsbezeichnern möglich:


• Aufruf der Funktion: myfun(1, 3.14);
• Benutzen der Funktion als aktuellen Parameter: anderefun(1, myfun);
• Zuweisung an Variable entsprechenden Typs: funvariable = myfun;

Erscheint ein Funktionsname außerhalb eines Funktionsaufrufes, d.h. nicht


myfun(...), so wird der Typ des Ausdrucks automatisch in Zeiger auf eine
Funktion, die T liefert umgewandelt (siehe Operation 3 oben).

Programmieren in C 119
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Funktionsdeklaration und -definition

Unterschied zwischen Funktionsdefinitionen und -deklarationen:

1) Funktionsdefinition:
Es wird ein Objekt dieses Typs erzeugt (Speicher verbraucht).

Beispiel:
int square( int x )
{
return x*x;
}

2) Funktionsdeklaration:
Es werden nur Angaben zum Typ gemacht, es wird kein Speicher
verbraucht.

Beispiel:
int square( int x ) ;

Programmieren in C 120
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* Funktion, die kein Argument hat und ein int-Resultat liefert */


int fun1(void);

/* Funktion, die ein int-Argument erwartet und kein Ergebnis liefert */


void fun2( int x );

/* Strukturtyp */
struct complex {float x; float y;};

/* Funktion, die 3 Argumente erwartet:


- ein int
- ein float
- ein struct complex
und einen Zeiger auf ein int als Ergebnis liefert.
*/
int * fun3(int x, float y, struct complex z);

Programmieren in C 121
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typdeklaration

Mit einer typedef-Deklaration kann man ein Synonym (Abkürzung) für einen
anderen Typen einführen, es entsteht dadurch kein neuer Typ!

Vorteile:
• Komplizierte Typen sind lesbarer
• Bedeutungsvolle Namen für Typen

Die allgemeine Syntax ist:

typedef Deklaration;

Deklaration wird später besprochen. Im einfachen Fall sieht dies so aus:

typedef Typ typedef-name ;

Programmieren in C 122
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* Counter (der neu eingeführte Bezeichner) ist ein Synonym für int */
typedef
int /* Typangabe */
Counter; /* neuer Name */

/* Unter Complex ist der struct-Typ ab jetzt ansprechbar */


typedef
struct {float re; float im;} /* Typangabe */
Complex ; /* neuer Name */

Complex c;
c.re = 5.0;
c.im = 0.0;

/* Hier wird es komplizierter und stimmt nicht mehr mit dem einfachen
Schema "typedef Typ Name" überein. Hier wird der Name "Cfun"
eingeführt, der ein Synonym ist für den Typ "Zeiger auf eine Funktion,
die kein Argument nimmt und ein Complex als Resultat liefert.
*/
typedef Complex (*Cfun)(void);

Programmieren in C 123
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typzusätze

Typzusätze (type qualifier) helfen einem Compiler effizienten und korrekten


Code in Spezialfällen zu erzeugen.

Durch den Zusatz const gibt man an, dass auf dieses Objekt nur lesen
zugegriffen wird.

Durch den Zusatz volatile gibt man an, dass dieses Objekt auch
außerhalb des Programms verändert werden kann (z.B. in parallelen
Programmen). Die Position der Typzusätze in einer Definition oder
Deklaration ist wichtig (s.u.)!

Der Zusatz restrict teilt dem Compiler im Zusammenhang mit Zeigern mit,
dass dieser Zeigerwert zur Zeit die einzige Möglichkeit ist, auf das Objekt
zuzugreifen (d.h. kein anderer Zeiger zeigt auf das gleiche Objekt).

Programmieren in C 124
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

const int x = 5; // Wert von x wird nicht mehr modifiziert

int fun(const int x) {...} // Argument x wird nicht verändert

const int *const_ptr; // Zeiger auf int-Konstante


int * const ptr_const; // konstanter Zeiger auf int

volatile int x; // Synchronisationsvariable


void synchronisieren(void)
{
/* Frage solange Wert ab, bis dieser (von außen) auf einen Wert ungleich
0 gesetzt wird.
*/
while(x == 0)
;
}

extern char * restrict ptr; // kein anderer Zeiger zeigt auf das Objekt,
// auf das ptr zeigt
void alloc(void)
{
ptr = malloc(sizeof(*ptr)); // nur ptr zeigt auf dieses Objekt
}

Programmieren in C 125
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 126
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Deklarationen

• Deklaration = Assoziation eines Bezeichners mit einem Objekt


• Definition = Deklaration + Speicherbereich
• Bezeichner für folgende Objekte möglich:
– Variablen
– Funktionen
– Typen
– Typbezeichner
– Komponenten von Struktur- oder Vereinigungstypen
– Aufzählungskonstanten
– Anweisungslabel
– Präprozessor-Makros

Programmieren in C 127
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Deklarationen

Deklarationen bestehen aus:

1. Optionaler Speicherklassenangabe
2. Optionaler Typangabe
3. Einem Deklarator mit dem Namen des zu deklarierenden Objektes
4. Optionale Initialisierungswerte für dieses Objekt (nur in Definition)

Beispiel:
static int x = 5;

Programmieren in C 128
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Programmhierarchie

• Ein C-Programm kann aus mehreren Programmdateien bestehen


• Eine Programmdatei (xyz.c) kann aus mehreren top-level-
Deklarationen bestehen, in denen globale Variablen und Funktionen
definiert werden
• Jede Funktion kann Deklarationen von formalen Parametern der
Funktion haben, sowie einen Funktionsrumpf
• Jeder Funktionsrumpf kann mehrere evtl. geschachtelte Blöcke
enthalten
• Jeder Block besteht aus optionalen Deklarationen gefolgt von
optionalen Anweisungen

• Eine Übersetzungseinheit ist eine Datei inkl. aller eingefügten


Headerdateien

Programmieren in C 129
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Programmhierarchie

C-Programm

x.c y.c z.c

Top-level Deklarationen:
- globale Variablen
- Typdeklarationen
- Funktionsdefinitionen Formale Parameter

Funktionsdefinition: int f ( int x, float y )


{
int i;
Rumpf (äußerer Block) i = x;
{
Innerer Block int j;
j = i;
}
}
Programmieren in C 130
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

datei1.c datei2.c

int i; // Variablendefinition int i; // Variablendefinition

// Typdeklaration // Funktionsdefinition
struct mystruct int myfun( int x )
{ {
int x; return x+1;
float y; }
};
int j; // Variablendefinition
// Funktionsdefinition
int main( int argc, char **argv ) // Funktionsdefinition
{ int myfun2( int x )
int i = 0; // keine top-level Deklaration {
return x+2;
printf("myfun1=%d\n", myfun(i); }
printf("myfun1=%d\n", myfun2(i));
}

Programmieren in C 131
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Gültigkeitsbereich von Bezeichnern

Bezeichner haben einen Gültigkeitsbereich (scope), innerhalb dessen die


Deklaration aktiv (bekannt) ist. In C existieren 5 mögliche Gültigkeitsbereiche:

1. Bezeichner einer top-level-Deklaration: vom Deklarationspunkt bis


zum Ende der Datei

2. Bezeichner in einer Deklaration von formalen Parametern einer


Funktion: vom Deklarationspunkt bis zum Ende der Funktion bzw.
des Funktionsprototypen

3. Bezeichner zu Beginn eines Blocks: Deklarationspunkt bis Ende


Block

4. Anweisungslabel: innerhalb der umschließenden Funktion

5. Makroname: vom #define bis zum Ende der Datei bzw. bis zu einem
entsprechenden #undef

Programmieren in C 132
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

/* TEST_CODE ist bis zum Ende der Datei gültig */


#define TEST_CODE

/* global_var ist gültig bis zum Ende der Datei */


int global_var;

/* Parameter x ist nur innerhalb der Klammern gültig */


double sin( double x );

int main( int argc, char **argv )


{
/* local_var ist nur innerhalb dieses Blocks gültig */
int local_var;

/* Das Label ende ist innerhalb dieser Funktion gültig */


ende: ;
}

Programmieren in C 133
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Überladen von Bezeichnern

In C existieren 5 Namensklassen. Ein Bezeichner x kann gleichzeitig in allen


5 Klassen existieren. Aus dem Kontext ist definiert, welcher Bezeichner
welcher Namensklasse gemeint ist.
Die Nutzung des gleichen Bezeichners in mehr als einer Namensklasse ist
schlechter Programmierstil!

Die Namensklassen sind:


1. Präprozessorname (nach Präprozessorphase nicht mehr)
2. Anweisungslabel
3. Name eines Struktur-, Vereinigungs-, Aufzählungstypen
4. Komponente eines Struktur-, Vereinigungstypen
5. Alle anderen Bezeichner

Programmieren in C 134
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

#define x y /* Präprozessorname */

int main( int argc, char **argv)


{
struct x /* Name eines Strukturtyps */
{
int x; /* Name einer Komponenten */
} x; /* Name einer Variablen */

x: ; /* Label */

/* alles klar? */
x.x = 4;
goto x;
}

Programmieren in C 135
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Sichtbarkeit

Zusätzlich zum Gültigkeitsbereich eines Bezeichners und den


Namensklassen kann es jedoch durch eine (erlaubte) Mehrfachverwendung
des gleichen Bezeichners zu Konflikten kommen:

Beispiel:
int f; /* globale Variable */
int main(int argc, char **argv)
{
double f; /* Block-lokale Variable */

f = 5.0; /* welches f? */
}

Namenskonflikte treten dann auf, wenn gleiche Bezeichner zur gleichen


Namensklasse gehören, aber in zwei verschiedenen (geschachtelten)
Gültigkeitsbereichen. Prioritätsegeln:
• Formale Funktionsparameter überschreiben globale Deklarationen
• Deklaration am Blockanfang überschreiben äußere Deklarationen

Programmieren in C 136
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Vorwärtsreferenzen

Normalerweise muss vor Benutzung eines Bezeichners dieser deklariert sein


(Compiler muss Größe etc. wissen).
Zwei Ausnahmen:
1. Anweisungslabel in goto-Anweisung darf vor der Label-Deklaration
genutzt werden
2. Ein Typbezeichner eines Struktur, Vereinigungs oder Aufzählungstypen
kann schon vor seiner Deklaration in einer typedef-Deklaration oder in
Verbindung mit einem Zeigertypen benutzt werden.

Beispiel: Beispiel:

struct type_a { int main(int argc, char **argv)


struct type_b *x; {
}; goto ende;
struct type_b {
struct type_a *y; ende: ;
}; }

Programmieren in C 137
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Lebensdauer von Speicherobjekten

Objekte (Variablen, Funktionen) existieren zur Laufzeit eines Programms.


Die Lebensdauer (storage duration) eines Objektes ist die Zeitdauer, die der
Speicherbereich angelegt ist, der das Objekt aufnimmt. Man unterscheidet:

1. Statische Lebensdauer: Speicher wird vor dem Start des Programms


ein mal angelegt und wird erst wieder freigegeben, wenn das
Programm beendet wird. (Funktionen, top-level Variablen, static-
Variablen)

2. Automatische Lebensdauer: Der Speicher wird jedes mal zu Beginn


eines Blocks angelegt und am Ende des Blocks wieder frei gegeben.
(Lokale Variablen eines Blocks, Parameter einer Funktion)

3. Dynamische Lebensdauer: Der Speicherbereich wird explizit durch


den Programmierer verwaltet (angelegt, freigegeben).

Programmieren in C 138
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

Codesegment
int global_j; main
f
int f( int arg )
{
int local_j; Datensegment
local_j = arg;
global_j = arg; global_j
return global_j + local_j;
}

int main(int argc, char **argv)


{
Stacksegement
int i,j;
argc
argv
global_j = 6;
i
for(i = 0; i < 2; ++i)
J
j = f(i);
arg
}
local_j

Programmieren in C 139
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

Codesegment
main
int main(int argc, char **argv)
{
static int x; Datensegment
auto int y;
int *p; x

p = (int *) malloc(sizeof(*p));
free(p);
} Stacksegement
argc
argv
y
p

Heap
*p

Programmieren in C 140
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Bindung von Bezeichnern

Teilt man das gesamte Programm auf mehrere Dateien auf (Datei_1.c, ...,
Datei_n.c), so ist weiterhin die Bindung von Bezeichnern von Interesse:
1. Externe Bindung: Der gleiche Bezeichner bezeichnet in allen Dateien
das gleiche Objekt.
2. Interne Bindung: Nur innerhalb einer Datei bezeichnet ein Bezeichner
das gleiche Objekt.
3. Keine Bindung: Objekte, die nur einmal vorkommen.

Regeln (Speicherklasse=auto, register, static, extern):

Top-level Innerhalb Block


Keine Speicherklassen- Variable: externe Bindung Variable: ohne Bindung
angabe Funktion: wie mit extern Funktion: wie mit extern
Speicherklassenangabe Interne Bindung Variable: ohne Bindung
static Funktion: interne Bindung
Speicherklassenangabe a) Bindung wie gleicher Bezeichner auf top-level a) Bindung wie top-level
extern b) Falls nicht vorhanden: externe Bindung b) Nicht vorhanden: extern

Programmieren in C 141
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel
Top-level Innerhalb Block
Keine Speicherklassen- Variable: externe Bindung Variable: ohne Bindung
angabe Funktion: wie mit extern Funktion: wie mit extern
Speicherklassenangabe Interne Bindung Variable: ohne Bindung
static Funktion: interne Bindung
Speicherklassenangabe a) Bindung wie gleicher Bezeichner auf top-level a) Bindung wie top-level
extern b) Falls nicht vorhanden: externe Bindung b) Nicht vorhanden: extern

int glo_var; /* externe Bindung */


int getchar(); /* externe Bindung */
static int datei_var; /* interne Bindung */
static int f(); /* interne Bindung */
extern int glo_var; /* externe Bindung (s.o.) */
extern int glo_var2; /* externe Bindung */
int main(int argc, char **argv)
{
int x; /* keine Bindung */
int getchar(); /* externe Bindung (s.o.) */
static int y; /* keine Bindung */
static int g(); /* interne Bindung */
extern int glo_var; /* externe Bindung (s.o.) */
}
Programmieren in C 142
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Speicherklasse

Die Speicherklasse eines Objektes hat Einfluss auf:

1. Bindung
2. Lebensdauer
3. Gültigkeitsbereich

Es existieren 4 Speicherklassen:

1. auto
2. register
3. static
4. extern

Die Angabe einer Speicherklasse ist optional bei einer Definition / Deklaration
eines Objektes möglich. Dann muss sie an erster Stelle stehen.

Programmieren in C 143
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Speicherklasse auto

Nur in Deklarationen am Anfang von Blöcken erlaubt


Dort ist dies auch der Default und wird deshalb meist weggelassen

Lebensdauer:
automatische Lebensdauer (bei Blockeintritt wird Objekt erzeugt und bei
Verlassen des Blocks vernichtet)

Gültigkeit:
Das Objekt ist bis zum Ende des Blocks bekannt.

Beispiel:

int myfun()
{
int x;

return 3;
}

Programmieren in C 144
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Speicherklasse register

Ursprüngliche Idee: Hinweis für Compiler (hat heute nur Nachteile; s.u.)
Nur für lokale Variablen und Funktionsparameter möglich
Kein Adressoperator zusammen mit register-Variablen möglich
Sonst wie auto

Lebensdauer:
automatische Lebensdauer (bei Blockeintritt wird Objekt erzeugt und bei
Verlassen des Blocks vernichtet

Gültigkeit:
Das Objekt ist bis zum Ende des Blocks bekannt.

Beispiel:

int myfun(register int y)


{
register int x;

return 3 * y;
}
Programmieren in C 145
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Speicherklasse static

In Funktionsdefinition: keine externe Bindung


In Funktionsdeklaration: Funktion wird später in Datei noch definiert (dann
ebenfalls mit static-Angabe)

Lebensdauer:
Statische Lebensdauer (Wert bleibt auch bei Verlassen des Blocks
erhalten)

Gültigkeit:
Das Objekt ist nur innerhalb des Blocks (Variablen) bzw. innerhalb der
Datei (Funktionen, top-level Variablen) bekannt.

Beispiel:

int myfun(int y)
{
static int x;

return x * y;
}
Programmieren in C 146
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Speicherklasse extern

In top-level Deklarationen und am Anfang eines Blocks

Lebensdauer:
Statische Lebensdauer (Wert bleibt auch bei Verlassen des Blocks
erhalten)

Gültigkeit:
Das Objekt einer top-level Deklaration ist global bekannt. Objekte, die am
Anfang eines Blocks deklariert werden, sind nur innerhalb des Blocks
bekannt.

Beispiel:

extern int yourfun(int x);


extern int x;
int myfun(int y)
{
return x * y;
}
Programmieren in C 147
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Default-Speicherklasse

Ist keine Speicherklassenangabe in einer Deklaration vorhanden, gilt


folgender Default:

1. Top-level Deklarationen haben Speicherklasse extern


2. Funktionsdefinitionen haben Speicherklasse extern
3. Parameterdeklarationen ohne Angabe register (nur diese Angabe wäre
dort erlaubt) bedeuten "kein register"
4. Deklarationen am Anfang eines Blocks haben die Default-
Speicherklasse extern für Funktionen und auto für alle anderen
Objekte

Programmieren in C 148
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

void myfun(void)
{
extern int lock; /* global bekannte Variable */
register int i; /* lokale bekannte Variable */
auto int j; /* lokal bekannte Variable */
static int k; /* lokal bekannte Variable; behält Wert */

static int f(void); /* lokal bekannte Funktion */


extern int g(void); /* global bekannte Funktion */
}

Programmieren in C 149
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Deklarationen

Allgemeine Form einer Deklaration:

Typangabe Deklarator

Wobei:

1. Typangabe die Typspezifikation einschließlich


Speicherklassenbezeichner und Typzusätze ist
2. Deklarator den Bezeichner des Objektes enthält, das deklariert
werden soll

Beispiel:
int x; /* Typangabe=int; Bezeichner=x */
const int y; /* Typangabe=const int; Bezeichner=y */
int *const z; /* Typangabe=int; Bezeichner=z (Rest gehört zu Dekl.) */
float a[3]; /* Typangabe=float; Bezeichner=a */

Programmieren in C 150
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Art der Deklaration

Deklarator hat die Form:

Bezeichner

Dann ist der Typ des Bezeichners Typangabe.

Beispiel:
int x; /* x hat Typ int */
struct { float re; float im; } c1; /* c1 hat den Strukturtyp */
typedef struct {float re; float im; } Complex;
Complex c2; /* c2 hat Typ Complex */

Programmieren in C 151
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Art der Deklaration

Deklarator hat die Form:

( Deklaration )

Dann ist der Typ des Bezeichners der, der durch Deklaration spezifiziert
wird. Die Klammern dienen der besseren Lesbarkeit oder zum Setzen von
Bindungsprioritäten.

Beispiel:
int (x); /* x hat Typ int */
int *(*p); /* entspricht **p */
int (*q)[]; /* Zeiger auf Feld von int's */
int *(q[]); /* Feld von Zeigern auf int */

Programmieren in C 152
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Art der Deklaration

Deklarator hat die Form:

* Deklaration
* type_qualifier_list Deklaration

Dann ist der Typ des Bezeichners ejn Zeiger auf den Typ, der durch
Typangabe Deklaration spezifiziert wird, unter Berücksichtigung der
type_qualifier_list. type_qualifier_list ist eine optionale Liste aus const
und/oder volatile.

Beispiel:
int *x; /* x hat Typ: Zeiger auf int */
const int *ptr_const_ptr; /* Zeiger auf int-Konstante */
int * const constr_ptr; /* Konstanter int-Zeiger */
int *volatile vol_ptr; /* volatiler int-Zeiger */
struct { float re, im;}
*complex_ptr; /* Zeiger auf Struktur */

Programmieren in C 153
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Art der Deklaration

Deklarator hat die Form:

Deklaration [ ]
Deklaration [const_expr ]
Deklaration [var_expr] C99
Deklaration [*] C99

Dann ist der Typ des Bezeichners ejn Feld des Basistyps, der durch
Typangabe Deklaration spezifiziert wird. const_expr muss ein konstanter
Ausdruck sein, dessen Wert echt größer 0 sein muss. Die Fälle, in denen
die Dimensionsangabe weggelassen werden kann, wurde bereits
besprochen. Ebenso Felder variabler Größe.

Beispiel:
float a[3]; /* float-Feld mit 3 Elementen */
float *(pa[3]); /* Feld von float-Zeigern */
int x[]; /* int-Feld ohne Dimensionsangabe */
volatile int y[3]; /* Feld von volatilen int's */
float b[3][4]; /* Feld der Dimension 3x4 */
Programmieren in C 154
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Art der Deklaration

Deklarator hat die Form:

Deklaration ( )
Deklaration ( id_list )

Dann ist der Typ des Bezeichners ejne Funktion, die einen Wert von
einem Typ liefert, der durch Typangabe Deklaration spezifiziert wird. Nicht
zulässig als Basistyp sind Funktions- oder Feldtypen. Fehlt id_list, so
bedeutet dies, dass keine Angaben zu Parametern der Funktion gemacht
werden (nicht notwendigerweise, dass diese Funktion keine Parameter
besitzt). Diese Form der Spezifikation eines Funktionstypen sollte nicht
mehr verwendet werden. Statt dessen gibt es Funktionsprototypen.

Beispiel:
int f(); /* Funktion, die int liefert */
int *(g()); /* Funktion, die Zeiger auf int liefert */
int (*h)(); /* Zeiger auf Funktion, die int liefert */
int f(x,y) /* Funktionsdefinition mit zwei formalen Parametern */
float x,y; {...} /* so nicht mehr benutzen! */
Programmieren in C 155
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Art der Deklaration

Deklarator hat die Form:

Deklaration ( parameter_type_list )

Dann ist der Typ des Bezeichners ejne Funktion, die einen Wert von
einem Typ liefert, der durch Typangabe Deklaration spezifiziert wird.
Nicht zulässig als Basistyp sind Funktions- oder Feldtypen.
parameter_type_list ist eine durch Kommas getrennte Liste von Typen
oder Deklaratoren, die Typangaben zu den den Funktionsparametern
machen:
• Die Typangabe void besagt, dass diese Funktion keine Parameter hat.
• Fall die Liste mit ... endet, so bedeutet dies, dass keine Angaben über
Anzahl und Typ weiterer Parameter gemacht wird.
• Als Speicherklassenangabe ist nur register erlaubt

Programmieren in C 156
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

int ((*u) ( char (*) (long),


float
)
)
(double, ... );

Um solch eine Deklaration zu verstehen, sollte man zuerst versuchen, die


Klammerhierarchie zu verstehen. Anschließend sollte man den Namen des zu
deklarierenden Objektes herausfinden.
Die Deklaration hat die Form: int (*u()) () . Der Name des Objektes ist u. u ist ein
Zeiger auf eine Funktion, die eine Funktion liefert, die wiederum ein int-Resultat
liefert. u hat zwei Parameter: (1) char (*) (long) (hier fehlt der Name des
formalen Parameters, also nur Typangabe) und (2) float. Der erste Parameter ist
ein Zeiger auf eine Funktion, die ein long-Parameter erwartet und ein char als
Ergebnis liefert.
u liefert als Ergebnis eine Funktion, die ein int liefert und selber mindestens
einen Parameter erwartet, dieser ist vom Typ double.

Programmieren in C 157
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Default-Typen

Aus Kompatibilität mit alten C-Compilern ist das Weglassen einer


Typspezifikation für Variablen- und Funktionsdefinitionen erlaubt. Der
Default ist dann int.
Man sollte dies nicht mehr verwenden! In C99 ist dies auch nicht mehr
erlaubt, dort muss ein Typ angegeben werden!

Beispiel:
x;
f(i)
{
return i;
}

Programmieren in C 158
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Priorität der Deklaratoren

Priorität:
1. Klammerung
2. Funktions- und Felddeklaratoren
3. Zeigerdeklaratoren

Beispiel:
int *sum1(); /* Funktion, die Zeiger auf int liefert */
int *a[a2]; /* Feld von Zeigern auf int */
int (*sum2)(void); /* Zeiger auf int-Funktion */

Programmieren in C 159
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Implizite Deklarationen

Erscheint im Programmtext ein bis dahin unbekannter Bezeichner gefolgt


von einer öffnenden Klammer, so wird implizit vom Compiler eine
Deklaration vorgenommen, die diese Funktion als int-Funktion auf dem
top-level deklariert.
Vorsicht: Die Problematik wird klar, wenn man das Beispielprogramm
unten anschaut.

Beispiel:
int main(int argc, char **argv)
{
double x;

x = sin(3); /* sin ist eigentlich vom Typ double sin(double);


Hier wird implizit die Deklaration
extern int sin();
auf dem top-level durchgeführt. */
}

Programmieren in C 160
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Deklaration / Definition bei top-level Variablen

Deklaration = Typangabe
Definition = Typangabe + Anlegen von Speicher

Folgerung: Zu jedem Objekt kann es viele Deklarationen evtl. in


verschiedenen Dateien geben, aber nur eine Definition geben.

Regeln zur Unterscheidung Deklaration/Definition von Variablen auf top-


level:
1. Beginnt eine Deklaration mit dem Schlüsselwort extern (ohne
Initialisierung), so ist dies eine Deklaration.
2. Für jede Variable mit externer Bindung muss irgendwo in einer der
Programmdateien genau eine Definition erfolgen.
3. Ist ein Initialisierungsausdruck vorhanden, so ist es eine Definition (mit
oder ohne extern erlaubt).
4. Schwebende Definition: keine Initialisierung, keine oder static-
Speicherklasse. Kann zu einer Variablen mehrfach in einer Datei
vorkommen. Falls keine externe Definition dieses Bezeichners in der
Datei (inkl. Includes) existiert, wird daraus eine Definition mit
Initialisierungswert 0 gemacht.
Programmieren in C 161
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

extern int i1; /* Deklaration; externe Bindung */


static int i2 = 2; /* Definition; interne Bindung */
extern int i3 = 3; /* Definition; externe Bindung */
int i3; /* schwebende Def. (s.o.); externe Bindung */
int i4; /* schwebende Def. (s.u.); externe Bindung */
int i4; /* schwebende Def. (s.o.); externe Bindung */
static int i5; /* schwebende Def.; interne Bindung */

/* falsch wären:
int i2; Widerspruch zu oben (interne/externe Bindung)
int i5; Widerspruch zu oben (interne/externe Bindung)
*/

Programmieren in C 162
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Deklaration / Definition bei Funktionen

Die Unterscheidung bei Funktionen ist einfach:


1. Folgt auf die schließende Klammer ein Semikolon, so ist dies eine
Deklaration.
2. Folgt kein Semikolon, so ist dies eine Definition (und es muss ein { oder
Typangaben zu Parametern folgen).

Beispiel:
int f1 ( int i, int j ) ; /* Deklaration */

int f2 ( int i, int j ) /* Definition */


{
return i+j;
}

int f3 ( i, j ) /* Definition */
int i,j;
{
return i-j;
}

Programmieren in C 163
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inline Funktionen

• Neu in C99
• Voranstellen des Schlüsselworts inline in Funktionsdefinition und
Funktionsdeklaration
• Hinweis an den Compiler, diese Funktion an Aufrufstellen zu
expandieren (Optimierung).
• Eine Funktion hat eine Inline-Definition, wenn alle top-level
Deklarationen der Funktion in der Übersetzungseinheit inline enthalten
und kein extern vorliegt
• In genau einer Übersetzungseinheit muss aber eine Definition der
Funktion mit extern erfolgen (falls der Compiler keine Inline-Expansion
vornimmt)
• Typische Nutzung einer Inline-Definition in Headerdatei

Programmieren in C 164
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

square.h: abstand.c:
// inline Definition #include <square.h>
// und Deklaration für externe Funktion #include <math.h>
inline double square( double x)
{ // berechne Abstand zweier Koordinaten
return x * x; // Wurzel((x2-x1)^2 + (y2-y1)^2)
} double abstand( double x1, double y1,
double x2, double y2)
{
// Compiler hat 2 Möglichkeiten:
square.c: // 1) inline expandieren
// 2) square explizit aufrufen
#include <square.h> return sqrt( square(x1-x2)
+ square(y2-y1));
// externe Definition }
// durch extern wird inline in square.h
// hier überschrieben
extern double square( double x)
{
return x * x;
}

Programmieren in C 165
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typnamen

An manchen Stellen ist ein Typname verlangt, z.B. bei der Umwandlung
eines Wertes von einen Typ in einen anderen (von int nach long).
Ein Typname ist syntaktisch eine Deklaration ohne Bezeichner.

Beispiel:
int /* int */
int * /* Zeiger auf int */
int *[3] /* Feld mit 3 Zeigern auf int */
int (*const []) (unsigned int,...)
/* Feld unbestimmter Dimension mit konstanten Zeigern
auf Funktionen, die ein int-Resultat liefern und
die einen ersten Parametern vom Typ unsigned int
haben und eine unbestimmte Anzahl und Typen
weiterer Parameter
Wo würde der Bezeichner in einer Deklaration
hingehören?
*/

Programmieren in C 166
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Initialisierungen

In einer Variablendefinition kann man zusätzlich einen Initialisierungswert


mit angeben, mit dem die Variable bei jeder Allokierung vorbesetzt wird.
Der Wert wird jeweils neu berechnet.
Syntax:

Deklaration = Ausdruck

Einschränkungen:
1. Initialisierungsangaben sind nicht erlaubt bei einer Variablendefinition in
einem Block, wenn die Variable interne oder externe Bindung hat.
2. Nur konstante Ausdrücke (zur Übersetzungszeit kann der Wert
ausgerechnet werden) für Objekte mit statischer Lebensdauer und
wenn Typ ein Feld, Struktur oder Vereinigung ist.

Beispiel:
static int i = 5;
static int *j = &i;

Programmieren in C 167
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Regeln zur Initialisierung: Klammerung

Der Initialisierungsausdruck bzw. Teilausdrücke können bzw. müssen durch


{ } geklammert werden:

1. Skalare Ausdrücke können geklammert werden.

Beispiel:
ink k = { 5 * (j+1) };

2. Ausdrücke für nicht-skalare Variablen (Feld, Struktur, Vereinigung)


müssen geklammert werden.

Beispiel:
float Grenze[2] = { -1.0f, 1.0f };

3. Ausnahme: String = Character-Felder

Beispiel:
char str[] = "Stringwert";

Programmieren in C 168
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Regeln zur Initialisierung: Implizite Initialisierungen

Objekte mit statischer Lebensdauer, die nicht explizit initialisiert werden,


werden implizit vorbesetzt:
1. Objekte mit arithmetische Typ werden mit 0 (des entsprechenden Typs)
vorbesetzt
2. Objektes eines Zeigertyps werden mit dem Nullzeiger vorbesetzt.

Beispiel:
static int i; /* bekommt Wert 0 */
static float f; /* bekommt Wert 0.0f */
static double d; /* bekommt Wert 0.0 */
static int *ptr_i; /* bekommt Wert NULL */

Programmieren in C 169
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Regeln zur Initialisierung: Vereinigungstyp

Bei Variablen eines Vereinigungstyps kann man nur Werte für die erste
Komponente des Vereinigungstyps angeben.

Beispiel:
union
{
float x;
int i;
} uvar = 3.0; /* Es ist nicht möglich, uvar.i zu initialisieren */

Programmieren in C 170
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Benannte Initialisierer

• Neu in C99
• Initialisierung bestimmter Teile eines Feldes, Struktur oder Union
• Syntax:
– [expr]=val für Felder
– .name=val für Strukturen und Unions

Beispiel:
// Feldelemente 1,2,7 werden explizit initialisiert (Rest implizit)
int feld1[10] = { [1]=5, [7]=3, [2]=1};

// Feld hat 5 Elemente [1, 2, 5, 4, 5]


int feld2[] = {1, 2, 3, [2]=5, 4, 5};

struct s {
int i;
float f;
char s[4};
} s1 = { .s="str", .f=3.14}; // Komponenten s und f explizit initialisiert

Programmieren in C 171
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Regeln zur Initialisierung: Sonstiges

1. Felder ohne Dimensionsangabe aber mit Initialisierungsausdruck


werden entsprechend der Anzahl der Werte im Initialisierungsausdruck
dimensioniert.
2. Es dürfen nicht mehr Werte angegeben werden, als zu initialisierende
Objekte vorhanden sind (Beispiel: nicht mehr Teilausdrücke, als Feld
Elemente hat).
3. Es dürfen weniger Werte angegeben werden. Dann gelten die Regeln
zur impliziten Initialisierung für die restlichen Elemente (mit 0
vorbesetzen).
4. Die innersten Klammern bei Unterdeklarationen (z.B. 2-dim. Feld)
können weggelassen werden.
5. Regeln gelten rekursiv für Untertypen

Programmieren in C 172
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* impliziert dimensioniertes Feld */


int x[] = { 3, 4, 5 };
int y[] = { {3}, {4}, {5} };

/* Strukturvariable */
struct { float re, im; } cmpl = { 2.0, 1.0 };

/* Geschachtelte Strukturen */
struct
{
int x[3]; /* 1. Komponente: x */
struct {float re, im; } cmpl; /* 2. Komponente: cmpl */
} s[2]
=
{ /* es folgt s[0]
{ { 1,2,3 }, /* 1. Komponente: x */
{ 2.0, 1.0} /* 2. Komponente: cmpl */
}
, /* es folgt s[1] */
{ { 4,5,6 }, /* 1. Komponente: x */
{ 4.0, 2.0 } /* 2. Komponente: cmpl */
}
};
Programmieren in C 173
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* nicht-konstante Ausdrücke */
extern int getchar(void);
int f(void)
{
int ch = getchar() + 'a'; /* bei jedem Aufruf von f */
}

/* Zwei Felder der Dimension 4x3. Die ersten 3 Zeilen werden mit den
vorgegebenen Zahlen explizit initialisiert, die letzte Zeile
jeweils implizit mit Nullen besetzt.
*/
int x[4][3] = { {1,2,3}, {4,5,6}, {7,8,9} };
int y[4][3] = { 1,2,3,4,5,6,7,8,9 };

/* Das jeweils erste Element jeder Zeile wird explizit gesetzt, die
anderen implizit mit Nullen besetzt.
*/
int z[4][3] = { {1}, {4}, {7}, {10} };

Programmieren in C 174
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiele

/* äquivalent: */
char s[] = "abc";
char t[] = { 'a', 'b', 'c', '\0' };

/* Hier wird ein char-Zeiger initialisiert. Der String "abc" kann


nicht (!) verändert werden. *p = 'd' ist nicht zulässig!
char *p = "abc";

/* Rückgriff auf schon definierte Objekte */


static short sh, *ptr_sh = &sh;
static double feld[100];
double *ptr_f = feld;

Programmieren in C 175
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 176
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Ausdrücke

Ausdrücke berechnen einen Wert.

Beispiel:
3+4

Ein l-Wert (links-Wert) ist ein Ausdruck, der auf ein Objekt im Speicher
verweist (z.B. ein Zeigerwert, ein Variablenname). Als Faustregel gilt, dass
alles, was auf der linken Seite einer Zuweisung stehen kann, ein l-Wert ist.

Beispiel:
int a, b;
a = b; /* b ist in diesem Ausdruck ein l-Wert */
a = (3+4); /* (3+4) ist kein l-Wert */

Programmieren in C 177
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Einfacher Ausdruck
Bezeichnung Wert
Konstante Wert der Konstanten
(Bei String-Konst. Zeiger auf 1. Zeichen)
Variablenname Wert der Variablen
(arithm.Typ, Zeigertyp, enum, struct/union)
Variablenname eines Feldtyps Zeiger auf 1. Element (außer bei sizeof; s.u.)
Funktionsname Zeiger auf Funktionscode (außer bei sizeof)
( expression ) Klammerung Ausdruck
(Auswertungsreihenfolge später)

Beispiel:
int i,j;
char *cptr, cfeld[10];
int fn( int (*fun)(void));
int fn1(void);

j = 1; /* Konstante 1 */
cptr = "abc"; /* String-Konstante: Zeiger auf 1. Zeichen */
i = j; /* Variable arithmetischen Typs */
cptr = cfeld; /* Feldname: Zeiger auf 1. Element */
i = fn( fn1 ); /* Funktionsname fn1: Zeiger auf Code */
Programmieren in C 178
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Einfacher Ausdruck

Bezeichnung Wert
x [ expr ] Auswahl Feldkomponente
(x Feld- oder Zeigervariable; expr int-Wert)
x.name Komponente name der Struktur/Union x
x -> name Komponente name einer Struktur, auf die x (Zeigerwert) zeigt.
&x Adresse von x. x muss l-Wert sein.
Kein Bit-Feld, keine Variable der Speicherklasse register.
*x Der Wert, worauf x zeigt. x muss Zeigertyp besitzen.

Beispiel:
int i, a[10];
struct {int ival; } str, *ptr_str;

i = a[3]; // Zugriff auf 3.Komponente des Feldes


str.ival = str.ival + 1; // Zugriff auf Komponente ival der Struktur
ptr_str = &str; // ptr_str zeigt nun auf str
(*ptr_str).ival = (*ptr_str).ival + 1; // Komponente ival inkrementieren
str.ival = str.ival + ptr_str->ival; // ptr_str->ival entspricht (*ptr_str).ival

Programmieren in C 179
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Arbeiten mit Zeigern
// Wichtig in den Beispielen: 0 entspricht false

// kopiere einen String von source nach dest


// (genügend Speicherplatz in dest vorausgesetzt)
char *my_strcpy(char *dest, *char *source)
{
char *save = dest; // Anfang des Resultatstrings merken

// Strings werden mit '\0' abgeschlosssen


while(*dest++ = *source++)
; // in letzter Zeile schon alles gemacht

return save; // Anfang des Resultatstrings als Ergebnis


}

// finde das Ende eines Strings


char *ende_string(char *str)
{
// Strings werden durch '\0' abgeschlossen
while (*p++)
;

return p; // gebe Position (=Zeiger) zurück


}

Programmieren in C 180
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Einfacher Ausdruck

Bezeichnung Wert

(Typname) x Wert x wird (falls möglich) in einen Wert des Typs Typname
umgewandelt (cast-Operation)
(Typname) { init-list } Cast von einem anonymen Objekt eines zusammengesetzten Typs
(C99)
x=y y wird an x (l-Wert) zugewiesen. Ergebnis des Gesamtausdrucks ist
Wert von y. Bei Überlappung der Speicherbereiche nicht definiert.
sizeof expr Größe von expr bzw. Typname in Bytes. Zur Übersetzungszeit
sizeof(Typname) berechnet, also keine Seiteneffekte zur Laufzeit (Bsp.: sizeof ++i).
Feldname: gesamtes Feld und nicht Zeigerwert
Nicht erlaubt: Funktion, Bit-Feld, void, Feld ohne expl. Längenangabe
x (Argumentliste) Funktionsaufruf der Funktion x mit aktuellen Argumenten.
Argumentwerte werden ausgerechnet und auf Laufzeitstack abgelegt
(call-by-value).

Beachte:
Die Zuweisung ist in C eine Operation und keine Anweisung!
Programmieren in C 181
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Einfacher Ausdruck

Beispiel:
int i, j, k;

i = (int) 3.0; /* Typ cast */


k = (j = i); /* Zuweisung j = i ist Ausdruck */
j = sizeof i; /* Größe in Bytes von i */
j = sizeof(int); /* Größe in Bytes vom Typ int */
i = fun1( i, i+1, fun2(j)); /* 3 Argumente werden vorher ausgewertet */

i = 5;
j = 3;
if( i = j ) ++i; /* Häufigster Fehler! Compiler-Option -Wall! */

Programmieren in C 182
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Anonyme Objekte

// Berechnung von Zweierpotenzen für 0<=n<=7 mit table-lookup


// über Typcast eines anonymen Objekts eines zusammengesetzten Typs (hier Feld)
inline int POW2(int n)
{
assert((n >= 0) && (n <= 7)); // nur für 0 <= n <= 7 erlaubt

// Typcast eines Initialisierungsausdrucks und Indizierung dieser Konstanten


return (const int[]) {1,2,4,8,16,32,64,128} [n];
}

struct point { int x; int y; };


void drawPoint(struct point p);
void drawLine(struct point *from, struct point *to);

void meineGrafik(int n)
{
// Arbeiten mit anonymen Objekten
drawPoint((struct point) {.x=n, .y=2*n});
drawLine(&(struct point){.x=n, .y=n}, &(struct point){.x=-n, .y=-n});
}

Programmieren in C 183
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inkrement- / Dekrement-Ausdruck

Bezeichnung Wert
++x Wert von x (l-Wert) wird inkrementiert und wieder in x gespeichert, Der
Wert des Ausdrucks ist der neue Wert von x.
--x Wert von x (l-Wert) wird dekrementiert und wieder in x gespeichert, Der
Wert des Ausdrucks ist der neue Wert von x.
x++ Wert von x (l-Wert) wird inkrementiert und wieder in x gespeichert, Der
Wert des Ausdrucks ist der alte Wert von x.
x-- Wert von x (l-Wert) wird dekrementiert und wieder in x gespeichert, Der
Wert des Ausdrucks ist der alte Wert von x.

Beispiel:
int i, j;

i = 1;
j = ++i + 1; /* i=2, j=3 */
i = 1;
j = i++ + 1; /* i=2, j=2 */

Programmieren in C 184
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Arithmetischer Ausdruck

Bezeichnung Wert

+x Unäres Plus
-x Unäres Minus
x+y Addition
x-y Subtraktion
x*y Multiplikation
x/y Division. Für Ausdruck mit integralem Typ ganzzahlige Division.
x%y Modulo-Bildung (nur integrale Typen).

Beispiel:
float x;
int i;

x = 5.0 + 2.0 * 12.0; /* Wert 29 */


i = 9 / 4; /* Wert 2 */
i = 9 % 4; /* Wert 1 */

Programmieren in C 185
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Anmerkungen

1. Typanpassungen findet evtl. vor Anwendung der Operation statt


(später)
2. Under-/Overflow bei Operationen mit signed Argumenten:
Ergebnis nicht definiert (Fehler, Modulo-Arithmetik usw. möglich)
3. Under-/Overflow bei Operationen mit unsigned Argumenten:
Modulo-Arithmetik

Beispiel: Beispiel:
#include <limits.h>
ui 1111 = 15
int i, j; uj +1111 = 15
unsigned int ui, uj; --------------
1110 = 30%16 = 14
i = j = INT_MAX;
ui = uj = UNINT_MAX;
i = i + j; /* nicht definiert */
ui = ui + uj; /* definiert */

Programmieren in C 186
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zeigerarithmetik

Mit Zeigerwerten ist ebenfalls (eingeschränkt) Addition und Subtraktion


möglich. Gerechnet wird nicht in Bytes, sondern in Einheiten des Basistyps,
d.h. ptr+1 entspricht dem Wert von ptr (1000) + so viele Bytes, wie der
Grundtyp (z.B. int=4) beansprucht (1004).

Einschränkungen:
1. Addition: Nur ein Operand darf Zeiger sein, der andere muss int sein.
Das Resultat ist ein Zeiger.
2. Subtraktion: Zwei Zeigeroperanden liefern ein int-Resultat, linker
Operand ein Zeiger, rechter Operand ein int liefert Zeiger-Resultat.

Beispiel:
int i, *ptr1, ptr2; /* Annahme: i liegt auf Adresse 1000,
Größe eines ints=4 Bytes */

ptr1 = &i; /* ptr1 = 1000 */


ptr2 = ptr1 + 1; /* ptr2 = 1004 */
i = ptr1 - ptr2; /* i = -1 */
ptr2 = ptr1 - 1; /* ptr2 = 996 */

Programmieren in C 187
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Relationaler und logischer Ausdruck
Bezeichnung Wert
x<y kleiner
x>y größer
x <= y kleiner gleich
x >= y größer gleich
x == y gleich
x != y ungleich
x && y logisches Und
x || y logisches Oder
!x logische Negation

• Wahrheitswerte sind vom Typ int: 0=falsch, ungleich 0=wahr


• || und && werten zweites Argument nur aus, wenn nötig!
• Entweder haben beide Operanden einen arithmetischen Typ oder
beide sind Zeiger auf Objekte kompatiblen Typs.
• Evtl. Typanpassungen der Operanden
Programmieren in C 188
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

int i,
a[2],
*ptr1, *ptr2;

i = (3 < 4) || f(10000); /* f(10000) wird nicht ausgewertet! */


j = (3 < 4) && f(10000); /* f(10000) wird ausgewertet! */

ptr1 = &a[0];
ptr2 = &a[1];
i = ptr1 < ptr2; /* liefert wahr */

Programmieren in C 189
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Bit-Ausdruck

Bezeichnung Wert
x << y Links-Shift von x um y Stellen
x >> y Rechts-Shift von x um y Stellen
x&y Bit-weises Und
x|y Bit-weises Oder
x ^y Bit-weises Xor (1, wenn beide Bits verschieden)
~x Bit-weise Negation

Für Shift-Operationen gilt:

• Rechter Operand > 0 und < Anzahl Bits des linken Operanden
• Es werden Null-Bits nachgeschoben
• Falls linker Operand signed-Typ hat und negativer Wert vorliegt, so ist
das Ergebnis undefiniert.

Programmieren in C 190
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

unsigned int i;

i = 1; /* i hat Wert 1 */
i = i << 3; /* i hat Wert 10002 = 8 */
i = i >> 2; /* i hat Wert 00102 = 2 */
i = i | 5; /* i hat Wert 01112 = 7: 00102 | 01012 = 01112 */
i = i & 3; /* i hat Wert 00112 = 3: 01112 & 00112 = 00112 */
i = i ^ 5; /* i hat Wert 01102 = 6: 00112 ^ 01012 = 01102 */
i = i & ~i; /* i hat Wert 00002 = 0: 01102 &~01102 = 01102 & 10012 = 00002 */

Programmieren in C 191
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Kurzformen von Zuweisungen

Bezeichnung Kurzform für


x += y x=x+y
x -= y x=x-y
x *= y x=x*y
x /= y x=x/y
x %= y x=x%y
x <<= y x = x << y
x >>= y x = x >> y
x &= y x=x&y
x |= y x=x|y
x ^= y x = x ^y

Die Kurzformen sind in C ebenfalls Operationen und keine Anweisungen!

Programmieren in C 192
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Sonstige Ausdrücke

Bezeichnung Wert
( e0 ) ? e1 : e2 Der Ausdruck e0 wird ausgewertet. Falls dieser wahr (!=0) ist, so wird e1
ausgewertet, ansonsten e2. Der Wert des Gesamtausdrucks ist der Wert
von e1 bzw. e2. Beachte: Entweder e1 oder e2 wird nur ausgewertet.
e1 , e2 Zuerst wird e1 ausgewertet, das Ergebnis ignoriert und anschließend e2
ausgewertet. Der Typ und das Ergebnis des Gesamtausdrucks ist der
Typ bzw. das Ergebnis von e2.

Beispiel:
int i, i, max;

k = i = (j = 1) + 1 , 2*j; /* i=2, j=1, k=2 */


max = (i > j) ? i : j; /* max = (2 > 1) ? 2 : 1 = 2 */

Programmieren in C 193
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Konstante Ausdrücke

An manchen Stellen sind konstante Ausdrücke erforderlich:


1. Testausdruck in #if-Präprozessordirektiven
2. Dimensionsangaben bei Feldern
3. case-Label in switch-Anweisungen
4. Länge von Bit-Feldern
5. Explizite Werte für Aufzählungselemente
6. Initialisierungswerte für static- und extern-Variablen

Bildung von konstanten Ausdrücken:


1. Konstanten eines integralen Typs
2. Klammern ()
3. Unäre Operatoren: + - ~ ! sizeof
4. Binäre Operatoren: + - * / % << >> == != < <= > >= & ^ | && ||
5. Ternäre Operatoren: ?:

Der sizeof-Operator kann nicht in Präprozessor-Direktiven verwendet werden.

Programmieren in C 194
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Priorität der Operatoren

Operator Assoziativität
f() a[] -> . links -> rechts
x++ x-- links -> rechts
++x --x ~ ! +x -x * & (type) sizeof rechts -> links
* / % links -> rechts
+ - links -> rechts
<< >> links -> rechts
< <= > >= links -> rechts
== != links -> rechts
& links -> rechts
^ links -> rechts
| links -> rechts
&& links -> rechts
|| links -> rechts
?: rechts -> links
= += -= *= /= %= &= ^= |= <<= >>= rechts -> links
, links -> rechts
Programmieren in C 195
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Reihenfolge der Auswertung

Die Reihenfolge der Auswertung eines Ausdrucks ist in C nur in wenigen


Fällen vorgegeben:

1. f(args)
2. && und ||
3. ?:
4. ,

Ansonsten ist der Compiler frei, in welcher Reihenfolge er Teilausdrücke


auswertet.

Beispiel:
int a,b,c,d;

a = (a + b) + (c + d);
/* Trotz Klammerung dürfte der C-Compiler dies auswerten als:
a = (a + d) + (b + c); */

Programmieren in C 196
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typumwandlungen

In manchen Fällen ist ein Umwandlung von einem Typ in einen anderen Typ
nötig, was explizit oder implizit geschehen kann:

1. Explizite Umwandlung in cast-Ausdruck.


Beispiel: i = 3 + (int) 4.0;
2. Implizite Umwandlung eines Operanden.
Beispiel: i = 3 + 4.0;
3. Implizite Umwandlung bei einer Zuweisung
Beispiel: int i = 4.0;
4. Aktuelles Argument in Funktionsaufruf wird auf den Typ des formalen
Parameters angepasst.
Beispiel:
int fun(int arg);
i = fun(4.0);
5. Ergebniswert einer Funktion wird implizit auf den Ergebnistypen
umgewandelt.
Beispiel:
int fun(int arg) { return 4.0; }

Programmieren in C 197
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Prinzipielle Möglichkeiten der Typumwandlung

↓von →nach void int float Zeiger Feld Struktur Funktion


void ok
Integer ok ok ok ok
Fließkomma ok ok ok
Zeiger ok ok ok (ok) (ok)
Feld ok
Struktur ok
Funktion ok

(ok) bedeutet implizite Umwandlung.


Beispiel: Der Wert eines Feldnamens in einem Ausdruck ist der Zeiger auf
das erste Element des Feldes.

Bei Zeigertypen muss zwischen Zeigertypen auf Objekte und Zeigertypen auf
Funktionen unterschieden werden. Ein Objektzeigertyp und ein
Funktionszeigertyp sind nicht in den anderen Typ umwandelbar.
Programmieren in C 198
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Umwandlung nach void

Jeder Wert kann in den Typ void umgewandelt werden. Der Effekt ist, dass
damit der Wert verschwindet. Eine sinnvolle Anwendung ist der Fall, wo man
explizit darauf hinweisen möchte, dass man den Wert ignorieren möchte.

Beispiel:

int printf(char *format, ...);

/* Die printf-Funktion liefert als Ergebnis die Anzahl der


ausgegebenen Zeichen. */
(void) printf("hallo\n");

Programmieren in C 199
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Umwandlung nach Integertyp

1. Von int-Typ:
Falls der Wert im neuen Typ darstellbar ist, dann umwandeln.
Ansonsten, falls der Ergebnistyp ein unsigned int ist, so schneide
obersten Bits ab (Modulo-Bildung). Ansonsten ist das Ergebnis
undefiniert.

2. Von Fließkommatyp:
Der ganzzahlige Teil des Wertes wird umgewandelt. Falls dieser Wert
nicht in dem int-Typ darstellbar ist, ist die Umwandlung undefiniert.

3. Von Zeigertyp:
Der Zeigerwert wird als unsigned int mit der gleichen Größe wie die des
Zeigers angesehen. Darauf werden obige Regeln angewendet.

Programmieren in C 200
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

unsigned int ui;


int i;
signed int si;
int *ptr;

i = 5l; /* (long int)5 ist in int darstellbar, also 5 */


ui = ULONG_MAX; /* ULONG_MAX ist evtl. nicht in unsigned int darstellbar.
Obersten Bits werden dann abgeschnitten. */

si = ULONG_MAX; /* undefiniert */

i = 3.5; /* 3.5 wird nach (int)3 umgewandelt */

ptr = &i; /* z.B. Adresse 1000 */


i = (int)ptr; /* i erhält den Wert (int)1000 */

Programmieren in C 201
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Umwandlung nach Fließkommatyp

1. Von int-Typ:
Eine entsprechende Näherung wird als Wert genommen.

2. Von Fließkommatyp:
Von float nach double:
Ist möglich.
Von double nach float:
Falls der Wert in float darstellbar ist, wird dieser Wert genommen
(gerundet oder abgeschnitten ist impl.abhängig). Ansonsten undefiniert.

Beispiel:
float f;
double d;

d = 5; /* Umwandlung nach 5.0 */


d = 5.0f; /* Umwandlung nach (double)5.0 */
f = 5.0; /* Umwandlung nach (float)5.0 */

Programmieren in C 202
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Umwandlung nach Zeigertyp

1. Von Zeigertyp:
Zeigerwert bleibt erhalten im neuen Zeigertyp.

2. Von Integertyp:
Integerwert wird als Adresse interpretiert.
Nicht portabel! Ausnahme: 0 (NULL).

3. Von Feldtyp:
Der Feldname wird als Adresse des ersten Elementes genommen
(Ausnahme: sizeof)
Beispiel:
float f, *fptr;
int i, *iptr, ifeld[10]

fptr = &f; /* z.B. Adresse 1000 */


iptr = (int *)fptr; /* iptr bekommt 1000 zugewiesen */
i = 0x0005; /* An Adresse 5 liegt z.B. ein HW Control-Port */
iptr = (int *)i; /* *iptr würde den Inhalt der Adresse 5 ergeben. */
iptr = ifeld; /* iptr hat &ifeld[0] */

Programmieren in C 203
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
"Übliche" Umwandlungen

Bis jetzt haben wir nur prinzipiell besprochen, welche Umwandlungen möglich
sind und was für Auswirkungen diese Umwandlungen haben.

Wann finden die Umwandlungen statt?

1. Explizite Umwandlung (cast)


2. Umwandlung in Zuweisungen
3. Umwandlung in Ausdrücken

Programmieren in C 204
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Umwandlungen in Casts

Über Casts lässt sich explizit eine Typumwandlung erzwingen. Die


erlaubten Umwandlungen in Casts sind:

Cast-Typ Ursprungstyp
Beliebiger arithmetischer Typ Beliebiger arithmetischer Typ
beliebiger int-Typ beliebiger Zeigertyp
void *, Zeiger auf T beliebiger int-Typ, void *, beliebiger Zeigertyp
void beliebig

Programmieren in C 205
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Umwandlungen in Zuweisungen

Stimmt der Typ auf der linken Seite der Zuweisung nicht mit dem Typ auf der
rechten Seite überein, wir der Wert rechts auf den Typ der linken Seite
angepasst.

Erlaubte ist dabei:

Linke Seite Rechte Seite


Beliebiger arithmetischer Typ Beliebiger arithmetischer Typ
Struktur-/Union-Typ kompatibler Struktur-/Union-Typ
void * 0, void *, Zeiger
Zeiger auf Typ T1 0, void *, Zeiger auf T2
(wobei T1 und T2 kompatibel sein müssen)

Programmieren in C 206
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

int i, *ptr_i, feld_i[2];


double d;
short int si;
void *ptr_v;

i = 3.0; /* implizite Umwandlung (double)3.0 -> (int)3 */


d = 3; /* implizite Umwandlung (int)3 -> (double)3.0 */
si = 3; /* implizite Umwandlung (int)3 -> (short)3 */

ptr_i = 0; /* 0 kann man Variablen beliebigen Zeigertyps zuweisen */


ptr_v = ptr_i; /* möglich, weil ptr_v Typ void * hat */
ptr_i = ptr_v; /* möglich, weil ptr_v Typ void * hat */

ptr_i = feld_i; /* Adresse des ersten Elementes */

/* Falsch: */
const int *ptr_ci;
int *ptr_i;

ptr_i = ptr_ci; /* Falsch: Nicht-kompatible Typen, keiner davon void * */

Programmieren in C 207
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Unäre (einstellige) Umwandlungen

Die unären Umwandlungen werden angewandt auf Operanden folgender


Operationen:

1. Funktionsargumente zu Funktionen, zu denen kein Funktionsprototyp


bekannt ist
2. Unäre Operatoren: ! - ~ *
3. Binäre Operatoren: << >>

Programmieren in C 208
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Unäre (einstellige) Umwandlungen

Integertypen ist ein Rang zugeordnet, der in den Umwandlungsregeln


eine Rolle spielt.

Rang Typen
60 long long int, unsigned long long int
50 long int, unsigned long int
40 int, unsigned int
30 short, unsigned short
20 char, unsigned char, signed char
10 _Bool

Die extended Integertypen müssen implementierungsabhängig in dieser


Tabelle eingeordnet sein.

Beispiel:
In einer Implementierung könnte int64_t den Rang 60, in einer anderen
Implementierung den Rang 55 haben.
Programmieren in C 209
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Unäre (einstellige) Umwandlungen

Die angewandte unäre Umwandlung ist die erste Regel der folgenden
Tabelle, die anwendbar ist.

Operandentyp Zieltyp
Fließkommatyp keine Umwandlung
Feld von T Zeiger auf T
Funktion, die T liefert Zeiger auf Funktion, die T liefert
int-Typ mit Rang >= int keine Umwandlung
signed int Typ mit Rang < int int
unsigned int Typ mit Rang < int und int
alle Werte dieses Typs sind darstellbar in int
unsigned int Typ mit Rang < int und nicht alle unsigned int
Werte dieses Typs sind darstellbar in int

Programmieren in C 210
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

int i;
char c;
short s1=1, s2=4;

c = 'a';
i = -c; /* Umwandlung nach (int)c und dann unäres Minus */
i = s1 << s2; /* Umwandlung nach (int)s1 << (int)s2 */

Programmieren in C 211
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Binäre Umwandlungen

Anwendung bei den meisten binären Operatoren und auf das 2. Und 3.
Argument des ?:-Operators.

Umwandlung durch die erste anwendbare der folgenden Regeln:


1. Ist ein Operand long double: Umwandlung nach long double
2. Ist ein Operand double: Umwandlung nach double
3. Ist ein Operand float: Umwandlung nach float
4. Anwenden der unären Umwandlungen auf beide Operanden und
anschließend höchstens eine der folgenden Regeln:
- Ist ein Operand unsigned long int: Umwandlung nach unsigned long int
- Ist ein Operand long int und der anderen unsigned int:
- Sind alle unsigned int Werte in long darstellbar, dann long
- Ansonsten unsigned long int
- Ist ein Operand long int: Umwandlung nach long int
- Ist ein Operand unsigned int: Umwandlung nach unsigned int

Programmieren in C 212
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Binäre Umwandlungen
Anwendung bei den meisten binären Operatoren und auf das 2. Und 3. Argument
des ?:-Operators. Zuerst wird auf beiden Operanden getrennt eine unäre
Umwandlung durchgeführt. Dann wird die erste der folgenden Regeln angewandt:

Ein Operandentyp Andere Operandentyp Umwandlung nach

long double Reeller Typ long double


double reeller Typ double
float reeller Typ float
unsigned Typ unsigned Typ unsigned Typ mit höherem Rang
signed Typ signed Typ signed Typ mit höherem Rang
unsigned Typ signed Typ mit <= Rang unsigned Typ
unsigned Typ signed Typ mit > Rang, kann alle signed Typ
Werte des unsigned darstellen
unsigned Typ signed Typ mit > Rang, kann nicht unsigned Version von signed Typ
alle Werte des unsigned darstellen
beliebiger Typ beliebiger Typ keine Umwandlung
Programmieren in C 213
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

/* unäre Umwandlungen */
/* binäre Umwandlungen */

int i;
short s = 1;
long int il;
unsigned long int iul;
float f;

f = 1.0 + 1.0ld; /* f = (float) ( (long double)1.0 + 1.0ld ) */


f = 1 + 1.0f; /* f = (float)1 + 1.0f */

i = s + 1; /* i = (int)s + 1 */
i = s + 1u; /* i = (int) ( (unsigned int)(int)s + 1u ) */

Programmieren in C 214
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 215
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Ein/Ausgabe

Dateien sind eine Folge von Bytes ohne jegliche Struktur. Lediglich
Textdateien (s.u.) haben eine Zeilenstruktur dadurch, dass dem Zeichen '\n'
eine besondere Bedeutung zukommt.

Um mit einer Datei zu arbeiten braucht man einen Dateibeschreiber vom


Typ FILE *, der in <stdio.h> definiert ist. Solch einen Beschreiber bekommt
man beim Öffnen einer Datei.

Es gibt drei bereits vorhandene und geöffnete Dateien (<stdio.h>):


1. stdin Standardeingabe
2. stdout Standardausgabe
3. stderr Standardfehlerausgabe

Eine Datei kann in einer von zwei Modi geöffnet werden:


1. Formatierte Ein-/Ausgabe
2. Unformatierte Ein-/Ausgabe

Programmieren in C 216
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Öffnen einer Datei

FILE *fopen(const char *filename, const char *mode);

filename ist der Name der zu öffnenden Datei in der Notation des jeweiligen
Systems (Unix: "/tmp/datei.dat", Windows: "C:\\tmp\\datei.dat").

mode ist der Öffnungsmodus mit einer Kombination der Buchstaben (s.u.):
r Von Datei nur lesen
w Auf Datei nur schreiben
a Auf Datei nur schreibend anfügen
b Binärdatei (Default: Textdatei)
+ Sowohl schreiben als auch lesen möglich (erste Operation entscheidet)

fopen liefert einen Dateibeschreiber vom Typ FILE * zurück. Falls ein Fehler
aufgetreten ist, ist dies NULL.

Programmieren in C 217
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Öffnungsmodus

"r" Existierende Textdatei zum Lesen öffnen.


"w" Textdatei zum Schreiben öffnen. Falls Datei existiert, wird Inhalt gelöscht.
"a" Textdatei zum Anfügen öffnen. Falls keine Datei existiert, erzeugen.
"rb" Binärdatei; s.o.
"wb" Binärdatei; s.o.
"ab" Binärdatei; s.o.
"r+" Existierende Textdatei zum Lesen oder Schreiben öffnen.
"w+" Textdatei zum Lesen oder Schreiben öffnen. Falls Datei existiert, wird Inhalt
gelöscht.
"a+" Textdatei zum Lesen oder Anfügen öffnen.
"rb+" Binärdatei; s.o.
"wb+" Binärdatei; s.o.
"ab+" Binärdatei; s.o.

Programmieren in C 218
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Öffnungsmodus

Eigenschaft Modus

r w a r+ w+ a+

Datei muss existieren? ja nein nein ja nein nein


Inhalt einer existierenden Datei verloren? nein ja nein nein ja nein

Lesen erlaubt? ja nein nein ja ja ja

Schreiben erlaubt? nein ja ja ja ja ja

Schreiben beginnt am Ende nein nein ja nein nein ja

Programmieren in C 219
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Weitere Dateioperationen

int fclose(FILE *f);


Schließt die Datei. Liefert 0, falls kein Fehler auftrat.

int fflush(FILE *f);


Synchronisiert interne Puffer mit der Datei.

int remove(const char *filename);


Löscht die Datei mit dem Namen filename. Liefert 0, wenn ok.

int rename(const char *old, const char *new);


Benennt Datei old in new um. Liefert 0, wenn ok.

FILE *tmpfile(void);
Erzeugt temporäre Datei im Modus "wb+". Datei wird automatisch gelöscht,
wenn Datei geschlossen oder Programm beendet wird. Liefert 0, wenn nicht ok.

char *tmpname(char *s);


Erzeugt nicht-existierenden Dateinamen.

Programmieren in C 220
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

#include <stdio.h>

int main(int argc, char **argv)


{
FILE *f;

/* Öffnen der Datei */


f = fopen(argv[1], "w");
if(f == NULL) exit(EXIT_FAILURE);

/* Schreiben */
fprintf(f, "Hallo\n");
printf("Hallo");

/* Rausschreiben des Schreibpuffers für stdout */


fflush(stdout);

/* Datei schließen */
if( fclose(f) != 0) exit(EXIT_FAILURE);
}

Programmieren in C 221
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Formatierte Ausgabe

int fprintf(FILE *f, char *format, ...);


int printf(char *format, ...);
int sprintf(char *str, char *format, ...);

Schreibt eine Anzahl von Werten auf die Datei f bzw. stdout bzw. str. Die
Anzahl wird implizit durch den Formatstring vorgegeben. Liefert Anzahl
geschriebener Zeichen, falls ok, ansonsten einen negativen Wert.

Der Formatstring wird zeichenweise ausgegeben, bis ein % erscheint, was


eine Formatangabe einleitet. Für jede Formatangabe wird eine Werteangabe
in ... benötigt.

Eine Formatangabe besteht aus % gefolgt von optionalen Zusatzangaben


Flags
Minimale Feldweite
Präzision
Zusatzangaben zu short/long
gefolgt von genau einem Formatbuchstaben.
Programmieren in C 222
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Formatbuchstaben

Buchstabe Argumenttyp Ausgabe


d, i int Dezimalzahl
x,X unsigned int Hexadezimalzahl
o unsigned int Oktalzahl
u unsigned int Dezimalzahl (ohne Vorzeichen)
c int Zeichen
s char * String
f double Reelle Zahl im Format [-]m.dddddd
e,E double Reelle Zahl im Format [-]m.dddddde[+|-]dd
g,G double Wie %e, falls Exponent klein, sonst %f.
a, A double Reelle Zahl in hexadezimaler Notation
p void * Zeigerwert als (impl.abhängige) Dezimalzahl
% - %-Zeichen
n int * Keine Ausgabe! Nächstem Argument wird Anzahl
der bis jetzt ausgegebenen Zeichen zugewiesen.
Programmieren in C 223
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

#include <stdio.h>

int main(int argc, char **argv)


{
int i;

printf("%d %x %c %s\n", 4711, 17u, 'a', "hallo");

printf("%f %p %n\n", 3.14, &i, &i);


}

Ausgabe:
4711 11 a hallo
3.140000 0xbffffa64

Programmieren in C 224
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Optionale Zusatzangaben

1) Flags (Auswahl):
- linksbündige Ausgabe
+ Zahlendarstellung beginnt immer mit Vorzeichen
Leerzeichen Zahlendarstellung beginnt mit Leer- oder Minuszeichen
0 0 statt Leerzeichen als Füllzeichen

2) Minimale Feldweite in Form einer Dezimalzahl


Mit Füllzeichen bis zur Feldweite auffüllen

3) Präzision in der Form .Dezimalzahl


Bei Fließkommaformaten: Nachkommastellen
Bei int: minimale Anzahl von Ziffern
Bei char *: maximale Feldweite

4) Typangaben (Auswahl):
ll long long bei int-Formaten erwartet
h/l short bzw. long Argument werden bei int-Formaten erwartet
L long double Argument bei Fließkomma-Formaten erwartet
Programmieren in C 225
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

#include <stdio.h>

int main(int argc, char **argv)


{
printf("%5.3f\n", 3.141592654);
printf("%10d\n", 1);
printf("%-10d\n", 1);
}

Ausgabe:
3.141
1
1

Programmieren in C 226
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Formatierte Eingabe

int fscanf(FILE *f, char *format, ...);


int scanf(char *format, ...);
Int sscanf(char *str, char *format, ...);

Liest eine Anzahl von Werten von der Datei f bzw. stdout bzw. str. Die Anzahl
wird implizit durch den Formatstring vorgegeben. Liefert Anzahl gelesener
Werte (nicht Zeichen), falls ok, ansonsten EOF.

Der Formatstring wird zeichenweise durchgearbeitet. Leerzeichen werden im


Format wie in der Eingabe ignoriert. Zeichen in der Formatangabe ungleich %
müssen auch in der Eingabe entsprechende Zeichen haben. Ein %-Zeichen
leitet eine Formatangabe ein. Für jede Formatangabe wird ein entsprechender
Zeiger (!) in ... benötigt.

Eine Formatangabe besteht aus % gefolgt von optionalen Zusatzangaben


Maximale Feldweite (Integer-Zahl)
Zusatzangaben wie z.B. short/long (h,l,L)
gefolgt von genau einem Formatbuchstaben.

Programmieren in C 227
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Formatbuchstaben

Buchstabe Argumenttyp Eingabe


d int * Dezimalzahl
i int * Dezimal-, Oktal oder Hexadezimalzahl
o int * Oktalzahl
x,X int * Hexadezimalzahl
u unsigned int * Dezimalzahl ohne Vorzeichen
c char * Ein Zeichen (vorangehende Leerzeichen ignorieren)
s char * String (vorangehende Leerzeichen ignorieren). String ist
bei Leerzeichen beendet.
e,f,g float * Fließkommazahl (Vorsicht:: float * und nicht double *)
% - % selber
p void ** Implementierungsabhängige Zeigerdarstellung
[ char * Angabe einer Menge von erlaubten Eingabezeichen

Programmieren in C 228
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel 1
#include <stdio,h>

int main(int argc ,char **argv)


{
int i;
float f;
char str[10];

scanf("%d %f", &i, &f);


printf("%d %f\n", i, f);

scanf("%s", str);
printf("%s\n", str);
}

Eingabe:
3 -3.14
abc def

Ausgabe:
3 -3.14
abc

Programmieren in C 229
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel 2

#include <stdio,h>

int main(int argc ,char **argv)


{
char str[10];

// Lese max. 9 Ziffern ein, '\0' wird angehängt


scanf("%9[0123456789]", str);

// Lese alles außer Ziffern ein, max 9 Zeichen, '\0' wird angehängt
// ^ bedeutet Negation
scanf("%9[^0123456789]", str);
}

Programmieren in C 230
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Weitere Funktionen

int fgetc(FILE *f);


int fputc(int c, FILE *f);
Liest bzw. schreibt nächstes Zeichen (EOF, wenn Fehler).

char *fgets(char *s, int n, FILE *stream);


Höchstens n-1 Zeichen werden aus einer Zeile eingelesen und mit
\0 abgeschlossen

char *fputs(const char *s, FILE *f);


Schreibt s auf Datei.

Programmieren in C 231
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Unformatierte Ein-Ausgabe

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *f);


Liest nmemb Elemente jeweils der Größe size aus der Datei
und speichert diese ab ptr im Speicher.

size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *f);


Schreibt nmemb Elemente jeweils der Größe size aus dem
Speicher ab ptr auf die Datei.

int fseek(FILE *f, long int offset, int whence);


Positioniert den internen Dateizeiger auf die Position, die offset
addiert mit whence ergibt, wobei whence sein kann:
SEEK_SET: Dateianfang
SEEK_CUR: aktuelle Position in Datei
SEEK_END: Dateiende

void rewind(FILE *f);


Setzt den Dateizeiger auf den Anfang der Datei.

Programmieren in C 232
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

/* ohne Fehlerüberprüfungen! */
#include <stdio.h>

int main(int argc, char **argv)


{
FILE *f;
int ifeld[10] = {,0,1,2,3,4,5,6,7,8,9};

/* Schreiben des gesamten Feldes auf Datei */


f = fopen("test.dat", "wb");
fwrite(ifeld, sizeof(ifeld[0]), 10, f);
fclose(f);

/* Lesen des gesamten Feldes von Datei */


f = fopen("test.dat", "rb");
fread(ifeld, sizeof(ifeld[0]), 10, f);
fclose(f);
}

Programmieren in C 233
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 234
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Standardbibliotheken

In der Bibliothek libc.a (automatisch eingebunden) und libm.a sind alle ANSI-C
Funktionen enthalten. Wenn man eine Funktion nutzen will, sollte man die
entsprechende Include-Datei xyz.h einbinden.

Hier werden nur die wichtigsten Funktionen besprochen.

Online-Dokumentation über "man abc" für Funktion abc.

Programmieren in C 235
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Headerdateien (1)

Headername Funktion
assert.h Zusicherungen
complex.h Komplexe Zahlen
ctype.h Zeichenklassen (islower, isupper etc.)
errno.h letzte Fehlernummer (in errno)
fenv.h low-level Kontrolle über Fließkommaverhalten (z.B. Rundung)
float.h Angaben zu Fließkommawerten (z.B. FLT_MAX)
inttypes.h Ausgabeformatierung von extended int-Typen
iso646.h Makros für einige Operatoren (z.B. not_eq für !=)
limits.h Angaben zu int-artigen Werten (z.B. INT_MAX)
locale.h Lokalisierung (z.B. Währungszeichen)
math.h Mathematikfunktionen (z.B. sin, cos)
setjmp.h Nichtlokale Sprünge

Programmieren in C 236
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Headerdateien (2)

Headername Funktion
signal.h Signale
stdarg.h Variable Argumentlisten
stdbool.h Boolsche Werte
stddef.h Grundlegende Typen und Konstanten (z.B. NULL, size_t)
stdint.h extended int Typen
stdio.h Ein-/Ausgabe
stdlib.h Nützliche Funktionen (z.B. malloc, qsort, system, getenv)
string.h String-Funktionen
tgmath.h Typ-generische mathematische Makros (sin für float, double,...)
time.h Zeit- und Datumsfunktionen
wchar.h Wide und Multibyte Character
wctype.h Wide und Multibyte Character

Programmieren in C 237
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zusicherungen mit <assert.h>

Beispiel:

#include <assert.h>
#include <stdlib.h>

/* #define NDEBUG */

int fun(int arg)


{
// Diese Funktion ist nur für Argumente mit 0 < arg < 5000 spezifiziert
assert((arg > 0) && (arg < 5000));
return 2*arg;
}

int main(int argc, char **argv)


{
int i, *ip;

ip = (int *) malloc(sizeof(*ip));
assert(ip != NULL); // Das würde man besser mit if() abprüfen

i = fun(4711);
assert(i > 0);
}
Programmieren in C 238
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Fehlersuche mit <assert.h>

cc -DNDEBUG prog.c übersetzt Programm ohne die Überprüfung.


cc prog.c übersetzt das Programm mit Überprüfung

Verwenden Sie assertions insbesondere bei umfangreichen Programmen:

1. Überprüfen, ob Funktionsargumente wirklich sinnvolle Werte haben


2. Überprüfen, ob Funktionsresultate wirklich sinnvolle Werte haben

Übersetzen Sie die Produktionsversion ihres Programms mit -DNDEBUG


und die Testversion ohne diese Option.

Programmieren in C 239
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Signale <signal.h>

Signale dienen zur asynchronen Programmausführung, z.B. im Fehlerfall. C


kennt mindestens die folgenden Signale:

Signalname Bedeutung

SIGABRT Fehlerhafte Programmbeendung (z.B. über abort())


SIGFPE Fließkommafehler (z.B. Division durch 0)
SIGILL Illegale Machineninstruktion
SIGINT Ctl-C gedrückt
SIGSEGV Illegaler Speicherzugriff
SIGTERM Signal zur Programmbeendigung

Programmieren in C 240
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Signale <signal.h>

Zu jedem Signal kann man einen Signal-Handler (eine Funktion) angeben, die
bei Auftreten des Signals aufgerufen wird. Angabe über:
void (*signal ( int sig, void (*func)(int))) (int);
int raise(int sig);

Beispiel: Ausgabe:
#include <signal.h>
#include <assert.h> > a.out
int a, b=0, c=0; Fehler erkannt
Bis jetzt läuft alles gut.
void sig_handler(int sig) { Floating point exception (core dumped)
printf("Fehler erkannt\n");
c = 1;
}

int main(int argc, char **argv) {


(void)signal(SIGFPE,sig_handler);
a = b / c;
printf("Bis jetzt läuft alles gut.\n");
c = 0;
(void)signal(SIGFPE, SIG_DFL);
a = b / c;
}
Programmieren in C 241
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Mathematische Funktionen (1) <math.h>

Prototyp Beschreibung
double acos( double x ); Arcuscosinus (Hauptwert)
double asin( double x ); Arcussinus (Hauptwert)
double atan( double x ); Arcustangens x (Hauptwert)
double atan2( double y, double x ); Arcustangens y/x (Hauptwert)
double cos( double x ); Cosinus (in Radianten)
double sin( double x ); Sinus (in Radianten)
double tan( double x ); Tangens (in Radianten)
double cosh( double x ); Cosinus Hyperbolicus
double sinh( double x ); Sinus Hyperbolicus
double tanh( double x ); Tangens Hyperbolicus
double exp( double x ); Exponentialfunktion ex

Programmieren in C 242
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Mathematische Funktionen (2) <math.h>

Prototyp Beschreibung
double pow( double x, double y); xy
double sqrt( double x ); Wurzel x
double ceil( double x ); Kleinster ganzzahliger Wert nicht kleiner als x
double floor( double x ); größter ganzzahligert Wert nicht größer als x
double fabs( double x ); Absolutwert
double fmod( double x, double y ); Rest von x/y
double modf( double x, double *ptr); Ganzzahliger Teil von x ist Ergebnis, Nachkommateil
in *ptr
double log( double x ); loge(x)
double log10( double x ); log10(x)
double ldexp( double x, int exp ); x * 2exp
double frexp( double x, int *exp); Aufspalten von x in normalisierten Bruchteil in [0.5,1)
oder 0 und Potenz von 2 in *exp (entspricht ldexp-1)

Programmieren in C 243
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Typgenerische Mathematikmakros

• Bis C89 gab es die mathematischen Bibliotheksfunktionen nur für


double
• Ab C99 gibt es die typgenerischen Makros für Fließkommatypen in
tgmath.h, die entsprechend dem Argumenttyp eine andere Funktion
aufrufen (z.B. steht hinter dem Makro sin dann sinf, sin, sinl für ein
float, double bzw. long double Argument).

Beispiel:
#include <tgmath.h>

int main(int argc, char **argv) {


float f = 1.0f;
double d = 1.0;
long double l = 1.0L;

f = sin(f); // Hier wird sinf aufgerufen


d = sin(d); // hier wird sin aufgerufen
l = sin(l); // hier wird sinl aufgerufen
}

Programmieren in C 244
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zeichenfunktionen <ctype.h>

Prototyp Beschreibung
int isalnum( int c ); Wahr, falls c Buchstabe oder Ziffer
int isalpha( int c); Wahr, falls c Buchstabe
int iscntrl( int c); Wahr, falls c Kontrollzeichen
int isdigit( int c ); Wahr, falls c Ziffer
int isgraph( int c ); Wahr, falls c druckbares Zeichen ungleich Leerzeichen
int isprint( int c ); Wahr, falls c druckbares Zeichen
int ispunct( int c ); Wahr falls c druckbares Zeichen und kein Buchstabe, Ziffer, Leerz.
int isspace( int c ); Wahr, falls c kein Leerzeichen
int isupper( int c ); Wahr, falls c Großbuchstabe
int islower( int c ); Wahr, falls c Kleinbuchstabe
int isxdigit( int c ); Wahr, falls c Hexadezimalzeichen (0-9, a-f, A-F)
int tolower( int c ); Liefert Großbuchstaben, wenn c Kleinbuchstabe. Sonst c.
int toupper( int c ); Liefert Kleinbuchstaben, wenn c Großbuchstabe. Sonst c.

Programmieren in C 245
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Stringfunktionen <string.h>

Prototyp Beschreibung
void *memcpy(void *s1, const void *s2, size_t n); Kopiert n Zeichen von s2 nach s1
void *memmove(void *s1, const void *s2, size_t n); Kopiert n Zeichen von s2 nach s1
(Überlappung erlaubt)
int memcmp(const void *s1, const void *s2, size_t n); Vergleicht n Zeichen von s1 und s2
void *memchr(const void *s, int c, size_t n); Sucht Zeichen c ab Position s.
Liefert gefundene Position/NULL
void *memset(void *s, int c, size_t n); Füllt Speicherbereich mit c

Beispiel:
#include <string.h>

int a[1000000], b[1000000];

int main(int argc, char **argv) {


int i;
for(i = 0; i < 1000000; i++) a[i] = i;
memcpy(a, b, 100000 * sizeof(int));
}

Programmieren in C 246
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Stringfunktionen <string.h>

Prototyp Beschreibung
char *strcpy(char *s1, const char *s2); Kopiert s2 nach s1
char *strcat(char *s1, const char *s2); Fügt s2 an s1 an
int strcmp(const char *s1, const char *s2); Vergleicht s1 mit s2. Liefert -1,0,1
char *strchr(const char *s, int c); Sucht c in s und liefert Zeiger/NULL.
char *strstr(const char *s1, const char *s2); Sucht s2 in s1. Liefert Zeiger/NULL.
int strlen(const char *s); Länge des Strings (ausschließlich \0)

Beispiel:
#include <string.h>

int main(int argc, char **argv) {


char s1[10], *s2 = "hallo";

printf("Laenge=%d, <ll> kommt vor:%p \n", strlen(s2), strstr(s2, "ll"));


strcpy(s1, s2);
/* nicht möglich: strcpy(s2, s1); */
}

Programmieren in C 247
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zeit und Datum <time.h>

Es existieren 3 Datentypen zur Darstellung von Datum und Zeiten:

clock_t Skalarer Wert für CPU-Zeit

time_t Skalarer Wert für kompakte Darstellung von struct tm

struct tm Strukturierter Wert mit mindestens den folgenden Komponenten


int tm_sec; Sekunden (0-59)
int tm_min; Minuten (0-59)
int tm_hour; Stunden seit Mitternacht (0-23)
int tm_mday; Tag des Monats (1-31)
int tm_mon; Monat des Jahres (0-11)
int tm_year; Jahr seit 1900
int tm_wday; Wochentag seit Sonntag (0-6)
int tm_yday; Tag seit Jahresanfang (0-365)
int tm_isdst; Sommerzeit-Flag (>0 falls Sommerzeit)

Programmieren in C 248
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zeit und Datum <time.h>

Prototyp Beschreibung
clock_t clock(void); CPU-Zeit in Ticks. Dividiert durch
CLOCKS_PER_SEC liefert CPU-Zeit in s.
Vorsicht: int-Division!
time_t time(time_t *timer); Ermittelt aktuelle Daten. Falls timer!=NULL, wird dort
ebenfalls abgespeichert.
double difftime(time_t t1, time_t t2); Differenz in Sekunden
time_t mktime(struct tm *timeptr); Umwandlung struct tm nach time_t
char *asctime(const struct tm *t); Umwandlung Zeit nach String (in statischen Bereich)
struct tm *localtime(const time_t *t); Umwandlung time_t nach struct tm mit lokaler
Zeitzone
struct tm *gmtime(const time_t Umwandlung time_t nach struct tm mit Zeitzone UTC
*timer);

size_t strftime(char *s, size_t Umwandlung von Daten mit mit


maxsize, const char *format, const Formatierungsangaben
struct tm *t);

Programmieren in C 249
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel
#include <time.h>
#include <math.h>

int main(int argc, char **argv) {


time_t t;
clock_t t0, t1;
int i; double a = 0.0;

/* CPU-Zeit Start und aktuelle Zeit */


t0 = clock();
t = time(NULL);

/* in lokale Zeit und nach String umwandeln */


printf("Lokale Zeit und Datum sind: %s\n", asctime(localtime(&t)));

/* CPU-Zeit verbrauchen und Gesamt-CPI-Zeit ausgeben */


for(i=0; i < 1000000; i++) a += sin(i);
printf("verbrauchte CPU-Zeit: %f s\n", (clock()-t0)/(double)CLOCKS_PER_SEC);
}

Ausgabe:
Lokale Zeit und Datum sind: Fri Nov 3 09:12:49 2000
Der Wert von a ist 0.232884
verbrauchte CPU-Zeit: 0.260000 s
Programmieren in C 250
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Speicherverwaltung <stdlib.h>

Prototyp Beschreibung
void *malloc(size_t size); Legt neuen Speicherbereich im Heap an.
void *calloc(size_t nmemb, size_t size); Legt neuen Speicherbereich (mit 0) im Heap an
void free(void *ptr); Gibt Speicherbereich wieder frei
void *realloc(void *ptr, size_t size); Vergrößert/verkleinert Speicherbereich, evtl. an
anderer Stelle im Speicher

Beispiel:

#include <stdlib.h>

int main(int argc, char **argv)


{
int *ptr_i, *ptr_j;

ptr_i = (int *)malloc(sizeof(*ptr_i));


*ptr_i = 4711;
ptr_i = (int *)realloc(ptr_i, 2 * sizeof(*ptr_i));
free(ptr_i);
}

Programmieren in C 251
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Schnittstelle zum Betriebssystem <stdlib.h>

Prototyp Beschreibung
void exit(int status); Beendet die Programmausführung
void abort(void); Erzeugt SIGABRT-Signal und beendet
Programm mit Fehlercode
int atexit(void (*func)(void)); Teilt dem System mit, dass vor dem
eigentlichen Programmende die
Funktion func aufgerufen werden soll
char *getenv(const char *name); Liefert einen String/NULL auf den Wert
der Umgebungsvariable name
int system(const char *str); Ruft den Kommandointerpreter (shell)
des Betriebssystems mit dem
Kommando str auf.

Programmieren in C 252
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel <stdlib.h>

#include <stdlib.h>

#define UVAR "PS1" /* Name einer Umgebungsvariablen */

/* Diese Funktion soll vor Programmende ausgeführt werden */


void exit_fun(void)
{
printf("Jetzt geht es langsam zu Ende\n");
}

int main(int argc, char **argv)


{
char *wert;

/* exit_fun soll vor Programmende ausgeführt werden */


atexit(exit_fun);

/* Umgebungsvariable suchen */
if( (wert = getenv(UVAR)) == NULL)
printf("Keine Umgebungsvariable %s bekannt\n", UVAR);
else
printf("Wert von %s ist %s\n", UVAR, wert);
}

Programmieren in C 253
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Sortieren <stdlib.h>

void qsort(const void *base, size_t nmemb, size_t size,


int (*compar)(const void *, const void *));

Beispiel:
#include <stdlib.h>

int mycompare(const void *arg1, const void *arg2) {


return *(int*)arg1 - *(int *)arg2;
}

int main(int argc, char **argv) {


int ifeld[10] = {9,8,7,6,5,4,3,2,1,0};

qsort((const void *)ifeld, 10, sizeof(ifeld[0]), mycompare);


}

Programmieren in C 254
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Binäres Suchen <stdlib.h>

void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));

Beispiel:
#include <stdlib.h>

int mycompare(const void *arg1, const void *arg2) {


return *(int*)arg1 - *(int *)arg2;
}

int main(int argc, char **argv) {


int ifeld[10] = {0,1,2,3,4,5,6,7,8,9};
int suchen = 2;

if(bsearch((const void *)&suchen, (const void *)ifeld, 10,


sizeof(ifeld[0]), mycompare) == NULL)
exit(EXIT_FAILURE);
}

Programmieren in C 255
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Zufallszahlen <stdlib.h>

Prototyp Beschreibung
int rand(void); Liefert eine Zufallszahl zwischen 0 und
RAND_MAX. Bei Normierung auf [0,1)
muss man mit der Integer-Division
aufpassen.
void srand(unsigned int seed); Initialisiert den Zufallszahlengenerator
mit dem Startwert seed. So kann man
wiederholt die gleiche Folge von
Zufallszahlen erzeugen.

Programmieren in C 256
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 257
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Modulare Programmierung

• Nur Bezeichner global bekannt machen, die auch außerhalb der .c-
Datei genutzt werden sollen. Dies kann man bei Objekten dadurch
erreichen, dass alle anderen (lokalen) Objektdefinition (Variablen,
Funktionen) auf dem top-level das Attribut static bekommen.

• Alle global sichtbaren Bezeichner einer Datei test.c in einer Datei


test.h mit vollem Typ und dem Zusatz extern deklarieren (nicht
definieren!). Ebenfalls Typdefinitionen aus test.c, die global bekannt
sein sollen, (nur) in test.h angeben.

• In allen Dateien, die solch ein Objekt bzw. Typ nutzen (auch in der
zugehörigen Definitionsdatei test.c) ein #include "test.h"
einfügen.

Programmieren in C 258
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel: bsp.c

/* global sichtbare Deklarationen dieser Datei


Auch hier ein include, damit der Compiler Inkonsistenzen prüfen kann.
*/
#include "bsp.h"

/* global_var ist eine extern sichtbare Variable */


int global_var = 5;

/* local_var soll nur innerhalb der Datei bekannt sein */


static int local_var = 3;

/* local_fun soll nur innerhalb der Datei bekannt sein */


static double local_fun(int y)
{
return 2.0 * local_var * global_var * y;
}

/* global_fun ist eine extern sichtbare Funktion */


double global_fun( double x )
{
return local_fun(x) + x + global_var + local_var;
}
Programmieren in C 259
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel: bsp.h

/* global sichtbare Deklarationen der Datei bsp.c */

/* global_var ist eine extern sichtbare Variable */


extern int global_var;

/* global_fun ist eine extern sichtbare Funktion */


/* Wichtig: Deklaration hier, keine Definition */
extern double global_fun( double x );

Programmieren in C 260
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel: nutzung_bsp.h

/* Nutzen der exportierten Deklarationen aus bsp.c */

#include "bsp.h"

int main(int argc, char **argv)


{
printf("Wert von global_var ist %d\n", global_var);
printf("Und global_fun(3.0) ist %f\n", global_fun(3.0));
}

Programmieren in C 261
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel: Stack

Stack.h:
typedef int * Stack;
extern Stack stack_create(int maxele);
extern void stack_push(Stack s, int x);
extern int stack_pop(Stack s);

Stack.c: Stacktest.c:
#include "Stack.h" #include "Stack.h"
/* keine Fehlerüberprüfung hier! */
int main(int argc, char **argv)
Stack stack_create(int maxele) { {
return (Stack)malloc(maxele*sizeof(*s)); Stack s;
} int ele;

void stack_push(Stack s, int x) { s = stack_create(10);


*(s++) = x; stack_push(s, 10);
} ele = stack_pop(s);

int stack_pop(Stack s) { /* hier wird es spannend */


return *(s--); stack_push(s, 20.0);
} }

Programmieren in C 262
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Makefiles

Problem:
1. Projekt besteht z.B. aus 300 .c-Dateien und 200 .h-Dateien
2. Komplexe Abhängigkeiten zwischen .c und .h-Dateien
3. Bei Änderungen an .c-Dateien möchte man nur geänderte Dateien neu
übersetzen
4. Bei Änderungen an .h-Dateien möchte man alle abhängigen Dateien neu
übersetzen

Lösung:
Makefiles sind ein Hilfsmittel, um solche (und andere) Probleme zu lösen

Programmieren in C 263
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Makefiles

Wesentliches Element: Abhängigkeitsregel

Ziel : Abhängigkeiten
Kommando

Falls eine der Abhängigkeiten (Dateien) jünger (im Sinne von modifiziert) als
das Ziel ist, so wird das Kommando ausgeführt.

Beispiel:

datei.o: datei.c datei.h xyz.h


cc -c datei.c

Wichtige Syntaxregel:
Vor dem Kommando muss ein TAB-Zeichen sein (keine Leerzeichen)!

Programmieren in C 264
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

# Makefile Aufruf:
prog: test1.o test2.o
cc -o prog test1.o test2.o
> make

test1.o: test1.c test1.h oder


cc -c -O test1.c

test2.o: test2.c test1.h > make prog


cc -c -O test2.c

/* Dies ist test1.c */ /* Dies ist test2.c */


#include "test1.h" #include "test1.h"

void fun(void) int main(int argc, char **argv)


{ {
printf("Hallo\n"); fun();
} }

/* Dies ist test1.h */


void fun(void);

Programmieren in C 265
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Variablen

# Makefile
CC = cc
CFLAGS = -O
OBJECTS = test1.o test2.o

prog: $(OBJECTS)
$(CC) -o prog $(OBJECTS)

test1.o: test1.c test1.h


$(CC) -c $(CFLAGS) test1.c

test2.o: test2.c test1.h


$(CC) -c $(CFLAGS) test2.c

clean:
-rm $(OBJECTS) prog

Packet:
tar -cf alles.tar test1.c test2.c test1.h

Programmieren in C 266
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Automatische Variablen
# Makefile
CC = cc
CFLAGS = -O
OBJECTS = test1.o test2.o

prog: $(OBJECTS)
$(CC) -o $@ $^

test1.o: test1.c test1.h


$(CC) -c $(CFLAGS) $<

test2.o: test2.c test1.h


$(CC) -c $(CFLAGS) $<

clean:
-rm $(OBJECTS) prog

Packet:
tar -cf alles.tar test1.c test2.c test1.h

$@ Ziel der Regel


$^ Namen aller Abhängigkeiten
$< Name der 1. Abhängigkeit
Programmieren in C 267
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Implizite Regeln

# Makefile
CC = cc
CFLAGS = -O
OBJECTS = test1.o test2.o

prog: $(OBJECTS)
$(CC) -o $@ $^

test1.o: test1.c test1.h


test2.o: test2.c test1.h

clean:
-rm $(OBJECTS) prog

Packet:
tar -cf alles.tar test1.c test2.c test1.h

Implizite Regel:
.o: .c
$(CC) -c $(CPPFLAGS) $(CFLAGS) $<

Weitere implizite Regeln für viele Dateiendungen.

Programmieren in C 268
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Anmerkungen

• make kann wesentlich mehr, als hier vorgestellt wurde


• make ist wesentlich flexibler, als hier vorgestellt wurde
• Nicht nur zum Übersetzen von Programmen geeignet
• Sollte man generell zu jedem Projekt anlegen
• Hierarchisch organisierte Projekte durch rekursiven Aufruf von make
mit Makefiles in Unterverzeichnissen möglich
• autoconf und automake zur Erstellung portabler Software
(Fortgeschrittene)

Programmieren in C 269
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Programmierwerkzeuge

• Umfangreiche Entwicklungsumgebungen (Microsoft, SUN, IBM, SGI, ...)


• Neben dem reinen Compiler und Linker werden Programmierwerkzeuge
angeboten:
– Projektunterstützung
– Dokumentation
– Fehlersuche
– Laufzeituntersuchung
– ...
• Hier sollen prinzipiellen Möglichkeiten gezeigt werden
• Schon behandelt: Projektunterstützung mit Makefiles

Programmieren in C 270
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Fehlersuche

• Einfache (und sehr beschränkte) Möglichkeiten:


– assert(x != 0); a = a / x;
– Signal-Handler

• Werkzeug zur Fehlersuche: Debugger

• Kommandozeilenorientierte Debugger (z.B. gdb) oder grafisches


Frontend (z.B. ddd)

• Vorgehensweise:
– Übersetzen und Linken des Quellcodes mit -g (Optimierung evtl.
ausschalten). Beispiel: cc -g meintest.c
– Starten des Debuggers mit dem Namen des ausführbaren Programms als
Argument. Beispiel: gdb a.out
– Starten des ausführbaren Programms unter der Aufsicht des Debuggers.
Beispiel: > run
– Analysieren der Fehler

Programmieren in C 271
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Debugger-Kommandos

Debugger-Kommandos können nur eingegeben werden, wenn das


Programm nicht läuft.
1. Breakpoint
Programm hält an, wenn es während des Programmlaufs an diese Stelle
gelangt. Man kann dann im Debugger weitere Kommandos ausführen
(z.B. Variableninhalte sich anschauen).
Syntax: break [Dateiname:]Zeilennummer
Beispiel: break meintest.c:75

2. Einzelschrittausführung
Nur der nächste Befehl (bezogen auf das Source-Programm) wird
ausgeführt. Unterscheidung:
next: Es werden Funktionsaufrufe nicht verfolgt.
step: Bei einem Funktionsaufruf wird in die Funktion gesprungen.

3. Anzeigen der Aufrufhierarchie mit where

4. Anzeigen des Source-Codes mit list


Syntax: list [Dateiname:]Zeilennummer
Programmieren in C 272
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Debugger-Kommandos

1. Anschauen von Variableninhalten


Mit print kann man sich Variableninhalte anschauen. Es sind auch
komplexe Angaben möglich.
Beispiel: print i
print *p
print p->daten[i]

2. Verändern eines Variableninhalts


Beispiel: set i=2
set p->daten[i]=5

Programmieren in C 273
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Programmoptimierung

Nachdem (!) man eine korrekte Programmversion hat, sollte man bei oft und
lang laufenden Programmen eine Optimierungsphase einfügen. Bei großen
Programmpaketen ist eine Analyse des Laufzeitverhaltens allerdings
schwierig.

Ein einfacher und in vielen Fällen hilfreicher Ansatz ist das Profiling, eine
statistische Methode. Während des Programmlaufs wird in bestimmten
Zeitintervallen (z.B. 10 ms) vom Laufzeitsystem überprüft, an welcher Stelle
im Programm man sich zu diesem Zeitpunkt befindet. Diese Daten werden
gesammelt. Diese Methode liefert für länger laufende Programme eine gute
Basis zur Programmoptimierung.

Vorgehensweise:
1. Übersetzen und Linken des Programm mit -pg
2. Ausführen des Programms mit repräsentativen Eingabedaten
3. Nach dem Programmlauf existiert eine Datei gmon.out
4. Analysieren des Laufzeitverhaltens mit: gprof a.out

Programmieren in C 274
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

Übersetzen und Ausführen:


cc -O -pg meintest.c
a.out
gprof a.out >meintest.daten

Analysieren der Ergebnisse:


...
flat profile:
% cumulative self self total
time seconds seconds calls ns/call ns/call name
55.17 10.68 10.68 640000 16687.50 30125.00 BerechnePunkt
44.42 19.28 8.60 187841276 45.78 45.78 c_abs_ohne_wurzel
0.36 19.35 0.07 640000 109.38 109.38 ZeichnePunkt
0.05 19.36 0.01 main
0.00 19.36 0.00 1 0.00 0.00 hole_argumente
0.00 19.36 0.00 1 0.00 0.00 init_display
...

Programmieren in C 275
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Inhalt

• Einführung
• Grundelemente der Sprache
• Präprozessor
• Anweisungen
• Funktionen
• Typen
• Deklarationen
• Ausdrücke
• Ein-/Ausgabe
• Standardbibliotheken
• Modulare Programmierung
• C++

Programmieren in C 276
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
C++

• 1980-1984: Bjarne Stroustrup (AT&T Labs): C with Classes


• 1983 Umbenennung C++
• C++ zuletzt standardisiert in ISO/IEC 14882:1998
• Dateiendung für C++:.cpp
• C++ ist sehr (!) komplex
• C++ ist eine Erweiterung von C (kleine Nichtschnittmenge),
Programmierung in C++ unterscheidet sich aber wesentlich von der
Programmierung in C
• Objektorientierte Programmierung möglich (aber nicht zwingend):
Klassen, Vererbung, Überladen
• Standard Template Library STL (Container, Iteratoren)

Programmieren in C 277
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Wesentliche Erweiterungen von C++ zu C

• Klassen: class Complex {...};


• Templates: array<int> ia(20); array <char> ic[10];
• Namespaces (zusätzliche Hierarchie für Sichtbarkeit von Namen)
• Überladen von Funktionen, Operatoren: feld1 = feld2 + feld3;
• Exceptions
• Run Time Type Information (RTTI)
• Referenztypen (call-by-value, call-by-reference)
• Statisches/dynamisches Binden von Funktionen
• Ein-/Ausgabe mit <<, >>
• Speicherverwaltung mit new, delete

Programmieren in C 278
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik
Beispiel

using namespace std; using namespace std;


class Complex { int main(...) {
public: Complex c1,
// Konstruktor c2(3.0, 4.0),
Complex(double r=0.0, double i=0.0) c3;
{re=r; im=i;}
// Destruktor c3 = c1 + c2;
~Complex(void) {} cout << c3.real()
<< c3.im();
// Überladen von Funktionen }
double real(void) { return re; }
double imag(void) { return im; }
double real(double r) { re = r; }
double imag(double i) { im = i; }

// Überladen von Operatoren


const Complex operator+ (const Complex& rhs)
{ return Complex( real()+rhs.real(),
imag()+rhs.imag());}

private:
double re, im;
};

Programmieren in C 279
Prof. Dr. Rudolf Berrendorf
Fachbereich Angewandte Informatik