alexandre.tsu.manuel

Alexandre Manuel

  • Date d'inscription: 14 septembre 2010
  • Dernière connexion : 7 décembre 2018

Pas de cours en rédaction

Compétences

  • Site Web
    • HTML / CSS HTML / CSS
      Niveau
    • Javascript Javascript
      Niveau
    • PHP PHP
      Niveau
  • Entreprise
    • Entrepreneuriat Entrepreneuriat
      Niveau
  • Programmation
    • Langage C Langage C
      Niveau
    • Langage C++ Langage C++
      Niveau
    • Langages.NET Langages.NET
      Niveau
    • Langage Java Langage Java
      Niveau
    • Langage Python Langage Python
      Niveau
    • Autres langages (VBA, Ruby,...) Autres langages (VBA, Ruby,...)
      Niveau
    • Base de données Base de données
      Niveau
    • Mobile Mobile
      Niveau
    • Discussions développement Discussions développement
      Niveau
  • Systèmes d'exploitation
    • Windows Windows
      Niveau
    • Linux & FreeBSD Linux & FreeBSD
      Niveau
  • Matériel & logiciels
    • Choix du matériel & configuration Choix du matériel & configuration
      Niveau
    • Vos réseaux Vos réseaux
      Niveau
  • Sciences
    • Mathématiques Mathématiques
      Niveau

Biographie

Anniversaire : 5 septembre (23 ans)

Bon au début j'avais vu biographie alors j'avais mis l'histoire de ma vie :p mais en fait je pense que ça n'intéresse pas grand monde. J'ai donc décidé de vous faire des mini-mini tutos sur des choses et d'autres :D en espérant avoir pu vous apprendre des choses.

Bon alors tout d'abord un tuto sur le langage C : Les listes variables d'arguments (beaucoup de tutos là dessus sont présents dans les bios d'autres membres du sdz mais un de plus c'est pas plus mal)

Les listes variables d'arguments


Bon alors tout d'abord nous allons voir ce que c'est, comment vous en servir puis à quoi ça peut servir.

Les listes variables d'arguments Cékoissa ?


Si vous ne savez pas ce que c'est, ça veut dire que toutes les fonctions que vous avez faites jusqu'ici prennent un nombre de paramètres fixes. Par exemple :
int calculTriple(int nb);

En général on peut tout faire avec ça mais dans certains cas on se sentira limités... Par exemple, ne vous êtes vous jamais demandés comment se fait-il que la fonction printf puisse prendre un, deux, trois ou même mille paramètres ?

Mais alors c'est quoi son prototype à cette satanée fonction printf ?

J'imagine que beaucoup d'entre vous qui ne savent pas vont me dire :
void printf(char* text);

Sauf qu'il y a un problème là... Si vous essayez de mettre ce code et que le prototype de printf est le précédent, ça plantera à la compilation :
printf("Salut %s", "toi");

vous aurez une belle erreur du genre "too many arguments for function printf" et ça ça ne va pas...

Bon mais alors tu nous expliques à la fin :colere2: ?

Le prototype de printf est le suivant (bon non ce n'est pas vraiment celui là mais c'est tout comme. Si vous tombiez sur le vrai prototype de printf vous vous embrouilleriez l'esprit. Je l'ai donc simplifié un peu mais tout est là) :
void printf(char* text, ...);

:waw: Nan mais oh tu te fout de nous là ou quoi ? C'est pas un prototype valide ça !

Eh bien si ! Essayez de compiler une fonction qui prends un paramètre normal et trois petit points et la compilation laissera tout passer.
Bon mais ça ne fait pas tout. Il faut gérer tous ces arguments qui arrivent et pour cela, je vous rassure, tout est dans les bibliothèques standard. Vous allez devoir vous servir de quelques petites choses.
En premier nous allons inclure stdarg.h dans le fichier où on écrit la fonction et pas forcément là où on l'utilise.
#include <stdarg.h>

Ensuite il faut savoir que si vous mettez
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void maFontion(char* text, ...)
{
    printf(text);
}

int main()
{
    maFonction("salut %s", "toi");
    
    return 0;
}

ça ne vous donnera pas du tout le résultat que vous attendez.
Il va falloir mettre dans maFonction le fait que lorsqu'il y a un "%s" dans ma première chaîne de caractères je le remplace par "toi" et ça ce n'est pas simple mais c'est pour ça que ce tuto est là ;)

Un peu de pratique


Bon on va s'amuser à faire une fonction qui va prendre tous ces argument, en faire une seule chaîne de caractères et l'envoyer à printf en tapant printf(text); à la fin de la fonction maFonction.
Tout d'abord, notre fonction maFonction va se découper comme ceci (je ne mets plus les includes et le main hein...) :
void maFonction(char* text, ...)
{
    va_list argumentPointer;
    
    va_start(argumentPointer, text);
    //Traitement des arguments
    va_end(argumentPointer);
    printf(text);
}

Bon un peu d'explications s'imposent.
argumentPointer est une variable de type va_list. C'est cette variable qui va nous servir à obtenir nos différents paramètres.
va_start est une fonction qui prend deux arguments :
  • la variable de type va_list créée juste avant (donc dans notre cas, argumentPointer)
  • la dernière variable avant les "..." Ici c'est la variable text qui précède les trois petits points.

Bon maintenant passons à la partie la plus difficile, celle du traitement des arguments.
Il faut tout d'abord que notre fonction puisse connaître le nombre de paramètres à l'exécution de celle ci. Pour cela vous avez plusieurs méthodes :
  • Des %s, %d, etc... sont là à chaque fois qu'on veut mettre une variable. On n'a plus qu'à les compter et on a notre nombre d'arguments
  • Le premier argument peut être le nombre d'arguments que prends la fonction, par exemple :
    monAutreFonction(4, "hey", "salut", "toi", "comment tu vas ?");
  • Le dernier paramètre peut être spécifique. Par exemple, si vous faites une fonction qui additionne un nombre variables de nombres entiers, vous pouvez mettre 0 en dernière variable. Par exemple somme(3, 5, 2, 8, 0); on aura plus qu'à s'arrêter d'additionner quand on rencontre un 0 (de toute façon qui va aller faire "+0" dans une addition :p )

Il faut aussi savoir que la fonction, en plus de connaître le nombre d'arguments, doit connaître le type de chaque arguments. Dans notre premier et troisième cas on le sais.
Pour la première méthode, on n'a qu'à regarder si la lettre qui suit le % est un s, un d etc...
Pour la troisième méthode, on sait que tous les paramètres sont des int.
Enfin bref revenons à nos moutons...
La fonction dont on va le plus se servir ici est va_arg dont le prototype est :
va_arg(va_list argumentPointer, type);

En fait vous allez vous en servir à chaque fois que vous voudrez un nouveau paramètre.
Par exemple, si je sais que le premier paramètre variable est un int, il me suffira de taper
int monNombre = va_arg(argumentPointer, int);

et ainsi vous aurez une variable monNombre qui contiendra le premier de vos arguments variables.

Attention ! Vous devez toujours être sûrs que votre paramètre est bien du type que vous attendez !


Bon maintenant je vais vous donner le code tout fait et commenté de cette fameuse fonction. Elle est un peu dure à prendre en main mais vous savez tout ce qu'il y a à savoir pour comprendre ce code :
void ecrire(const char *texteAAfficher, ...)
{
    char *texte = calloc((strlen(texteAAfficher) + 1) + 250, sizeof(char));
	//texte contiendra le la chaîne assemblée finale. Elle peut contenir la chaîne principale + 250 octets de variables.
    char *argument; //Argument contiendra l'argument étudié si c'est un nombre ou une chaîne de caractères.
    int i = 0, j = 0;
    va_list argumentPointer;
    va_start(argumentPointer, texteAAfficher);

    while (texteAAfficher[i] != 0) //Tant que je n'arrive pas au bout de la chaîne principale
    {
        while (texteAAfficher[i] != '%' && texteAAfficher[i] != 0)
        {
            texte[j] = texteAAfficher[i];
            i++;
            j++;
        } //Si fin de chaîne ou variable trouvée je sort de la boucle.
        if (texteAAfficher[i] != 0)
            i++; //Si c'est un % alors j'analyse le caractère d'après.
        switch(texteAAfficher[i])
        {
            case '%'://Si je trouve un double % j'en ajoute un à la chaîne finale.
                texte[j] = '%';
                j++;
                i++;
            break;
            case 'd': //Si je trouve un d alors c'est un entier.
                argument = calloc(strlen(itos(*argumentPointer, NULL)) + 1, sizeof(char)); //Je réserve la place qu'il faut,
                itos(va_arg(argumentPointer, int), argument); //je transforme l'entier en chaîne de caratères,
                strcat(texte, argument); //je l'ajoute à la chaîne finale,
                j += strlen(argument);
                i++;
                free(argument); //et je libère argument.
            break;
            case 'f': //Si c'est un f alors c'est un nombre à virgule.
				// je fait un peux la même chose que pour un entier mais j'appelle dtos à la place de itos
                argument = calloc(strlen(dtos(*argumentPointer, NULL)) + 1, sizeof(char));
                dtos(va_arg(argumentPointer, double), argument);
                strcat(texte, argument);
                j += strlen(argument);
                i++;
                free(argument);
            break;
            case 'c': //Si c'est un caractère je l'ajoute bêtement à la chaîne finale.
                texte[j] = va_arg(argumentPointer, int);
                j++;
                i++;
            break;
            case 's':
                argument = calloc(strlen(argumentPointer) + 1, sizeof(char)); //Je réserve la place nécessaire.
                sprintf(argument, va_arg(argumentPointer, char*));
                strcat(texte, argument);
                j += strlen(argument);
                i++;
                free(argument);
            break;
            case 0: //Si c'est la fin de chaîne alors je sors du switch sans rien faire
            break;
            default: //Et si je trouve un % sans rien de spécial derrière alors je l'affiche tout simplement.
                texte[j] = '%';
                j++;
            break;
        }
    }

    va_end(argumentPointer);
    printf(texte); //Enfin j'envoie la chaîne finale à printf.
    free(texte); //Je libère la mémoire et fin de fonction.
}

Et voilà. Si vous essayez de compiler ce code, vous remarquerez que les fonctions itos et dtos n'existent pas. Et pour cause ce ne sont pas des fonctions standard. Ce sont des fonctions que j'ai codé qui transforment respectivement un int en chaîne de caractère et un double en chaîne de caractère.
Si vous les voulez les voici mais elles n'ont pas d'intérêt particulier pour ce cours :
char* dtos(double nb, char* nbTxt)
{
    char *nbTexte = NULL; //nbTexte sera une chaîne de caractères de passerelle entre le double et nbTxt
    int nbEnt = (int)nb; //nbEnt sera aussi une sorte de passerelle. Ici elle contient tout les chiffres avant la virgule du double.
    int i = 0;

    if (nbTxt == NULL) //Si j'ai reçu NULL en argument ou si le pointeur reçu est nul
    {
        nbTxt = calloc(22, sizeof(char)); //Alors je lui alloue de la mémoire
        if (nbTxt == NULL) //Et je gère les erreurs éventuelles.
        {
            printf("Ne peut pas allouer de la memoire ligne %d fichier %s\n", __LINE__, __FILE__);
            exit(EXIT_FAILURE);
        }
    }

    sprintf(nbTxt, itos(nb, nbTexte)); //Je met tous les nombres avant la virgule dans la chaîne de caractère finale.
    if (nb != nbEnt) //S'il y a des chiffres après la virgule alors j'affiche la virgule.
        strcat(nbTxt, ",");
    nb -= nbEnt; //Je fait en sorte que nb soit sous la forme 0,xxx avec xxx représentant les nombres après la virgule.
    if (nb < 0) //Si le double était négatif il ne faut pas afficher un deuxième moins après la virgule.
        nb *= -1;
    nb *= 1000000000; //Je prend les dix chiffres après la virgule et je les fait passer avant.
    nb += 0.5; //Je rajoute 0.5 pour arrondir.
    nbEnt = (int)nb; //Je caste tout ça dans un int. Les chiffres après la virgule supplémentaires seront ignorés.
    while (nbEnt % 10 == 0 && i < 10) //Ici j'enlève les zéros en trop. S'il n'y avait que des zéros, de toute façon la boucle s'arrête au bout de dix tours.
    {
        nbEnt /= 10;
        i++;
    }
    strcat(nbTxt, itos(nbEnt, nbTexte)); //Et je rajoute tous ça après la virgule de la chaîne finale.

    return nbTxt; //Et enfin je retourne la chaîne finale. Ainsi je pourrai écrire monPointeurSurChar = dtos(monDouble, NULL);
}

char* itos(const int nb, char* nbTxt)
{
    char DEC[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; //Je prépare une chaîne de caractères avec tous les nombres sous forme de caractères dedans.

    int i = 0, j = 0, cpt = 0, reste = 0; //reste ira de 0 à 10
    char chaine[11]; //Un int contient dix chiffres maximum plus un octet de signe cela fait 11 char.
    int quotient = nb;

    quotient = abs(quotient); //Je ne travaille pour le moment qu'avec des nombres positifs.
    while (quotient != 0) //Ici j'inscrit les chiffres les uns après les autres mais en commençant par la droite.
    {
        reste = quotient % 10;
        quotient = (int) quotient / 10;
        chaine[cpt] = DEC[reste];
        cpt++;
    }
    if (nb < 0) //Si l'entier reçu est négatif alors je rajoute le moins. (à droite de ma chaîne)
    {
            chaine[cpt]='-';
            cpt++;
    }
    if (nbTxt == NULL) //>Si le pointeur reçu était nul ou même si le paramètre reçu était NULL,
    {
        nbTxt = calloc(cpt, sizeof(char)); //j'alloue la mémoire nécessaire,
        if (nbTxt == NULL) //et je gère les erreurs.
        {
            printf("Ne peut pas allouer de la memoire ligne %d fichier %s\n", __LINE__, __FILE__);
            exit(EXIT_FAILURE);
        }
    }
    for(i = 0, j = cpt - 1 ; i < cpt ; i++, j--) //Enfin je met la chaîne de caractères dans le bon sens.
        nbTxt[j] = chaine[i];
    nbTxt[cpt] = 0; //Je rajoute le caractère de fin de chaîne,

    return nbTxt; //et je renvoie tout ça.
}

Et voilà vous savez tout sur comment faire des fonctions à nombre variable d'arguments. Mais je n'ai pas répondu à une question essentielle :

A quoi ça sert ?


Ah ? Vous trouvez que c'est un peu tard pour répondre à la quetion ? :-°
Bon plus sérieusement vous pouvez faire beaucoup de choses avec. Je vous donne quelques pistes :
  • Avec les listes variables d'arguments, vous allez pouvoir, comme en C++, faire des fonctions à arguments facultatifs. Vous définissez dans votre tête le fait que votre dernier argument est facultatif et peut valoir un nombre ou par défaut NULL. Il vous suffit de mettre ... à la fin. Vous mettez une variable int à NULL. Cette variable va ensuite recevoir le résultat de la fonction va_arg et si le paramètre existe, la variable sera changée. Sinon, euh... Allez savoir ce que ça peut faire. A tester.
  • Si vous faites de la SDL, vous vous rendrez vite compte de la galère juste pour écrire dans une bête zone de texte et pouvoir aller à la ligne. Les plus courageux d'entre vous feront une fonction qui prendra en argument une chaîne de caractères et l'affichera en allant à la ligne si un \n est trouvé. Les programmeurs furieux vont même faire un retour à la ligne automatique qui ne coupe pas les mots en plein milieu. Mais une fois que vous aurez fait cette fonction vous vous direz : Damned !!! Comment je fais pour afficher une variable moi maintenant ?!?! Eh bien vous n'aurez qu'à prendre la fonction que nous venons de faire et remplacer printf par votre fonction qui écrit. Ainsi vous aurez une fonction SDL qui écrira du texte dans une zone donnée, qui ira à la ligne toute seule, qui par je ne sais quel miracle gérera les accents (avec gcc en tout cas. Même si vous mettez vos accents directement dans des guillemets dans votre code source. Enfin bref, enfin une fonction simple à utiliser quoi... (si vous êtes intéressés par cette fonction qui écrit toue seule, envoyez moi un MP.

Si vous avez des critiques, des corrections, des conseils, n'hésitez pas à m'envoyer un MP. Je suis à votre écoute.
Ceci est mon premier tutoriel et il se fait tard mais je compte en faire d'autres et améliorer celui là. Je pense parler de tous les problèmes que l'on peut rencontrer avec la fonction scanf aussi bien avec les caractères, les chaînes de caractères, les nombres à virgule que les nombres entiers (tous posent des problèmes... -_- ). Je compte aussi vous donner une fonction toute faite (pas expliquée mais commentée) d'une nouvelle fonction printf qui irait à la ligne toute seule comme une grande. Je compte également vous éclairer su

Signature

qry = 2b || !2b