Programmieren in C Zeiger auf void und ihre Anwendungen Prof. Dr. Nikolaus Wulff
Fallstudie: minmax.h #ifndef _MINMAX_H_ #define _MINMAX_H_ /** find minimal element in array */ char cmin(int length, char array[]); int imin(int length, int array[]); short smin(int length, short array[]); long lmin(int length, long array[]); float fmin(int length, float array[]); double dmin(int length, double array[]); /** find maximal element in array */ char cmax(int length, char array[]); int imax(int length, int array[]); short smax(int length, short array[]); long lmax(int length, long array[]); float fmax(int length, float array[]); double dmax(int length, double array[]); #endif /* _MINMAX_H_ */ Es ist eine Bibliothek zu entwickeln, die minimale und maximale Werte eines Feldes liefert. minmax.h zeigt die Funktionsprototypen. Prof. Dr. Nikolaus Wulff Programmieren in C 2
Motivation für void Zeiger long lmin(int length, long array[]) { long min = array[0]; if (array[i] < min) { min = array[i]; float fmin(int length, float array[]) { float min = array[0]; if (array[i] < min) { min = array[i]; double dmin(int length, double array[]) { double min = array[0]; if (array[i] < min) { min = array[i]; Lediglich die Signaturen der Funktionen sind unterschiedlich: int, long, float, double,... Code wird unnötig dupliziert. Gibt es eine Möglichkeit eine universelle Routine zu schreiben? Prof. Dr. Nikolaus Wulff Programmieren in C 3
C void Zeiger Alle C Datentypen lassen sich auch per Zeiger verund bearbeiten. Ein solche universelle Routine müsste daher mit einem universellen Zeiger arbeiten, der nicht streng auf die Typisierung int *, float * etc. besteht. C kennt den Datentyp void. Dieser wird z.b. von Funktionen ohne Rückgabewert zurückgeliefert. void (engl. leer) steht dort für Nichts. Ein void * ist jedoch kein NullPointer oder Zeiger auf Nichts, sondern auf eine Speicherstelle von unbestimmten Typ... Prof. Dr. Nikolaus Wulff Programmieren in C 4
Arbeiten mit dem void * Zeiger Ein solcher unbestimmter Zeiger ist genau der richtige Kandidat für eine generische Routine. Diese kann dann mit Zeigern von beliebigen Typen arbeiten, sofern sie nur universell genug codiert ist. Die Transformation der minimum Routine geschieht in mehreren Schritten: Feld-Arithmetik gegen Zeiger-Arithmetik austauschen. Kleiner-Vergleichsoperation generalisieren. Typsichere void Zeiger Arithmetik einbauen. Prof. Dr. Nikolaus Wulff Programmieren in C 5
1. Schritt Feld -> Zeiger int imin(int length, int array[]) { int i,min; min = array[0]; if (array[i] < min) { min = array[i]; Die Indizierung per Feld ist leicht zu eliminieren. array[i] (array + i) int imin(int length, int *array) { int i,min,*ptr; min = *array; ptr = array + i; if (*(ptr) < min) { min = *(ptr); Der Compiler sorgt dafür, das ptr richtig berechnet wird. Die Variable min muss auch void* werden... Prof. Dr. Nikolaus Wulff Programmieren in C 6
2. Schritt Vergleich per Fkt. int icmp(void *px, void *py) { int x = *(int *)px; int y = *(int *)py; return x < y; int imin(int length, int *array) { int i, *ptr,*min; min = array; ptr = array + i; if (icmp(ptr,min)) { min = ptr; return *min; icmp verallgemeinert den Vergleich als Funktion mit void *. (int *) px ist ein Zeiger auf ein int. *(int *) px ist dessen int Wert. array, ptr und min sind Zeiger, allerdings noch vom falschen Typ int * und nicht void *. Prof. Dr. Nikolaus Wulff Programmieren in C 7
3. Schritt Zeiger-Arithmetik int imin(int length, void *array) { void *ptr, *min; min = array; ptr = array + i*sizeof(int); if ( icmp(ptr,min) ) { min = ptr; return *(int *)min; Diese Version arbeitet vollständig mit Zeigern. Da void keine Größe besitzt muss mit sizeof die richtige Inkrementierung berechnet werden. Jetzt müssen nur noch der Rückgabewert, die explizite Schrittweite und die Vergleichsoperation zur Verallgemeinerung entfernt werden... Prof. Dr. Nikolaus Wulff Programmieren in C 8
4. Schritt Generische Funktion void* vmin(int length, void *array, int typesize, int (* cmp)(void *,void *)) { void *ptr, *min; min = array; ptr = array + i*typesize; if ( cmp(ptr,min) ) { min = ptr; Diese letzte generische Funktion erhält zwei zusätzliche Parameter: Die Inkrementierungsgröße typesize wird explizit übergeben, so dass sizeof(int) entfällt. Ein Zeiger (*cmp) auf die Vergleichsfunktion. Prof. Dr. Nikolaus Wulff Programmieren in C 9