Beispiel

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