Lellipse est une capacité du langage C qui permet décrire des fonctions avec un nombre variable darguments. Un exemple classique est la fonction printf
, fonction décriture standard du C (en C++, on utilise plutôt cout
). La déclaration de printf
est la suivante :
int printf(const char *format, ...)
Noter lellipse, qui se traduit par trois points de suspension derrière le dernier argument (la dernière virgule est facultative). On peut alors appeler printf
en précisant autant de variables que lon veut (y compris zéro) derrière la chaîne de caractères :
printf("Bonjour."); printf("La racine de %d est %lg.", 2, sqrt(2)); printf("Il est %d heures, %d minutes, %d secondes.", heure, min, sec);
Dans la chaîne format
, on doit placer des codes spéciaux suivant le caractère %
, pour indiquer les variables à afficher. Les principaux formats sont les suivants :
%d |
Variable int |
%ld |
Variable longint |
%g |
Variable float |
%lg |
Variable double |
%s |
Variable char* (chaîne de caractères) |
%c |
Variable char |
mais il en existe beaucoup dautres ; pour écrire le caractère %
, il suffit de le redoubler (%
%
). On arrive ainsi à des écritures assez absconses :
printf("Bénéfices\t= %d\nPourcentage\t= %g%%\n\n", benef, pourcent);
(et encore, on peut faire bien pire). Le plus grave, cependant, est labsence de contrôle de types. Si dans lécriture ci-dessus, la variable pourcent
est de type double
, alors quune float
est attendue daprès la chaîne de format, cela conduira au mieux à une écriture erronée, au pire à une catastrophe plus ou moins amusante. Essayez par exemple ceci :
int i = 1; printf("Badaboum %s", i);
Nous ne pouvons pas vous dire le résultat, cela dépend de létat de la mémoire ; mais on peut obtenir un bien curieux listing de celle-ci.
Pour ces deux raisons, clarté et sécurité, printf
est avantageusement remplacée par le flot de sortie cout
, qui lui ne peut absolument pas produire derreur de ce type (voir chapitre 9).
La programmation des fonctions avec ellipse est un peu délicate. Il faut utiliser trois fonctions spéciales définies dans <stdarg.h>
, nommées va_start
, va_arg
, et va_end
. Elles utilisent un type spécial nommé va_list
:
void va_start(va_list vl, derniere); type va_arg(va_list vl, type); void va_end(va_list vl);
Voici comment faire dans la pratique :
vl
de type va_list
;
va_start(vl, der)
, où der
est le nom de la dernière variable qui précède lellipse ;
va_arg(vl, type)
où type
est le type de largument souhaité : la fonction renvoie alors un argument de ce type ;
va_end(vl)
.
Voici à titre dexemple une fonction qui calcule la moyenne dune suite dentiers, terminés par un zéro :
#include <stdarg.h> float moyenne(int premier...) { va_list vl; va_start(vl, premier); long somme = premier; int suivant, nombre = 1; while (suivant = va_arg(vl, int)) { somme += suivant; nombre++; } va_end(vl); return somme/float(nombre); }
On notera que vous devez connaître les types et le nombre darguments, par une méthode personnelle (dans lexemple, tous les arguments sont int
et le dernier est nul) ; il ny a aucun moyen de les déterminer autrement. En outre il ne faut pas oublier dargument en cours de route, ni les appels à va_start
et va_end
. De tels oublis peuvent provoquer un plantage de lordinateur.
Lutilisation de fonctions avec ellipse est aussi dangereuse que leur programmation. Ainsi, si lon écrit malencontreusement :
float f = moyenne(1, 2, 3); // catastrophe !!
au lieu de :
float f = moyenne(1, 2, 3, 0);
le résultat risque dêtre désagréable. De même si lon écrit :
float f = moyenne(1, i, 2, 3, 0);
et que i
est nul.
Dune façon générale, il vaut mieux éviter les fonctions avec ellipse lorsquelles ne simposent pas. Ici, lellipse de la fonction sera avantageusement remplacée par un pointeur sur une liste dentiers, plus un paramètre entier indiquant le nombre déléments à prendre en compte, ce qui permet de mettre des zéros dans la liste :
float moyenne(unsigned nombre, int *liste) { long somme = 0; for (int i = nombre; i > 0; i--) somme += *liste++; return somme/float(nombre); }
Exercice 5.1 |
Écrire une version de |
Voir solution |
On notera que la conversion automatique des arguments interdit de récupérer des variables de type float
avec va_arg
(ils sont en fait au format double
).
Exercice 5.2 |
Écrire une fonction équivalente à |
Voir solution |
Terminons en notant que la fonction va_start
nécessite un premier argument pour aller chercher les suivants, et quen conséquence les déclarations du genre :
void danger(...)
quoique permises, sont très dangereuses puisquil ny a pas de moyen simple de retrouver les arguments.
Dune façon générale, lellipse est un outil dangereux et difficile à manier, qui sera avantageusement remplacé par les arguments par défaut dans la majorité des cas.
Précédent | Sommaire | Suivant |