Partage
  • Partager sur Facebook
  • Partager sur Twitter

Utilisation fontion main et arguments

    13 août 2022 à 18:11:20

    Bonjour,

    J'aimerais connaître plus précisément la fonction int main(int argc, char *argv[]);

    d'abord pourquoi mettre un pointeur vers un tableau de caractère nommé argv dans les paramètre de la fonction ?

    et pourquoi déclarer dans la fonction un paramètre d'une variable numérique argc ?

    Quelle est cette utilité ?

    Par ailleurs, pourriez vous m'expliquer la différence entre arguments et paramètres, désolé d'embêter avec mes questions de novice mais j'essaie d'apprendre au mieux des meilleures ;)

    PS: Merci de me corriger si je me suis trompé sur les concepts utilisés.  Merci et bonne journée !

    • Partager sur Facebook
    • Partager sur Twitter
      13 août 2022 à 22:19:57

      Alors vous avez posté sur le forum C++, donc du point de vue du C++, la fonction main telle quelle c'est juste un vieux truc bidon hérité du C. Sinon du point de vue du C, ces 2 paramètres permettent permettent de passer des arguments au programme (quand on lance un programme on peut lui passer des arguments, par exemple en le lançant depuis la ligne de commande). Donc le "int" (qu'on peut appeler comme on veut d'ailleurs, pas forcément "argc") c'est le nombre d'arguments qu'on a passé au programme, et l'autre paramètre (qu'on peut aussi appeler comme on veut) c'est un tableau de chaines de caractères à la C, qui représente les arguments qu'on a passé. A noter qu'on peut aussi écrire juste int main(); si on se fiche de prendre des paramètres.

      Un argument c'est la valeur qu'on passe effectivement à la fonction et un paramètre c'est ce qu'attend la fonction

      void foo(int a)
      {
        /* a est un paramètre de la fonction foo */
      }
      
      int main()
      {
        int toto = 3;
        foo(toto); /* toto est un argument qu'on passe à foo */
        foo(45); /* 45 aussi */
      }

      Sans vouloir rendre les choses encore plus tordues, ce que vous appelez "argument" ça s'appelle aussi un paramètre effectif (ou paramètre réel) et ce que vous appelez "paramètre" ça s'appelle aussi paramètre formel (ou argument muet).

      Frohlint a écrit:

      j'essaie d'apprendre au mieux des meilleures ;)

      Alors dans ce cas vous êtes effectivement au bon endroit (les autres notez bien le "meilleures" :))

      -
      Edité par JadeSalina 13 août 2022 à 22:29:46

      • Partager sur Facebook
      • Partager sur Twitter
        13 août 2022 à 22:33:28

        Salut,

        D'abord, il faut savoir que ce format nous vient tout droit du C.

        Ensuite, le parametre argc est fournit pour indiquer le nombre de paramètres distincts qui sont fournis, sachant qu'il y en aura au minimum un : le nom de l'exécutable.

        Ainsi, si tu peux faire appel à ton programme sous la forme de mon_programme -argument1 -argument2 -argument3, argc vaudra d'office 4 car il y a bel et bien quatre chaines de caractères qui sont fournies, à savoir

        1. mon_programme
        2. -argument1
        3. -argument2
        4. -argument3
        5. et -argument4

         Enfin, quant à savoir pourquoi argv est déclaré sous la forme d'un pointeur sur un tableau de caractères, la raison est toute simple: les paramètres transmis le sont forcément sous la forme de ... chaines de caractères.

        Seulement, ai-je mentionné le fait que les paramètres étaient transmis sous une forme issue du C?  Ce ne sont donc pas des std::string qui sont transmises comme paramètres, mais bien des chaines de caractères "C style". Autrement dit, des char[n], où n correspond au nombre de caractères de chaque paramètres.

        Le truc, c'est que l'on n'a aucun moyen de prévoir à l'avance pour n'importe quel programme le nombre de caractères dont seront constitués les différents paramètres.

        Au lieu de définir des char[3], au risque de vouloir transmettre des chaines de caractères plus longues ou des char[255] au risque de n'en utiliser que 10 dans "le meilleur des cas", nous allons donc transmettre des char[], qui permettront au compilateur de prévoir lui-même le nombre de caractères nécessaires.

        De même, il est impossible de prévoir un nombre bien précis de paramètres pour l'ensemble des programmes que nous pourrions envisager de créer.

        Nous allons donc transmettre ... un pointeur sur le premier carctère du premier paramètre à être transmis ainsi que le nombre de paramètres que l'on est en mesure de récupérer directement.

        Voilà donc pourquoi le premier paramètres est une valeur numérique entière de type int (souvent appelée argc pour "argument count") et le deuxième est un pointeur sur un char[](souvent appelé argv pour argument values).

        Note au passage que peu importe le type de valeur que tu vas transmettre comme paramètre, la transmission se fera toujours sous la forme d'une chaine de caractères.

        Ainsi, même si tu lances ton programme sous la forme de mon_programme 3.141592 100000, les valeur 3.141592 et 100 000 seront bel et bien transmises sous la forme de chaines de carctères et non sous la forme de valer réelle ou entière (selon le cas) ;)

        • Partager sur Facebook
        • Partager sur Twitter
        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
          13 août 2022 à 22:40:58

          Cette forme du main() est en effet héritée du C, mais elle n'est pas "bidon", juste à utiliser si le besoin de passer des paramètres au programme lors de son appel se fait sentir (par exemple pour lancer un firefox en ligne de commande avec une url en paramètre (c'est un exemple, je ne sais pas si firefox est developpé en C/C++) ).

          Pour un peu plus de précisions, le paramètre à l'indice 0 de argv contient le nom de l'exécutable.

          Edit : Arf me suis fait devancer lol

          -
          Edité par nours59 13 août 2022 à 22:50:55

          • Partager sur Facebook
          • Partager sur Twitter
            14 août 2022 à 3:39:04

            Voici un exemple très simple qui fonctionne:
            -
            #include <iostream>
                int main(int arguments_count, char *arguments_values[]) {
                for(int i {0}; i < arguments_count; i++)
                    std::cout << arguments_values[i] << std::endl;
            }
            -
            J'ai volontairement changé les noms des variables qui sont en général consacrés comme argc et argv
            • Partager sur Facebook
            • Partager sur Twitter

            Le Tout est souvent plus grand que la somme de ses parties.

              14 août 2022 à 19:02:38

              > Sans vouloir rendre les choses encore plus tordues, ce que vous appelez "argument" ça s'appelle aussi un paramètre effectif (ou paramètre réel) et ce que vous appelez "paramètre" ça s'appelle aussi paramètre formel (ou argument muet).

              "argument muet" ... je n'avais jamais entendu ce terme, et wiktionary m'a donné une citation avec l'auteur à l'origine de ce terme... Mais pourquoi je ne suis pas surpris 🤦‍♂️

              Sinon oui, dans la littérature anglaise on aura essentiellement argument et paramètre (et j'avoue que je ne me souviens jamais de qui est qui), et paramètre réel et paramètre formel dans la littérature française, quand les termes sont traduits (et là, la distinction m'est bien plus simple à retenir; pour quand c'est nécessaire).

              ---

              Pour ce qui est de la signature de main(), à un moment donné il a fallu faire un choix. Et il a abouti en ce que tu as vu en C, et le C++ en a hérité: un pointeur vers un ensemble contigu de pointeurs vers des caractères (attendus être des chaines 0-terminées), plus un compteur pour savoir combien il y a de pointeurs (dit autrement: de paramètres qui sont des chaines). C'est très C dans l'esprit avec l'ambivalence quant à comment interpréter un pointeur (élément seul ou adresse du premier élément d'une séquence, séquence qui sera terminée avec une valeur sentinelle ('\0' des chaines) ou dont on devra connaitre (autrement) la taille (d'où le argc)).

              Quant à argv[0], c'est aujourd'hui quasi systématiquement le chemin qui a servi à lancer l'exécutable. Mais... en vrai le contenu de la chaine est un choix fait par le shell (bash, cmd, etc) ou d'autres couches de l'OS/environnement fenêtré. Je me souviens de pinaillages il y a fort fort longtemps qui rappelaient que ce n'était pas nécessaire et que cela pouvait tout à fait être vide (IIRC). Bref, le contenu exact est un choix de l'appelant (externe) qui exécute notre programme.

              -
              Edité par lmghs 14 août 2022 à 19:04:06

              • Partager sur Facebook
              • Partager sur Twitter
              C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                15 août 2022 à 7:06:55

                Dans le système Unix, et bien d'autres, l'exécution d'un programme est réalisé par l'appel d'une fonction exec-quelque chose (1) à qui on donne en paramètres:

                • Le chemin d'accès du fichier exécutable (2)
                • Les paramètres à transmettre à cet exécutable (3) qu'il recevra sous forme dune table de chaînes, dans les argc/argv de son main

                Quand on tape une commande dans un shell, il fait un exec en utilisant, comme 1ere chaîne de la table, le nom qu'on a utilisé pour désigner le programme à lancer. Mais ce n'est pas une obligation.

                (1) il y en a plusieurs

                (2) sous forme absolue, ou relative dépendante ou pas de la variable d'environnement PATH, d'où des variantes d'exec avec un p dans le nom, comme PATH

                (3) selon la fonction exec, fournis sous forme d'une table de chaines, ou d'un nombre variable de paramètres (la fonction exec constitue la table a partir de la liste de "varargs", ), d'où  des variantes d'exec avec un l dans le nom, pour liste

                DEMO Un exemple d'appel qui cache le nom de l'exécutable lancé (en fait il se relance lui-même) , qui exécute un ps permettant de constater qu'il s'est déguisé, le coquinou.

                #include <stdio.h>
                #include <stdlib.h>
                #include <unistd.h>
                #include <string.h>
                #include <sys/wait.h>
                
                void restart_under_fake_name(char *fakename, int argc, char *argv[]);
                
                int main(int argc, char *argv[])
                {
                    printf("# (pid %d) %d Arguments reçus :\n", getpid(), argc);
                    for (int i = 0; i < argc; i++) {
                        printf("- argv[%d] = \"%s\"\n", i, argv[i]);
                    }
                	printf("# liste des processus\n");
                	// équivalent à system("ps -o pid,comm,args -H");
                    if (fork() == 0) {
                        execl("/bin/ps",       // executable
                              "ps", "-o", "pid,comm,args", "-H",  //arguments
                              (char *) NULL);  // fin de la liste des arguments
                    }
                    wait(NULL);
                	
                	if (strcmp(argv[0], "XXXX") != 0) {
                		restart_under_fake_name("XXXX", argc, argv);
                	}
                    
                	printf("# (pid %d) fin\n", getpid());
                
                	return EXIT_SUCCESS;
                
                }
                
                void restart_under_fake_name(char *fakename, int argc, char *argv[]) 
                {
                	printf("restarting as %s\n", fakename);
                	char *realname = argv[0];
                	if (fork() == 0) {
                		argv[0] = fakename;
                		execvp(realname, argv);
                	}
                	wait(NULL);
                }

                C'était l'occasion de fouiner dans les options de ps qui ne m'ont jamais servi jusque là :-)

                On utilise deux variantes d'exec

                • execl pour lancer /bin/ps (par flemme de constituer nous-même la table des arguments, on donne une liste)
                • execvp pour relancer le même exécutable avec un nom différent. On a déjà une table (d'où v qui veut dire table, allez savoir pourquoi), et on recherche l'exécutable à travers PATH (d'où p)
                Compilation et exécution
                $ make 
                cc -std=c17 -Wall -Wextra -pedantic -Werror -Wno-unused -D_XOPEN_SOURCE=700 -g    prog.c   -o prog
                
                $ ./prog abc def
                # (pid 5399) 3 Arguments reçus :
                - argv[0] = "./prog"
                - argv[1] = "abc"
                - argv[2] = "def"
                # liste des processus
                    PID COMMAND         COMMAND
                   3320 bash            /usr/bin/bash --init-file /usr/shar
                   5399   prog            ./prog abc def
                   5400     ps              ps -o pid,comm,args -H
                restarting as XXXX
                # (pid 5401) 3 Arguments reçus :
                - argv[0] = "XXXX"
                - argv[1] = "abc"
                - argv[2] = "def"
                # liste des processus
                    PID COMMAND         COMMAND
                   3320 bash            /usr/bin/bash --init-file /usr/shar
                   5399   prog            ./prog abc def
                   5401     prog            XXXX abc def
                   5402       ps              ps -o pid,comm,args -H
                # (pid 5401) fin
                # (pid 5399) fin
                On voit que le processus 5401, lancé par 5399, a reçu la chaîne "XXXX" dans argv[0].
                Rappels : quand un processus fait un appel exec, le code et les données du processus sont REMPLACÉS par ceux du programme lancé. ON NE REVIENT JAMAIS d'un appel d'exec (sauf si il a échoué).  C'est pour cela que, pour lancer une commande et continuer ensuite, on fait un exec de la commande dans un processus fils créé par fork().

                -
                Edité par michelbillaud 15 août 2022 à 14:27:49

                • Partager sur Facebook
                • Partager sur Twitter
                  15 août 2022 à 12:45:17

                  koala01 a écrit:

                  Salut,

                  D'abord, il faut savoir que ce format nous vient tout droit du C.

                  Ensuite, le parametre argc est fournit pour indiquer le nombre de paramètres distincts qui sont fournis, sachant qu'il y en aura au minimum un : le nom de l'exécutable.

                  Ainsi, si tu peux faire appel à ton programme sous la forme de mon_programme -argument1 -argument2 -argument3, argc vaudra d'office 4 car il y a bel et bien quatre chaines de caractères qui sont fournies, à savoir

                  1. mon_programme
                  2. -argument1
                  3. -argument2
                  4. et -argument3

                   Enfin, quant à savoir pourquoi argv est déclaré sous la forme d'un pointeur sur un tableau de caractères, la raison est toute simple: les paramètres transmis le sont forcément sous la forme de ... chaines de caractères.

                  Seulement, ai-je mentionné le fait que les paramètres étaient transmis sous une forme issue du C?  Ce ne sont donc pas des std::string qui sont transmises comme paramètres, mais bien des chaines de caractères "C style". Autrement dit, des char[n], où n correspond au nombre de caractères de chaque paramètres.

                  Le truc, c'est que l'on n'a aucun moyen de prévoir à l'avance pour n'importe quel programme le nombre de caractères dont seront constitués les différents paramètres.

                  Au lieu de définir des char[3], au risque de vouloir transmettre des chaines de caractères plus longues ou des char[255] au risque de n'en utiliser que 10 dans "le meilleur des cas", nous allons donc transmettre des char[], qui permettront au compilateur de prévoir lui-même le nombre de caractères nécessaires.

                  De même, il est impossible de prévoir un nombre bien précis de paramètres pour l'ensemble des programmes que nous pourrions envisager de créer.

                  Nous allons donc transmettre ... un pointeur sur le premier carctère du premier paramètre à être transmis ainsi que le nombre de paramètres que l'on est en mesure de récupérer directement.

                  Voilà donc pourquoi le premier paramètres est une valeur numérique entière de type int (souvent appelée argc pour "argument count") et le deuxième est un pointeur sur un char[](souvent appelé argv pour argument values).

                  Note au passage que peu importe le type de valeur que tu vas transmettre comme paramètre, la transmission se fera toujours sous la forme d'une chaine de caractères.

                  Ainsi, même si tu lances ton programme sous la forme de mon_programme 3.141592 100000, les valeur 3.141592 et 100 000 seront bel et bien transmises sous la forme de chaines de carctères et non sous la forme de valer réelle ou entière (selon le cas) ;)



                  • Partager sur Facebook
                  • Partager sur Twitter
                  Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                    15 août 2022 à 14:46:56

                    Wouha !

                    Merci pour toutes vos réponses, grâce à vous j'ai pu bien saisir cette utilité et le pourquoi du comment :)

                    Je repasserai pour d'autres questions, encore merci à vous ;)

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Utilisation fontion main et arguments

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown