Liste variable d'arguments |
Toute personne connaissant bien le C dans sa
norme ANSI, sait qu'il ne peut exister qu'une version d'une fonction à l'intérieur
d'un espace de nom d'un fichier C.
Si vous avez écrit un code source avec la fonction suivante : func(int a, int b) définie, il ne peut y avoir une seconde fonction avec le meme non mais un nombre de paramètres différent (par exemple func (int a, int b, int c). Le compilateur générera une erreur. Une question peut alors nous venir à la tete :
En fait la véritable déclaration de printf est : int printf( char * s, ...) le symbole '...' signifie que le nombre d'argument n'est pas connu à l'avance et est totalement aléatoire. Celle-ci ne peut figurer qu'à la fin d'une liste d'argument. On placera ainsi dans la signature de la fonction en premier les arguments dont la présence est obligatoire puis le symbole '...' représentant les arguments dont la présence est facultative |
Sommaire
2. va_list
3. va_start
4. va_end
5. va_arg
6. Un exemple
|
mise en place d'une fonction à liste variable d'arguments |
Pour utiliser une liste variable d'arguments dans
un source C il faut obligatoirement inclure le fichier d'entete stdarg.h :
|
#include <stdarg.h> |
celui ci contient la définition de type et
de macro qui vnot nous permettre de manipuler une liste d'argument de façons très
simple.
|
va_list |
Le type va_list tout d'abord permet au
programmeur de déclarer une variable qui va rerpésenter chaque arguments
passé à la fonction a tour de role permettant au programmeur de les manipuler.
Votre programme devra obligatoirement contenir une instruction de déclaration du type : |
va_list pointeurArgument; |
va_start |
va_start est une marco qui va initialiser
une variable de style va_list en la faisant pointer sur le premier argument facultatif
passé à la fonction. Il est donc nécessaire d'appeler cette fonction avant
d'utiliser une variable de type va_list. On passe à cette marco en deuxième
paramètre un argument nommé pour l'initialisation.
|
#include <stdar.h> void func(char * s, ...) { va_list pointeurArgument; //on pointer sur le premier argument après s va_start(pointeurArgument, s); //... } |
va_end |
Si va_start permet d'initialiser,
va_end cloture et nettoie tout ce qui est nécessaire. Il est donc important de
l'appeler avant la fin de la méthode acceptant une liste d'arguments variables. La macro
va_end accepte un paramètre : la variable de type va_list.
|
#include <stdar.h> void func(char * s, ...) { va_list pointeurArgument; //on pointer sur le premier argument après s va_start(pointeurArgument, s); //... va_end(pointeurArgument); } |
va_arg |
C'est la macro va_arg qui va retourner un
argument parmi ceux de la liste variable d'arguments passés à la fonction. Cette
macro fait en outre pointer la variable de type va_list sur l'argument suivant.
Une question peut nous venir à la tete :
En fait on va passer en deuxième paramètre à cette macro le type de l'argument voulu et la macro vous ne le restiruer dans ce type : |
va_arg(pointeurArgument, int); |
restitue l'argument sous la forme d'un entier de
type int
|
va_arg(pointeurArgument, char *); |
restitue l'argument sous la forme d'une
string
... |
Un exemple |
La plupart du temps pour expliquer les
possibilités offertes par stdarg.h ont présente une version de la fonction printf
réécrite, nous ne dérogerons pas à la regle.
|
/** * notre printf à nous * il accepte une chaine obligatoirement suivit de * plusieurs arguments dont nous ne connaissons ni * le type ni le nombre */ void PGWPrintf(char * chaine, ...) { //variable pouvant contenir les différentes valeur //de la liste variable d'arguments char *p, *valeurString; int valeurEntiere; double valeurFlottante; //liste des arguments va_list pointeurArgument; //on fait pointer pointeurArgument sur le //premier argument de la fonction va_start(pointeurArgument, chaine); //on va parcourir la chaine princpipal à //la recherche des balises '%' for (p = chaine ; *p ; p++) { //si le caractère lu n'est pas un '%' if (*p != '%') { //alors afficher le caractère sur la sortie std putchar(*p); //nouveau tour de boucle continue; } //sinon s'il y'a un '%' else { p++; //suivant le caractère suivant le % switch(*p) { //entier case 'd' : valeurEntiere = va_arg(pointeurArgument, int); printf("%d", valeurEntiere); break; //flottant case 'f' : valeurFlottante = va_arg(pointeurArgument, double); printf("%f", valeurFlottante); break; //string case 's' : //on fait pointer notre varable sur la string valeurString = va_arg(pointeurArgument, char *); //on affiche alor chaque caractères de la string for( ; *valeurString ; valeurString++) { putchar(*valeurString); } break; //Important dans tout switch un label default default : //réaffiche le % et la caractère putchar('%'); putchar(*p); } } } //nettoyage va_end(pointeurArgument); } |
Notre programme s'articule donc en 4 points
majeurs :
si nous utilisons notre fonction ainsi : |
PGWPrintf("salut %s\n tu as %dans et PI vaut %f\n", "valentin", 23, 3.14); |
nous aurons la sortie :
|
salut valentin tu as 23ans et PI vaut 3.140000 |
Comme on le voit la programmation de fonctions
acceptant une liste variable d'arguments est très simple. Il n'y a aucun moyen réel
de connaitre le nombre d'arguments passé à une telle fonction. On comprend donc
pourquoi printf ne plante pas si vous lui passez des arguments en trop....ou en moins (dans ce
dernier cas les résultats sont hazardeux...).
|
par Valentin BILLOTTE vbillotte@programmationworld.com http://www.programmationworld.com Dernière mise à jour: |