• Aucun résultat trouvé

Fonctions à Nombre Variable de Paramètres

Dans le document 1. Ce qu'est un Programme C (Page 130-136)

LES FONCTIONS ET LES NOMS EXTERNES

V.1.4 Que peut on dire des appels de fonctions à effet de bord (e.g

1.6. Fonctions à Nombre Variable de Paramètres

Indication: Prendre g(x)= x*x et pour f une fonction qui imprime ses paramètres et se rappeler l'exercice V.1.2 précédent.

déclaration: f(<type> x, <type> y, int z, ...);

appel: f(x, y ,3 , a, b, c);

pile d'exécution:

...

x y 3 a b c

Pointeur liste_param

Bas de pile d'exécution

Fig-V-1 Mécanisme de Fonction avec Nombre Variable de Paramètres. (Ici 6 paramètres: x, y et z déjà signalés, plus a, b, c fournis à l'appel. La valeur 3 de z est là pour en indiquer le nombre).

1.6. Fonctions à Nombre Variable de Paramètres

C'est une innovation très intéressante introduite en C ANSI. Même du point de vue du choix de la syntaxe. On met trois points de suspension ... avant la parenthèse fermante de la liste des paramètres:

Les Fonctions et Les Noms Externes

f(x, y, z, ...)

On appelle cela une ellipse. Cette caractéristique est très utile, quand on voudrait faire un traitement sur un nombre quelconque d'objets même de type différents. Par exemple, l'opérateur de projection d'une relation dans le modèle relationnel de CODD, opère sur une liste quelconque d'attributs. (C'est la liste SELECT dans le langage SQL). Sans aller aussi loin, printf() ou scanf() de C, sont des exemples de telles fonctions avec ellipse:

printf/scanf( char *, ...) où char* est le paramètre format d'impression/lecture.

Plus concrètement, dans la définition d'une fonction, on a la possibilité de traiter un nombre de paramètres quelconque dépendant de l'occurrence d'un appel. Un moyen pour faire cela est de disposer dans la fonction d'une liste qui contient la valeur des paramètres effectifs, et d'en connaître le nombre. Un compilateur C standard gère une telle liste et la met à disposition en cours d'exécution (Figure V-1). Pour bien utiliser ce mécanisme, le programmeur doit aussi choisir un moyen d'indiquer lors de chaque appel d'une fonction avec ellipse, combien de paramètres sont actuellement transmis. On pourra décider que le dernier paramètre avant l'ellipse (z dans la forme

f(<type> x, <type> y, int z, ...)) )

est ce nombre. (Dans printf() c'est le nombre de symboles % dans la spécification de format qui indique combien de paramètres effectifs sont transmis).

Voici à présent les outils (type d'objet et fonctions) bibliothèques, macro-définis, qui permettent d'accéder au mécanisme de liste de paramètres en la parcourant. Ils se trouvent dans le fichier include <stdarg.h>

Déclaration de la Liste des Paramètres: Type va_list

Il faut d'abord déclarer la structure de l'objet qui contient cette liste (qu'on peut considérer comme une pile par cohérence avec la pile d'exécution). En fait il suffit juste de déclarer un pointeur vers cette liste, laquelle liste existe déjà du fait de

Langage C

126

déclare une variable liste_param du type va_list, et qui est un pointeur qui va servir à parcourir la liste en pointant à chaque fois vers un paramètre.

Exemple:

void f(float x, char y, int nb_param /* z */, ...) {

va_list liste_param;

...

}

Fonctions d'Accès au Paramètres: va_start(), va_arg(), va_end().

Les deux fonctions va_start() et va_end() sont juste une initialisation et une finalisation (respectivement) de la liste des paramètres.

a) va_start (liste_param, nb_param);

est l'appel à une routine qui initialise l'accès à la liste des paramètres: liste_param est le pointeur déclaré va_list, et nb_param est la taille de cette liste (taille qui est par convention passée explicitement en paramètre à l'appel comme nous l'avons dit)

b) va_end (liste_param);

est l'appel à une routine qui ferme en quelque sorte la liste liste_param. Elle est sans effet en général, mais son usage est recommandé avant la fin de la fonction pour une meilleure documentation.

Exemple:

void f(float x, char y, int nb_param /* z */, ...) { va_list liste_param;

...

va_start (liste_param, nb_param);

...

va_end (liste_param);

}

c) va_arg (liste_param, <type>);

Les Fonctions et Les Noms Externes

est l'appel à une fonction qui rend le paramètre suivant de la liste liste_param (ou le premier paramètre après appel à va_start()). Elle rend donc le paramètre pointé par liste_param. <type> est le type supposé de ce paramètre (int ou double en principe, voir plus bas). On l'utilise donc (si le prochain paramètre est du type entier) comme suit:

param = va_arg (liste_param, int);

où param est une variable int locale qui recevra à chaque fois un paramètre. Cela se fera par exemple dans une boucle avec un appel à va_arg() par itération.

Exemple:

void f(float x, char y, int nb_param /* z */, ...) { va_list liste_param;

int i, param;

...

va_start (liste_param, nb_param);

for(i=0; i<nb_param; i++){

param = va_arg(liste_param, int);

... /* traitement de param */

} ...

va_end (liste_param);

}

Voici un exemple de programme principal avec des appels à cette fonction f, dans laquelle on imprime simplement la valeur des paramètres. On a rajouté les lignes:

printf("\n%d paramètres: \n", 2+nb_param);

printf("x = %f y = %c plus \n", x, y);

avant l'appel va_start, et la ligne:

printf (" %d \n", param);

dans la boucle for.

% cat ellipseEssai.c

#include <stdarg.h>

void f(float x, char y, int nb_param, ...) { /*... voir texte ci dessus */

}

Langage C

128 f(0.2, 'b', 0);

f(0.3, 'c', 1,1);

}

% cc ellipseEssai.c

% a.out

6 paramètres:

x=0.100000 y=a plus 1

2 3 4

2 paramètres:

x=0.200000 y=b plus 3 paramètres:

x=0.300000 y=c plus 1

Remarquer l'appel (le deuxième) avec aucun paramètre dans l'ellipse. Noter aussi le résultat des appels suivants:

f(0.4, 'd', 3, 9); /* nb_param=3 > nombre des arguments */

5 paramètres:

x = 0.400000 y = d plus 9

0 <-- on récupère des miettes 0

f(0.5, 'e', 2, 1,2,3,4,5,6); /* nb_param=2 < ce nombre*/

4 paramètres:

x = 0.500000 y = e plus

1 <-- on ne récupère que les premiers 2

Le nombre de paramètres doit par conséquent être égal au total des arguments fournis, pour une bonne utilisation.

Variation sur un Thème

Les Fonctions et Les Noms Externes

L'usage des fonctions avec un nombre de paramètres variable est donc relativement simple. Dans l'exemple nous avons utilisé des entiers int pour ces paramètres. Peut-on utiliser d'autres types et lesquels?

1) Si on se rappelle que lors d'un appel de fonction, les paramètres de type char et short sont promus int, et ceux de type float promus double, on conclut qu'on n'a pas intérêt à utiliser ces types (char, short, float) dans la macro va_arg(), et que celle-ci doit avoir int ou double comme deuxième argument.

2) Par contre si on sait par avance que les paramètres dans l'ellipse sont tous de même type (soit T), on peut utiliser va_arg() par:

T param;

...

param = va_arg (liste_param, T);

Exemple: T est STRING. Dans l'exemple précédent on remplace la déclaration de param par

typedef char * STRING;

STRING param;

et écrit

param = va_arg(liste_param, STRING);

Avec l'appel 

f(0.1, 'a', 4, "toto", "tintin");

on obtient

6 paramètres:

x = 0.100000 y = a plus toto

tintin

(null) <--- On a 4 (au lieu de 2) à l'appel

(null)

3) Si maintenant le type des paramètres est variable aussi à l'appel, on doit choisir une politique pour pouvoir l'indiquer lors

Langage C

130

indicateur entier valant 0 pour char* (ou STRING), 1 pour int et 2 pour double etc... comme de toute façon on a droit à autant d'arguments qu'on veut. On double par conséquent le nombre variable de paramètres.

Dans la fonction f on doit alors distinguer les différents cas comme ainsi par exemple

switch ( va_arg(liste_param, int)) {

case 0: param0 = va_arg(liste_param, char*);

... /* c'etait un char * */

case 1: param1 = va_arg(liste_param, int);

... /* c'etait un int */

case 2: param2 = va_arg(liste_param, double);

... /* c'etait un double*/

} Exercices

V.1.5 Ecrire une fonction f qui imprime ses arguments, sachant qu'ils sont en nombre et en type quelconque.

V.1.6 Simuler la fonction printf() avec une fonction output() dont le premier argument est une chaîne qui contient autant de caractères 'x' que d'arguments entiers qui viennent ensuite (et qui sont à imprimer). Par exemple

output ("abcx efghxx", 1,2,3)

doit imprimer les trois valeurs 1 2 3 fournies (il y a 3 'x').

Dans le document 1. Ce qu'est un Programme C (Page 130-136)

Documents relatifs