Le guide ultime du débutant
LVL 3Une variadic function est une fonction qui accepte un nombre variable d'arguments. Tu ne sais pas à l'avance combien de paramètres elle va recevoir !
Tu les reconnais à leurs ... dans la déclaration — appelés les ellipses.
/* Prototype d'une variadic function */
int ma_fonction(int count, ...);
Imagine que tu joues à un RPG et que tu as un coffre magique (le personnage/la fonction). Ce coffre peut contenir autant d'items que tu veux : une épée, 3 potions, 7 flèches... Tu ne sais pas à l'avance ce qu'il va recevoir. C'est exactement ça une variadic function : elle accepte tous les items qu'on lui donne !
printf() ! Elle accepte une chaîne de format, puis autant d'arguments que tu veux.Tout se passe dans ce header. Il contient toutes les macros dont tu as besoin pour manipuler les arguments variables.
#include <stdarg.h> /* OBLIGATOIRE pour les variadic functions */
C'est ton inventaire. Sans l'ouvrir (#include), tu ne peux pas accéder aux outils pour fouiller dans les arguments. Ce header est ton Bag of Holding de D&D !
Déclare une variable qui contiendra les arguments variables. C'est ton pointeur vers les args.
Initialise la va_list. Pointe sur le premier argument variable. Doit être appelé AVANT va_arg.
Récupère le prochain argument dans la liste. Tu dois lui dire le type attendu.
Nettoie la va_list. TOUJOURS appeler avant de quitter la fonction. C'est ton free() des va_list.
| MACRO | SYNTAXE | RÔLE | RETOURNE |
|---|---|---|---|
| va_list | va_list args; |
Déclare la variable liste d'args | type spécial |
| va_start | va_start(args, last_named); |
Initialise args après le dernier param nommé | void |
| va_arg | va_arg(args, type); |
Récupère l'arg suivant du type donné | valeur du type |
| va_end | va_end(args); |
Libère / nettoie la va_list | void |
| va_copy | va_copy(dest, src); |
Copie une va_list dans une autre | void |
#include <stdarg.h>
type_retour nom_fonction(type param_fixe, ...) /* 1. Déclaration avec ... */
{
va_list args; /* 2. Déclare le "sac à dos" */
va_start(args, param_fixe); /* 3. Ouvre le sac après le dernier param fixe */
/* 4. Boucle pour récupérer chaque argument */
while (/* condition d'arrêt */)
{
int val = va_arg(args, int); /* 5. Récupère un int du sac */
}
va_end(args); /* 6. Ferme le sac — TOUJOURS ! */
return (résultat);
}
Additionne un nombre inconnu d'entiers. Le premier argument dit combien il y en a.
#include <stdarg.h>
#include <stdio.h>
int sum_them_all(int n, ...)
{
va_list args;
int sum = 0;
int i;
va_start(args, n);
i = 0;
while (i < n)
{
sum += va_arg(args, int); /* Récupère chaque int */
i++;
}
va_end(args);
return (sum);
}
int main(void)
{
printf("%d\n", sum_them_all(4, 10, 20, 30, 40)); /* 100 */
printf("%d\n", sum_them_all(2, 5, 5)); /* 10 */
return (0);
}
C'est comme ramasser des pièces dans Mario. n te dit combien de pièces il y a sur le chemin. Ensuite tu les ramasses une par une avec va_arg et tu les additionnes dans ton compteur sum.
Affiche un nombre variable de chaînes de caractères.
#include <stdarg.h>
#include <stdio.h>
void print_strings(int count, ...)
{
va_list args;
char *str;
int i;
va_start(args, count);
i = 0;
while (i < count)
{
str = va_arg(args, char *); /* Type : char * !! */
printf("%s\n", str);
i++;
}
va_end(args);
}
int main(void)
{
print_strings(3, "Zelda", "Link", "Ganon");
return (0);
}
va_arg doit correspondre exactement à ce qu'on a envoyé. char * pour une string, int pour un entier, double pour un flottant (jamais float seul !).Gère les specifiers %d et %s comme le vrai printf.
#include <stdarg.h>
#include <stdio.h>
void my_printf(const char *fmt, ...)
{
va_list args;
int i = 0;
va_start(args, fmt);
while (fmt[i])
{
if (fmt[i] == '%' && fmt[i + 1])
{
i++;
if (fmt[i] == 'd')
printf("%d", va_arg(args, int));
else if (fmt[i] == 's')
printf("%s", va_arg(args, char *));
}
else
putchar(fmt[i]);
i++;
}
va_end(args);
}
int main(void)
{
my_printf("Joueur: %s, Score: %d\n", "Cloud", 9999);
return (0);
}
va_end → fuite mémoireva_arg → comportement indéfinifloat à va_arg → toujours utiliser double... → interdit en C-Wall -Wextra toujourschar et short sont promus en intfloat est promu en doubleQuand tu passes des arguments variables, le compilateur upgrade automatiquement certains types :
va_arg(args, float) ! Utilise va_arg(args, double) car float est promu en double.Plutôt que de passer le nombre d'args, tu peux terminer avec NULL comme sentinelle :
#include <stdarg.h>
#include <stdio.h>
void print_all(const char *first, ...)
{
va_list args;
char *str = (char *)first;
va_start(args, first);
while (str != NULL)
{
printf("%s\n", str);
str = va_arg(args, char *); /* Récupère jusqu'à NULL */
}
va_end(args);
}
int main(void)
{
print_all("Mario", "Luigi", "Bowser", NULL); /* NULL = fin */
return (0);
}
C'est comme le dernier ennemi dans un couloir. Tu avances (boucle) jusqu'à rencontrer un mur (NULL). Le NULL est ta checkpoint qui dit "stop, le niveau est terminé !"
Si tu dois parcourir les args deux fois, utilise va_copy :
void double_parcours(int n, ...)
{
va_list args;
va_list args_copy;
va_start(args, n);
va_copy(args_copy, args); /* Duplique la va_list */
/* Premier parcours avec args */
/* ... */
/* Deuxième parcours avec args_copy */
/* ... */
va_end(args_copy); /* va_end sur la copie aussi ! */
va_end(args);
}
va_copy crée une copie indépendante. Il faut appeler va_end sur les deux !#include <stdarg.h>
...int func(int n, ...);
va_listva_list args;
va_startva_start(args, n); — le 2e param = dernier param nommé
va_argint val = va_arg(args, int);
va_endva_end(args); — sinon : undefined behavior !
# Compilation basique
gcc -Wall -Wextra -Werror -pedantic main.c -o mon_prog
# Compiler plusieurs fichiers
gcc -Wall -Wextra -Werror main.c variadic.c -o mon_prog
# Avec le flag pedantic (recommandé Holberton)
gcc -Wall -Wextra -Werror -pedantic -std=c89 main.c -o mon_prog
# Vérifier les warnings
gcc -Wall -Wextra main.c 2>&1 | head -20
/* FAUX — float dans va_arg */
float f = va_arg(args, float);
/* undefined behavior ! */
/* CORRECT — double dans va_arg */
double f = va_arg(args, double);
/* float est promu en double */
void bad_func(int n, ...)
{
va_list args;
va_start(args, n);
/* ... */
return; /* OUBLI de va_end ! */
}
void good_func(int n, ...)
{
va_list args;
va_start(args, n);
/* ... */
va_end(args); /* TOUJOURS ! */
return;
}
va_list, va_start, va_arg, va_endvalgrind qu'il n'y a pas de fuite mémoire.h avec #ifndef/* Exemple de header pour variadic functions */
#ifndef VARIADIC_H
#define VARIADIC_H
#include <stdarg.h>
int sum_them_all(int n, ...);
void print_numbers(int n, ...);
void print_strings(int n, ...);
#endif /* VARIADIC_H */