Programmieren in C Eigene Datentypen

Transcription

Programmieren in C Eigene Datentypen
Programmieren in C
Eigene Datentypen per typedef und Strukturen
Prof. Dr. Nikolaus Wulff
C Datentypen
• Variablen in C sind immer von einem bestimmten
Datentyp, z. B. int, float, char, etc.
• Zusätzlich definiert die Sprache Operationen die mit
den Datentypen erlaubt sind, +,*, etc.
• Diese Typen sind eng angelegt an die Möglichkeiten
einer CPU und den primitiven mathematischen
Operationen, die damit möglich sind.
• Für fachliche Anwendungen ist es wünschenswert
eigene Datentypen zu verwenden, wie z.B. einen
Geldbetrag, ein Datum, eine Farbe, etc.
Prof. Dr. Nikolaus Wulff
Programmieren in C
2
Eigene Datentypen
• C kennt verschiedene Mechanismen, um eigene
Datentypen zu definieren.
• Diese Typen können dann mit „sprechenden
Typnamen“ versehen sein.
• Dies ermöglicht es sprachlich näher an der
Semantik des Problembereichs zu bleiben.
• Dies erleichtert die Lesbarkeit und kann auch
verwendet werden, um Programme leichter
zwischen verschiedenen Maschinenarchitekturen
zu portieren.
Prof. Dr. Nikolaus Wulff
Programmieren in C
3
Aufzählungstypen
• Das Konzept von Aufzählungen (eng.
Enumeration) kommt derart häufig vor, das C
hierfür eine eigene Anweisung besitzt, um einen
Aufzählungstypen für Konstanten zu definieren.
enum <identifier> { value1,...,valueN};
• Statt vieler Konstanten per #define wird die enum
Anweisung verwendet, um einen ganzen Satz von
Konstanten zu definieren.
Prof. Dr. Nikolaus Wulff
Programmieren in C
4
Boolean per enum
• C kennt keinen Booleschen Datentyp, daher werden
die Variablen TRUE und FALSE häufig per #define
Anweisung definiert:
#define FALSE 0
#define TRUE 1
• Sauberer geht es per enum:
enum Boolean { false, true};
• Die Voreinstellung ist, dass der erste Bezeichner
(hier false) mit 0 belegt wird und dann wird jeweils
um eins inkrementiert.
Prof. Dr. Nikolaus Wulff
Programmieren in C
5
Wochentage
• Wir wollen ein Programm Kalender zur
Bearbeitung von Wochentagen entwickeln.
• Die Header Datei Kalender.h könnte so aus sehen:
#define
#define
#define
#define
#define
#define
#define
MONDAY
1
TUESDAY
2
WEDNESDAY 3
THURSDAY
4
FRIDAY
5
SATURDAY 6
SUNDAY
7
void printDay(int day);
Prof. Dr. Nikolaus Wulff
Programmieren in C
6
Aufzählungstypen
• Statt vieler Konstanten per #define wird die enum
Anweisung verwendet.
enum Day { monday=1, tuesday, wednesday,
thursday, friday, saturday,
sunday};
void printDay(enum Day day);
• Damit die Zählung nicht mit monday bei 0 beginnt,
kann explizit der erste Wert belegt werden.
• Der enum Typ „Day“ kann sogar als Parametertyp
in der Methode printDay deklariert werden.
Prof. Dr. Nikolaus Wulff
Programmieren in C
7
Verwendung von enum
• Die Methode printDay kann nun mit den Konstanten
des Aufzählungstypen Day gerufen werden:
printDay(tuesday);
• aber auch ein Aufruf mit int funktioniert:
printDay(2);
• Intern wird enum immer als eine Konstante vom Typ
int repräsentiert.
• Leider kann auch Unsinn übergeben werden:
printDay(-20);
• Merke: Wir kümmern uns um die Namen, der
Compiler um die Werte...
Prof. Dr. Nikolaus Wulff
Programmieren in C
8
Enum sind intern Integers
void printDay(enum Day day) {
switch(day) {
case monday:
case tuesday:
case wednesday:
case thursday:
case friday:
printf("Go to work\n");
break;
case saturday:
case sunday:
printf("Sleep well it's weekend\n");
break;
default:
printf("Unknown day %d\n", day);
handleError();
};
}
• Für solche Fälle muss eine Möglichkeit der
Fehlerbehandlung vorgesehen werden...
Prof. Dr. Nikolaus Wulff
Programmieren in C
9
Enum Konstanten vorbelegen
• Die enum Konstanten können beliebig vergeben
werden auch mit negativen Werten:
enum example { a, b, c, d, e=-2, f, g, h
i=-3, j, k, l, m, ...};
• Es lassen sich mehrere Bezeichner mit dem selben
Wert belegen:
Wert Name
-3
-2
-1
0
1
2
3
Prof. Dr. Nikolaus Wulff
Programmieren in C
e
f
a
b
c
d
i
j
k
g l
h m
10
Alias per typdef
• Während enums immer Konstanten vom Typ int
sind gibt es mit dem typedef Konstrukt eine
Möglichkeit ein Alias für einen schon existierenden
beliebigen Typ zu vergeben:
typedef <type> <new_type_identifier>;
• So kann z.B. das Konstrukt char* mit einer
Zeichenkette als String identifiziert werden:
typedef char* String;
String s = "Hallo";
• s ist immer noch eine Variable vom Typ char*!
Prof. Dr. Nikolaus Wulff
Programmieren in C
11
Anwendungen von typedef
• Ein paar Beispiele zur Anwendung von typedef:
typedef float Geld;
typedef unsigned short
Alter;
Alter volljaehrig = 18;
Geld gehalt = 4711.00;
• Die Bedeutung des Codes erschließt sich mit den
Typen Alter und Geld wesentlich leichter, als wenn
dort mit unsigned short gearbeitet worden wäre.
• Sollte die Genauigkeit von float nicht ausreichen,
genügt es den typedef auf double zu ändern und an
allen Stellen, wo Geld vorkommt wird mit double
gerechnet ...
Prof. Dr. Nikolaus Wulff
Programmieren in C
12
typedef und enum
• Etwas unschön beim Day Beispiel war es überall in
den Methodensignaturen enum mit einzufügen:
enum Day { monday=1, tuesday, ...};
void printDay(enum Day day);
• Wesentlich eleganter geht es mit typedef :
enum day_enum { monday=1, tuesday,...};
typedef enum day_enum Day;
void printDay(Day day);
• oder in einem Rutsch:
typedef enum { monday=1, tuesday,...} Day;
Prof. Dr. Nikolaus Wulff
Programmieren in C
13
Ein altes Beispiel ...
/** two constants for f2c and c2f calculus */
#define SHIFT 32.0
#define SCALE (9.0/5.0)
/** calulate celsius as fct of fahrenheit
double f2c(double f) {
double c = (f - SHIFT)/SCALE;
return c;
}
/** calulate fahrenheit as fct of celsius
double c2f(double c) {
double f = c*SCALE + SHIFT;
return f;
}
*/
*/
• Da müsste sich doch mit typedef etwas machen
lassen ...
Prof. Dr. Nikolaus Wulff
Programmieren in C
14
Nochmal Celsius zu Fahrenheit
/** two constants for f2c and c2f calculus */
#define SHIFT 32.0
#define SCALE (9.0/5.0)
typedef double
typedef double
Celsius;
Fahrenheit;
/** calulate celsius as fct of fahrenheit
Celsius f2c(Fahrenheit f) {
Celsius c = (f - SHIFT)/SCALE;
return c;
}
/** calulate fahrenheit as fct of celsius
Fahrenheit c2f(Celsius c) {
Fahrenheit f = c*SCALE + SHIFT;
return f;
}
*/
*/
• So wird der Code sprechend ...
Prof. Dr. Nikolaus Wulff
Programmieren in C
15
C Datentypen
• Die bis lang vorgestellten Konstrukte, enum und
typedef, erfüllen noch nicht die Anforderung an
selbst definierten Datentypen.
• enum definiert Aufzählungen von int Konstanten.
• typedefs sind lediglich alias Bezeichner für bereits
existierende Datentypen.
• Keines der beiden Konstrukte liefert wirklich einen
neuen Datentyp.
Prof. Dr. Nikolaus Wulff
Programmieren in C
16
Do it yourself...
• Wir möchten den Datentyp Farbe (Color)
implementieren.
• Color soll die drei Werte (r,g,b) als Triple, jeweils
im Bereich 0<x<255 repräsentieren. = >(28,28,28)
• Welch Möglichkeiten kennen wir bis lang?
– Farbwerte in einer 4Byte Ganzzahl abspeichern
– Farbwerte in einem short/char Array abspeichern
Prof. Dr. Nikolaus Wulff
Programmieren in C
17
Farbe als Ganzzahl modelliert
• Ein int mit 4 Byte (ansonsten typedef mit long)
bietet genug Platz, um 3 Byte rgb-Werte abzulegen,
mit entsprechenden Bit und Shift Operationen...
typedef int Color;
#define R_MASK 0x00FF0000
#define G_MASK 0x0000FF00
#define B_MASK 0x000000FF
#define getR(C) ( ((C) & R_MASK)>>16
#define getG(C) ( ((C) & G_MASK)>>8
#define getB(C) ( ((C) & B_MASK)
Bits:
)
)
)
0| 1| ... |7|8| ... |15|16| ... |23|24| ... |32
• Die get-Methoden lesen mit entsprechenden
Masken und shift Operationen die rgb-Werte.
Prof. Dr. Nikolaus Wulff
Programmieren in C
18
Farbe als Ganzzahl setzen
• Ein Farb-Byte gezielt innerhalb des 4 Byte Color int
zu setzten erfordert schon mehr Operationen:
#define
((C)
#define
((C)
#define
((C)
setR(C,x) ( (C) = \
& ~R_MASK)|(((x)<<16)& R_MASK))
setG(C,x) ( (C) = \
& ~G_MASK)|(((x)<< 8)& G_MASK))
setB(C,x) ( (C) = \
& ~B_MASK)|((x) & B_MASK) )
Farbe löschen
neuer Farbwert
• Zunächst werden die Bits der zu setzenden Farbe
gelöscht, ohne die beiden anderen Farben zu löschen.
• Anschließend wird mit oder der neue Wert gesetzt.
• Diese Bytefolge ergibt sich aus einer Shift-Operation
des Farbwertes und anschließender Maskierung.
Prof. Dr. Nikolaus Wulff
Programmieren in C
19
Ganzzahl Variante
• Durch die Macros lassen sich per get/setX die Bit
und Shift-Operationen verstecken und in eine
Color.h Datei auslagern.
• Ein Nachteil ist, dass für jeden getX/setX Befehl
Berechnungen durchgeführt werden müssen.
• Eine Zuweisung per = Operanden funktioniert
nicht, es ist immer ein setX Befehl notwendig.
Color color;
setR(color,238);
setG(color,124);
setB(color, 32);
printf("Color (%d,%d,%d)\n",
getR(color),getG(color),getB(color));
Prof. Dr. Nikolaus Wulff
Programmieren in C
20
Farbe als Feld modellieren
typedef unsigned char Color[3];
#define getR(C) ( (C)[0])
#define getG(C) ( (C)[1])
#define getB(C) ( (C)[2])
#define setR(C,x) ( (C)[0] = x)
#define setG(C,x) ( (C)[1] = x)
#define setB(C,x) ( (C)[2] = x)
• In diesem Fall werden die Farbeinträge als Feld
abgelegt und auf jede Komponente kann direkt
zugegriffen werden.
• Die Makros verstecken vor der Testroutine wieder
die Implementierungsdetails ...
Prof. Dr. Nikolaus Wulff
Programmieren in C
21
Feld Variante
• Für die getX/setX Methoden sind keine
Berechnungen notwendig.
• Es ist möglich direkte Zuweisungen mit dem =
Operanden vorzunehmen.
• Es muss immer berücksichtigt werden, dass es sich
bei diesem Color Typ um ein Feld der Länge drei
handelt, d.h. Zuweisungen geschehen per Index:
Color color ={0,255,127};
color[1] = 32;
setR(color,125);
• Beide bisherigen Varianten sind unbefriedigend...
Prof. Dr. Nikolaus Wulff
Programmieren in C
22
C Strukturen
• Im Allgemeinen kennen Programmiersprachen für
genau diese Problematik ein eigenständiges Modellierungselement: z.B. in Pascal den record, in C
das struct und OO-Sprachen besitzen hierzu das
Konzept der Klasse.
• Ein struct ist ein neuer Datentyp. Zusammengesetzt
als Aggregat aus bereits bekannten Datentypen:
struct <identifier> {
type1 <identifier1>;
...
;
typeN <identifierN>;
};
Prof. Dr. Nikolaus Wulff
Programmieren in C
23
Color struct
struct color_struct {
unsigned char r;
struct Definition
unsigned char g;
unsigned char b;
};
typedef mit struct
typedef struct color_struct Color;
#define getR(C) ( (C).r
#define getG(C) ( (C).g
#define getB(C) ( (C).b
)
)
)
#define setR(C,x) ( (C).r = (x) )
#define setG(C,x) ( (C).g = (x) )
#define setB(C,x) ( (C).b = (x) )
• Die Zuweisungen sind viel deutlicher und klarer.
• Ein struct ist ein eigenständiger neuer Datentypen.
• Per typedef wird häufig ein struct-Zeiger definiert.
Prof. Dr. Nikolaus Wulff
Programmieren in C
24
struct Verwendung
Color white = {255,255,255};
Color color;
color.r = white.r;
color.g = 64;
color.b = 32;
printf("Color (%d,%d,%d)\n",
color.r, color.g, color.b);
• Ein struct kann bei der Erzeugung direkt mit
entsprechenden Werten vorbelegt werden.
• Mit dem „. “ Operator wird auf die einzelnen
Elemente des struct in beliebiger Reihenfolge
sowohl lesend als auch schreibend zugegriffen.
• Die getX/setX Makros sind im Prinzip überflüssig.
Prof. Dr. Nikolaus Wulff
Programmieren in C
25
Der komplexe Datentyp
• Für die Ingenieurwissenschaften sind in vielen
Bereichen die komplexen Zahlen von Bedeutung.
• Mit Hilfe eines struct fällt es leicht diesen
Datentypen zu implementieren und auch
entsprechende Methoden wie +, - , * und / zu
definieren.
• Ein gut modellierter abstrakter Datentyp (ADT)
zeichnet sich nicht nur dadurch aus, dass er Felder
besitzt, sondern auch noch einen Satz von
Methoden, für die gültigen Operationen dieses
ADT.
Prof. Dr. Nikolaus Wulff
Programmieren in C
26
Complex.h
#ifndef __COMPLEX_H
#define __COMPLEX_H
/**
* Structure for the complex ADT.
*/
typedef struct {
double real, imag;
} Complex;
struct Definition und
typedef in einem
Schritt ...
/**
* The basic operations for the complex ADT.
*/
Complex cadd(const Complex u, const Complex v);
Complex csub(const Complex u, const Complex v);
Complex cmul(const Complex u, const Complex v);
Complex cdiv(const Complex u, const Complex v);
#endif
/* __COMPLEX_H */
Prof. Dr. Nikolaus Wulff
Programmieren in C
27
Complex.c
/** multiplication of two complex numbers */
Complex cmul(const Complex u, const Complex v) {
Complex z = {
u.real * v.real - u.imag * v.imag,
u.real * v.imag + u.imag * v.real
};
return z;
}
• Sobald die wesentlichen Operationen definiert und
implementiert sind lässt sich leicht mit komplexen
Zahlen rechnen.
• Leider erlaubt es C nicht die Operatoren für +,-,*,/
zu überladen, wie es mit C++ möglich ist. Deshalb
werden die Methoden cadd, cmul, etc. definiert.
Prof. Dr. Nikolaus Wulff
Programmieren in C
28
structs verschachteln
• Strukturen können ihrerseits wieder aus Strukturen
zusammengesetzt sein:
typedef struct {
String town;
String street;
long
zipCode;
} Adress;
typedef struct {
short year, month, day;
} Date;
typedef struct {
String name;
String surname;
Adress adresse;
Date
birthday;
} Person;
Prof. Dr. Nikolaus Wulff
Programmieren in C
29
Anwendung von Strukturen
• Mit Hilfe von geschachtelten Strukturen lassen sich
Zugriffe recht übersichtlich programmieren:
Person nw;
nw.name
nw.surname
= "Nikolaus";
= "Wulff";
nw.adresse.town
= "Steinfurt";
nw.adresse.street = "Stegerwaldstr. 39";
nw.adresse.zipCode = 48565;
nw.birthday.month
nw.birthday.day
...
Prof. Dr. Nikolaus Wulff
= 11;
= 20;
Programmieren in C
30

Documents pareils