ein Kapitel zurück                                           ein Kapitel weiter

Sehen sie sich mal in der Headerdatei <stdio.h> die Standartfunktion printf an...

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

Sie werden sich jetzt sicherlich Fragen was diese ... 3 Punkte bei dieser Funktion bezwecken sollen. Diese 3 Punkte werden Ellipse genannt. Diese 3 Punkte deuten an das beim Aufruf der Funktion neben einen fest vorgeschriebenen Parameter (const char *format) beliebig weitere aktuelle Argumente angegeben werden können. Sehen wir uns einfach die Benutzung von printf mal an. Wir benutzten printf ohne weiter Argumente...

printf("Hallo ich bin printf ohne extra Argumente\n");

Jetzt benutzten wir mal printf mit Argumente...

char argument[] = "Argumente !";
int i=2;

printf("Ich habe %d %s\n",i,argument);

Ihr Compiler erkennt jetzt anhand der beiden Formatzeichen %d und %s das dorthin 2 verschieden Argumente kommen.

printf("Ich habe %d %s\n",i,argument);
printf(const char *format, ...);

Jetzt wollen wir mal schauen wie wir das auch bewerkstelligen können ohne printf. Alle Funktionen die sie benötigen sind in der Headerdatei <stdarg.h> eingebunden. Folgende 4 Makros sind darin definiert...

MAKRO Syntax Bedeutung
va_list va_list name; Abstrakter Datentyp mit dem die Liste der Parameter definiert wird
va_start va_start(va_list name,lastarg); Argumentliste wird initalisiert
va_arg typ=va_arg(va_list name,typ); liest den nächsten Parameter in der Argumentliste
va_end void va_end(va_list name); schließt die Argumentliste


Schauen wir uns das ganze mal in der Praxis an. Wir schreiben eine Funktion bei der wir mehrere Zahlen übergeben und diese zusammenaddieren.....

/*Download:varg1.c*/
#include <stdio.h> #include <stdarg.h> static int summe(int zahl1, ...) { va_list zeiger; int zahl; va_start(zeiger,zahl1); while(1) { zahl=va_arg(zeiger,int); if(zahl==0) /*Abbruchbedienung*/ break; zahl1+=zahl; } va_end(zeiger); return zahl1; } int main() { printf("%d\n",summe(11,12,13,0)); printf("%d\n",summe(99,66,33,22,11,0)); return 0; }

Gehen wir das Programm Schritt für Schritt durch.....

printf("%d\n",summe(11,12,13,0));

Wir rufen die Funktion auf und übergeben die Zahlen 11,12,13 und 0. Die Zahl 0 ist wichtig da diese unsere Abbruchbedienung darstellt. Zuerst definieren wir mit...

va_list zeiger;

...einen Abstrakten Datentyp den wir für die Verarbeitung der weiteren Funktionen benötigen. Mit...

va_start(zeiger,zahl1);

...initialisieren wir die Liste. Man könnte auch sagen der Datentyp zeiger zeigt auf die 1.Zahl unserer Argumenten-Liste. Anschließend übergeben wir an die Variable zahl mit....

zahl=va_arg(zeiger,int);

...den nächsten Wert in der Liste, was bei uns die 12 wäre. Der Typ diese Wertes den wir als Parameter übergeben ist int. Falls dies Zahl nicht 0 ist (if(zahl==0)) wird der Wert von zahl1 (11) zu den Wert von zahl (12) addiert. Dann springen wir wieder mit (zahl=va_arg(zeiger,int);) zu dem nächsten Wert (13) und addieren diesen wieder zu zahl1 (23). Beim nächsten Durchgang ist die Bedienung erfüllt das unsere zahl==0 ist. Wir beenden (schließen) unsere Liste mit...

va_end(zeiger);

Anhand des ersten Funktionsaufruf können sie sich das ganze bildlich so vorstellen........


Argumentenliste



Jetzt wollen wir das Programm so umschreiben damit wir variabel Lange Argumentlisten Abarbeiten können...

/*Download:varg2.c*/
#include <stdio.h> #include <stdarg.h> static int summe(int anzahl, ...) { va_list zeiger; int zahl,ergebnis=0; int i; va_start(zeiger,anzahl); for(i=1; i<=anzahl; i++) { zahl=va_arg(zeiger,int); ergebnis+=zahl; } va_end(zeiger); return ergebnis; } int main() { printf("%d\n",summe(4,3,2,4,7)); printf("%d\n",summe(6,11,22,33,44,55,66)); return 0; }

Das Programm ist im Prinzip Dasselbe wie zuvor nur das wir als Abbruchbedienung den ersten Wert nehmen den wir an die Funktion übergeben. Bei dem Funktionsaufruf...

printf("%d\n",summe(6,11,22,33,44,55,66));

sind dies 6 Variablen vom Typ int die wir Abarbeiten müssen. Dies ist die Abbruchbedienung für die for - Schleife in der Funktion summe().

Natürlich können wir das ganze auch mit anderen Datentypen machen. Sie kennen doch die Funktion strcat, zum Anhängen eines Strings an den anderen. Oft kommt es doch vor das sie mehrere Strings an einen anderen hängen müssen. Dabei müssen lautet strcat-Aufrufe ausgeführt werden. Wir wollen nun eine Funktion schreiben bei der wir mit folgendem Aufruf.....

strxcat(3, string, string1, string2);

Beide Strings, string1 und string2 an den String string hängen. Die Anzahl der Strings kennzeichnnen wir am Anfang der Argumentenliste. Hier sind es 3.

Wollen uns nun den Quellcode dazu ansehen....

/*Download:strxcat.c*/
#include <stdio.h> #include <stdarg.h> #include <string.h> #define MAX 40 void strxcat(int n_strings, ...) { va_list zeiger; char *quelle, *ziel, *p; int counter=0; va_start(zeiger,n_strings); /*Nun auf den Zielstring*/ ziel = va_arg(zeiger,char *); p = ziel; /*Am Ende vom Zielstring*/ ziel+=strlen(ziel); if( (ziel-p) > MAX){ printf("!!!Max. Anzahl Zeichen überschritten!!!\n"); return; } while(--n_strings > 0) { /*Quelle einlesen*/ quelle=va_arg(zeiger, char *); /*Jetzt Zeichen für Zeichen an ziel*/ while(*quelle){ *ziel++ = *quelle++; if( (ziel-p) > MAX){ printf("!!!Max. Anzahl der Zeichen ueberschritten!!!\n"); return; } } } *ziel = '\0'; } int main() { char string[MAX] = "Test : "; char string2[] = " Und"; strxcat(3, string, "hallo " , "welt"); printf("%s\n",string); strxcat(5, string, string2, " noch", " ein", " Test"); printf("%s\n",string); /*Und nun ein Fehler mit Absicht*/ strxcat(4, string , " Ueberlauf", " von", " MAX"); printf("%s\n",string); return 0; }

Hier haben wir auch eine Sicherung eingebaut um uns vor sogenannten Pufferüberläufe zu sichern.

Für Fortgeschritten :

(Anmerkung: Dies Kapitel setzt voraus das sie sich mit Bitoperatoren und Bitmanipulationen auskennen)

Mal Ehrlich, haben sie schon mal überlegt wie sie die Daten Netzwerkgerecht verpacken können? Wie wollen sie zum Beispiel ein char (mind. 8Bit), einen short (16Bit) und einen long-Wert (32Bit) von A nach B versenden. Vor allem wie können wir das ganze Byteweise verpacken. Nehmen wir mal an wir müssen folgende Werte....

short count = 10;
char  val   = 'a';
long  data  = 10000;
long  data2 = 12345;

...in ein unsigned char - Array verpacken durch das Netzwerk von A nach B schicken und es am Zielrechner wieder entpacken. Wir verwenden hier das 1.Byte um unser Packet zu codieren und wieder decodieren. Da wir hier wieder mehrer Verschieden Werte haben eignet sich hierfür wieder prima die Argumentenliste.

Der Aufruf zum packen der Daten sollte wie folgt aufgerufen werden....

pack(ziel, Formatliste, Größe der einz. Packete, short, char, long, long);

Das ziel stellt unser Packet da das wir über das Netzwerk schicken. Die Formatliste die einzlnen Kürzel der Datentypen. Hier also "cscll" (char,short,char,long,long). Wobei das erste char hier die Größe der einzelnen Packete darstellen soll. Wir verwenden hier 1 Byte. Am Ende kommen dann die Daten in entsprechender Reihenfolge.

Beim Entpacken der Daten auf der Gegenstellen sieht das ganze dann gleich aus. Nur wird aus der Variable ziel logischerweise quelle.

Zum Programm muss ich noch Anmerken das ich hier keinen Speicher dynamisch allokiere für das Packet. Dies nur zur Information, sollten sie das Programm in ähnlicher weise in der Praxis einsetzen wollen. Hier nun der Quellcode...........

/*Download:packet.c*/
#include <stdio.h> #include <stdarg.h> typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned long ulong; int pack(uchar *buf, char *fmt, ...) { va_list args; char *p; uchar *bp; ushort s; ulong l; bp=buf; va_start(args, fmt); for(p=fmt; *p!='\0'; p++) { switch(*p) { case 'c' : *bp++ = va_arg(args,int); break; case 's' : s = va_arg(args, int); *bp++ = s >> 8; *bp++ = s; break; case 'l' : l = va_arg(args, ulong); *bp++ = l >> 24; *bp++ = l >> 16; *bp++ = l >> 8; *bp++ = l; break; default : va_end(args); return -1; } } va_end(args); return bp-buf; } int unpack(uchar *buf, char *fmt, ...) { va_list args; char *p; uchar *bp, *pc; ushort *ps; ulong *pl; bp=buf; va_start(args, fmt); for(p=fmt; *p!='\0'; *p++) { switch(*p) { case 'c' : pc = va_arg(args, uchar*); *pc = *bp++; break; case 's' : ps = va_arg(args, ushort*); *ps = *bp++ << 8; *ps |=*bp++; break; case 'l' : pl = va_arg(args, ulong*); *pl = *bp++ << 24; *pl |= *bp++ << 16; *pl |= *bp++ << 8; *pl |= *bp++; break; default : va_end(args); return -1; } } va_end(args); return bp-buf; } int main() { int p; uchar *buf,*buf2; ushort count = 10, ucount; uchar val = 'a', uval,c; ulong data = 10000, data2=12345, udata, udata2; p=pack(buf, "cscll", 0x01, count, val, data, data2); printf("Gepackt in %d Bytes\n",p); unpack(buf, "cscll", &c, &ucount, &uval, &udata, &udata2); printf("Und wieder entpackt = "); printf("%d:%c:%ld:%ld\n", ucount, uval, udata, udata2); return 0; }

Dies stellt natürlich nur einen Anriss (Code-Snippet) dieses sehr Umfangreichen Themas da. Hier geht es ja auch nur ledeglich um denn Sinn von Argumentlisten.

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf