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