Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

Les fonctions à nombre variable d’arguments


Information sur le tutorial

Catégorie :Tutoriaux Date de création : 14/08/2008 00:22:25 Vu : 1 754 fois

Note :
Aucune note

Commentaire sur cette source (2)
Ajouter un commentaire et/ou une note

Description

Nous avons souvent utilisé les fonctions
   – printf
   – scanf
Ces fonctions sont particulières
  – Elles acceptent un nombre de paramètres variable !
Pouvons nous aussi créer de telles fonctions ?
Présentation d'une bibliothéque du langage C "stdarg.h".
Explication avec des exemples et des exercices.  

Tutorial

Les fonctions à nombre variable d’arguments

Le langage C permet de définir des fonctions dont le nombre d'arguments n'est pas fixé et peut varier d'un appel à un autre.
Nous avons souvent utilisé les fonctions
– printf
– scanf

Ces fonctions sont particulières
– Elles acceptent un nombre de paramètres
variable!

1 paramètre :
Code : c


Printf(" Bonjour !\n");




2 paramètres :
Code : c


Printf(" le cube de %ld est :\n", valeur);




4 paramètres :
Code : c


Printf("%ld * %ld = %ld\n", i, valeur, produit);






Pouvons nous aussi créer de telles fonctions ?

  • Déclaration et syntaxe
  • Accès aux arguments
  • Applications:
  • Exemples
  • Exercice

Déclaration et syntaxe

La déclaration se fait de la manière suivante:
Code : C


#include <stdarg.h>
type fonction(type1 arg1, type2 arg2, ...)
{
}




Appel au fonction :

Code : C


fonction(arg1,arg2);




Règles à respecter :
R1:
Si le prototype contient P paramètres formels, alors l’appel à la fonction doit se faire avec au moins P paramètres.

NB: Ceci n'est pas valide
Exemple :
On considère la déclaration suivante :

Code : C


type fonction (type1 arg1, type2 arg2 ,type3 arg3, ...)




Lors de l'appel de cette fonction on doit fournie au moins 3 arguments :

Code : C


fonction( arg1,arg2,arg3) //appel correct
fonction( arg1,arg2) //appel incorrect
fonction( arg1,arg2,arg3,arg4) //appel correct





R2 :
Une fonction avec un nombre variable de paramètres doit comporter au moins un paramètre fixe.





NB: déclaration fausse
Code : C


int somme(...) ;



R3 :
La notation … (obligatoirement à la fin de la liste des paramètres) spécifie que la fonction possède un nombre variable de paramètres.
cette liste variable de paramètres doit toujours figurer en dernière position parmi les paramètres formels.
Exemple correcte de déclaration :


Code : C


void erreur(int n, char *msg, ...);




Cette déclaration dit que la fonction erreur est défini de telle manière que les appels doivent fournir au moins deux arguments, un de type int et un de type char mais qu'ils peuvent fournir des arguments supplémentaires.
Exemple d'appel :

Code : C


erreur( 3, "appel correct");



Accés aux arguments

Pour accéder aux arguments situés après le dernier argument fixe, il faut utiliser certaines
fonctions (ou plutôt macros) du fichier stdarg.h :
Code : C


#include <stdarg.h>




Cette bibliothèque contient tout les fonction qu’on a besoin pour accéder aux arguments,
elle définit un type a utiliser pour traiter les listes variables de paramètres:
- va_list : crée un tableau contenant les informations sur les arguments .
Par exemple : va_list ap ;
Et 3 macros pour récupérer la valeur des paramètres.



Code : C



void va_start(va_list ap, last);



la macro Va_start fait pointer ap sur le premier argument variable fourni à la fonction.
Code : C


type va_arg(va_list ap, type);


- la macro Va_arg renvoie le premier argument variable et fait pointer ap sur l'argument suivant.
- type est le type de l'argument qui va être lu et Va_arg génère une expression de ce même type.
Code : C

void Va_end (va_list ap);

la macro va_end remet tout en normal avant le retour à la fonction appelante.
Ordre d’appel :
Va_start doit être appliquée avant Va_arg ou Va_end.
Va_end doit être appelée une fois que Va_arg a lu tous les arguments, sinon le comportement du programme sera imprévisible.
Progression dans la liste d’arguments Le premier appel à Va_arg fait renvoyer le premier argument de la liste
Chaque appel suivant à Va_arg permet de récupérer un argument.
Valeurs renvoyée
Va_start et Va_end ne renvoient pas de valeur.
Va_arg renvoie l’argument courant de la liste (celui sur lequel ap pointe).


Remarques :


Les types char, unsigned char ou flottants sont interdits avec va_arg.
Règles de passage de paramètres:
• Pour les paramètres de type entier
– char, short sont convertis en int.
– long reste au format long.
• Pour les paramètres de type réel
– Ils sont convertis en double

Applications

Dans cette partie on va appliquer tous ce qu'on vu dans les précédentes :
Je vais vous montrer un petit exemple d’application puis un petit exercice avec solution.

Exemple :

On va réaliser une fonction qui calcule la somme des tout ses arguments, le nombre d’arguments est variable d’un appel à un autre.
Comme je l’ai déjà dit : « Une fonction avec un nombre variable de paramètres doit comporter au moins un paramètre fixe. « Puisque les paramètres sont de même type (exemple : int ,double,… )
donc la déclaration de notre fonction somme doit être :
Code : C

Double somme(double a,...) ;

Ou si vous voulez une fonction pour seulement les entiers :
Code : C

int somme(int a,...) ;

Dans La définition on va utiliser tout les macros (fonction) qu’on a vue.

-va_list : pour crée le tableau contenant les informations sur les arguments.
-va_arg : pour récupérer l’argument courant et fait passer le pointeur (va_list) au suivant.
-va_end : remet tout en normal avant le retour à la fonction appelante.

Un problème :
le nombre de paramètres est variable donc on ne sait pas combien de fois on doit utiliser la fonction va_arg () ;
Solution :
1-Soit on vas ajouter un paramètre indiquant le nombre de paramètre.
2-Soit vous choisissez un marqueur de fin pour qu’in puisse arrêter notre boucle (exemple : 0,-1) .
Pour la 1er solution :
Code : C


#include<stdio.h>
#include <stdarg.h>
int somme(int a,...)//la déclaration avec un paramétre(le nombre des paramatres passé à la fonction)
{
int som=0,i=0,j=a;
va_list ap; //création du pointeur
va_start(ap,a); //initialisation sur le premier paramétre
int c=0;
while(i<j) //boucle pour récupérer tout les paramétres
{
c=va_arg(ap,int); //va-arg permet de retourner la valeur du paramétre courant et fait pointé ap sur le suivant
som+=c;
i++;
}
va_end(ap);
return som;
}
int main()
{
printf("la somme est : %d \n",somme(4,2,3,4,6));
return 0 ;
system("pause");
}



Résultat du programme:
Code : Consol


la somme est : 15
Press ENTER to continue





Et maintenant pour la 2ème :

(Avec un marqueur de fin) je vais choisir 0 comme marqueur de fin puisque x+0=x;

Code : C

#include <stdio.h>
#include <stdarg.h>
int somme(int a,...)
{
int som=a,i=0;
va_list ap;
va_start(ap,a);
int c=1;
while(c!=0)
{
c=va_arg(ap,int);
som+=c;
i++;
}
va_end(ap);
return som;
}
int main()
{
printf("la somme est : %d \n",somme(1,2,3,4,6,7,8,9,0));
system("pause");
}



Résultat du programme:
Code : Consol


la somme est : 40
Press ENTER to continue



Le moment de l'exercice est venu.

EXERCICE

Faire une simulation de la fonction printf();
pour ne pas compliquer les choses,on trait que les entier les caractères, et les chaînes de caractères;
Donc notre fonction doit traiter le cas :
Code : C ma_printf("je suis %s,j'ai %d",nom,age);
Aide :
On peut utiliser la fonction "fputc()" ou autre fonction pour afficher les caractères et "itoa()" (utilisable seulement sous Windows) pour la conversion d'un entier en chaîne de caractères.
Code : C fputc('c',stdout);
Code : C itoa(int entier_a_convertir,char * ou_mettre_resulat ,int base);//base :10 décimal,8 octal,16 hex. Si vous êtes sous Linux ou autre OS vous pouvez utiliser la fonction suivante au lieu de "itoa()"cette fonction permet de convertir un entier en chaîne de caractères,on peut aussi choisir la base de conversion(base :2,8,10,16):


Code : C

char* convertion(const int x,const unsigned short base, char* resultat){


char HEX[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};


/* espace de travail */

int i, j, cpt, reste; /* reste est compris entre 0 et 16*/

char chaine[34]; /*en base 2 le plus long int se représente sur

32 octets + 1 octet final + 1 octet de signe ...*/

int quotient = x;



/* vérification de la base. il faut que ça cadre avec "HEX" */

if ((base<2)||(16<base)){

printf("base non valide \n");

/* ici il faudrait presque mettre un exit... ce qui est un peut violent*/

return NULL;

}




/* parce qu'on ne travaille qu'avec des entiers positifs */

if (quotient<0){

quotient = -quotient;

}



/*initialisations*/

cpt = 0;



/* calculs */

while (quotient!=0){

reste = quotient % base ; /* sinon: reste -= (quotient*base) */


/*pour passer à la ligne suivante*/

quotient = (int) quotient/base;


/*operation qui nous interesse*/

chaine[cpt]=HEX[reste];

cpt++;

}


/*ajout du signe*/

if (x<0){ /* si c'est négatif*/

chaine[cpt]='-';

cpt++;

}

/*inversion du sens de lecture de la chaîne pour obtenir le résultat*/

for(i = 0, j=cpt-1 ; i<cpt ;i++, j--){

resultat[j]=chaine[i];

}

resultat[cpt]='\0';


return resultat;


}





Vous avez maintenant tous les moyens pour simuler la fonction "printf()"

Correction

Je vais vous faire de solution :

La 1er en utilisant "itoa()" :


Code : C


#include <stdio.h>

#include <stdarg.h>

#include <string.h>

#include <stdlib.h>


int ma_printf(char *p,...)

{

int i=0,j=0;

va_list ap;//création de la liste

va_start(ap,p);//intialisation de la liste ap

char v[30];

int n;

while(*(p+i)!='\0')//

{

switch(*(p+i))//

{

case '%':

{ i++;

if(*(p+i)=='c')

{

fputc(va_arg(ap,int),stdout);

}

if(*(p+i)=='d')

{ n=va_arg(ap,int);

itoa(n,v,10);


for(j=0;j<strlen(v);j++)

fputc(v[j],stdout);

}

if(*(p+i)=='s')

{ strcpy(v,va_arg(ap,char *));



for(j=0;j<strlen(v);j++)

fputc(v[j],stdout);

}

}

break;

default :fputc(*(p+i),stdout); break;

}

i++;

}

va_end(ap);


}


int main()

{

char nom[]="Leo";

int age=21;

ma_printf("\n--->Je m'appelle %s, j'ai %d ans\n",nom,age);

system("pause");

}


  1. La 2eme en utilisant la fonction que j'ai donné ("conversion()"):
    Code : C


#include <stdio.h>

#include <stdarg.h>

#include <string.h>

#include <stdlib.h>

char* convertion(const int x,const unsigned short base, char* resultat)

{

char HEX[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};


/* espace de travail */

int i, j, cpt, reste; /* reste est compris entre 0 et 16*/

char chaine[34]; /*en base 2 le plus long int se représente sur 32 octets + 1 octet final + 1 octet de signe ...*/

int quotient = x;



/* vérification de la base. il faut que ça cadre avec "HEX" */

if ((base<2)||(16<base)){

printf("base non valide \n");

/* ici il faudrait presque mettre un exit... ce qui est un peut violent*/

return NULL;

}




/* parce qu'on ne travaille qu'avec des entiers positifs */

if (quotient<0){

quotient = -quotient;

}



/*initialisations*/

cpt = 0;



/* calculs */

while (quotient!=0){

reste = quotient % base ; /* sinon: reste -= (quotient*base) */


/*pour passer à la ligne suivante*/

quotient = (int) quotient/base;


/*operation qui nous interesse*/

chaine[cpt]=HEX[reste];

cpt++;

}


/*ajout du signe*/

if (x<0){ /* si c'est négatif*/

chaine[cpt]='-';

cpt++;

}






/*inversion du sens de lecture de la chaîne pour obtenir le résultat*/

for(i = 0, j=cpt-1 ; i< cpt ;i++, j--){

resultat[j]=chaine[i];

}

resultat[cpt]=0; /*caractère de fin de chaîne a NE PAS oublier normalement on écrit ' 0 ' */


return resultat;


}


int ma_printf(char *p,...)

{

int i=0,j=0;

va_list ap;

va_start(ap,p);

char v[30];

int n;

while(*(p+i)!='\0')

{

switch(*(p+i))

{

case '%':

{ i++;

if(*(p+i)=='c')

{

fputc(va_arg(ap,int),stdout);

}

if(*(p+i)=='d')

{ n=va_arg(ap,int);

convertion(n,10,v);


for(j=0;j<strlen(v);j++)

fputc(v[j],stdout);

}

if(*(p+i)=='s')

{ strcpy(v,va_arg(ap,char *));



for(j=0;j<strlen(v);j++)

fputc(v[j],stdout);

}

}

break;

default :fputc(*(p+i),stdout); break;

}

i++;

}

va_end(ap);


}


int main()

{

char nom[]="Leo";

int age=21;

ma_printf("\n--->Je m'appelle %s, j'ai %d ans\n",nom,age);

system("pause");

}


Résultat des deux programmes:
Code : Consol


--->Je m'appelle Leo, j'ai 21 ans

Appuyez sur une touche pour continuer...




J'espère que ce tutorial vous aura plu, j'ai essayé de faire aussi clair que possible. Vous pouvez aller sur Site pour moi pour d’autres tutoriaux sur langage C et autres . Et j'espère qu'il vous a aussi donné des idées pour de futures applications.


signaler à un administrateur
Commentaire de juju12 le 15/08/2008 03:08:44

Petite variante pour la fonction Somme, sans utilisation de stdarg...

int __cdecl Somme(int nParams,...)
{
int result=0x0;
int *arg,*limit=&nParams+nParams+0x1;
for(arg=&nParams+0x1;arg<limit;arg++) result+=*arg;
return result;
}


A part ça, tutorial OK sur un sujet pas très courant tout de même à part printf...

signaler à un administrateur
Commentaire de Ilsundal le 29/08/2008 20:29:56

Au passage, ca s'appelle "Ellipse" !!! Sinon pour le reste, on y apprend le principal : c'est à dire comment utiliser l'ellipse.

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Décembre 2008
LMMJVSD
1234567
891011121314
15161718192021
22232425262728
293031    

Consulter la suite du CalendriCode



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel BAÏSE, Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,047 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.