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 :
  • comment font printf, scanf, sprintf, scanf, fprintf, fscanf etc. pour accepter un nombre variable d'arguments ?


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

1. mise en place d'une fonction à liste variable d'arguments
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
  • va_start
  • va_arg
  • va_end


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 :
  • Si nous passons à notre fonction plusieurs arguments de types différents, comment va_arg va t'il nous les restituer ?

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 :
  1. la céclaration de notre objet de type va_list nommé pointeurArgument
  2. faire pointer cet objet sur la premier argument suivant la string nommée chaine passé à la fonction via l'instruction va_start(pointeurArgument, chaine)
  3. dans le switch, suivant la balise %d (entier), %f (flottant), %s (chaine de caractères) nous accédons à l'argument en spécifiant son type à la macro va_arg : va_arg(pointeurArgument, int)pour accéder à une valeur entière. Cette macro passe en outre à l'argument suivant dans la liste.
  4. enfin la libération des ressource via la macro va_end : va_end(pointeurArgument)

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...).


[ Précédent | Index | Suivant ]



par Valentin BILLOTTE
vbillotte@programmationworld.com
http://www.programmationworld.com
Dernière mise à jour: