IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Sous-programmes en C++


Sujet :

Langage C++

  1. #1
    Membre très actif
    Homme Profil pro
    Etudiant en génie mécanique
    Inscrit en
    Mars 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Etudiant en génie mécanique

    Informations forums :
    Inscription : Mars 2011
    Messages : 146
    Par défaut Sous-programmes en C++
    Salut!

    Depuis quelques temps, j'ai pu écrire plusieurs programmes simples en C++, et j'essaie de mettre des nouveautés à chaque fois.

    Quand mes programmes sont devenus plus conséquents (500 lignes en gros), on m'a un peu reproché de tout faire dans le Main.
    J'ai demandé ce que je pouvais faire d'autre, et on m'a expliqué qu'il fallait faire des fonctions.
    Mais en lisant la théorie sur les fonctions, j'ai pas trop compris comment c'était censé sortir du Main..?
    Autre chose: pour que les variables soient liées entre un sous-programme et les autres, est-ce que je dois utiliser une subtilité?

    Merci pour vos indications =D

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    En fait, le principe est tout bête: pour passer de la fonction (parce qu'on parle de fonction, voir de routine et non de sous programme ) main à une autre fonction, il "suffit" d'appeler cette fonction.

    une fois que la fonction appelée aura terminé son boulot, on retournera automatiquement dans la fonction appelante pour reprendre "là où on l'avait laissée".

    Un exemple tout bête, pour te permettre de comprendre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    #include <iostream>
    /* une fonction "toute bête" qui avise juste l'utilisateur qu'on arrive dedans */
    void foo()
    {
       std::cout<<"bienvenue dans foo()"<<std::endl;
    }
    /* et la fonction principale qui l'appelle */
    int main()
    {
        std::cout<<"nous sommes au debut de main()"<<std::endl;
        foo();
        std::cout<<"nous somme de retour dans main()"<<std::endl;
        return 0;
    }
    A l'exécution, tu verra apparaitre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    nous sommes au debut de main()
    bienvenue dans foo()
    nous sommes de retour dans main()
    Il faut cependant savoir que le compilateur travaille de la même manière que toi quand tu lis un livre:
    Tout comme, la première fois que tu lis le livre, tu ne sais pas ce qui va se passer à la page 10 quand tu n'en es qu'à la page 8, le compilateur ne sait pas ce qu'il y a à la ligne 10 lorsqu'il se trouve à la ligne 9.

    Et s'il ne connait pas "quelque chose", il n'est pas content et t'éjecte sans vergogne.

    Ainsi, alors que les deux fonctions existent, si on en inverse simplement l'ordre sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <iostream>
     
    /* et la fonction principale qui l'appelle */
    int main()
    {
        std::cout<<"nous sommes au debut de main()"<<std::endl;
        foo();
        std::cout<<"nous somme de retour dans main()"<<std::endl;
        return 0;
    }/* une fonction "toute bête" qui avise juste l'utilisateur qu'on arrive dedans */
    void foo()
    {
       std::cout<<"bienvenue dans foo()"<<std::endl;
    }
    le compilateur refusera de compiler le programme sous prétexte "qu'il ne connait pas foo" quand il est dans main

    Mais il "suffit" de lui dire, avant d'entrer dans main, que la fonction foo existe, et il sera content . On parle alors d'une déclaration de fonction.

    Ainsi, le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    #include <iostream>
    /* déclare la fonction foo() pour que le compilateur sache qu'elle existe */
    void foo();
    /* définition de la fonction main */
    int main()
    {
        std::cout<<"nous sommes au debut de main()"<<std::endl;
        foo();
        std::cout<<"nous somme de retour dans main()"<<std::endl;
        return 0;
    }
    /* et enfin la définition de la fonction foo */
    void foo()
    {
       std::cout<<"bienvenue dans foo()"<<std::endl;
    }
    sera parfaitement accepté par le compilateur.

    Afin de savoir de quoi l'on parle, on utilise le terme "déclaration d'une fonction" (ou toute utilisation adéquate du verbe "déclarer", bien sur) lorsqu'on ne fait qu'indiquer la signature (*) de la fonction, mais on parle de définition (ou toute utilisation adéquate du verbe "définir", bien sur) ou encore d'implémentation (ou tout autre utilisation du verbe ... implémenter) lorsque l'on fournit le corps de la fonction (comprends : lorsque l'on indique ce que doit faire la dite fonction )

    Toute définition (ou implémentation) de fonction vaut d'office déclaration pour "le code qui suit", mais tu auras de gros problèmes si tout ce qui existe au sujet d'une fonction, c'est sa déclaration: tout le processus de compilation se passera correctement jusqu'au moment où... il s'agira de faire coincider l'appel d'une fonction avec l'adresse mémoire à laquelle elle se trouve, et là, tu sera confronté à une erreur de "référence indéfinie"

    (*) On utilise régulièrement plusieurs termes lorsque l'on parle des fonction:
    • Le prototype, qui est composé du nom de la fonction et des arguments éventuels qu'elle accepte
    • La signature, qui est composée du prototype de la fonction et du type de donnée qu'elle renvoie
    • le corps de la fonction qui est composé de toutes les instructions qu'elle doit effectuer.

    Pour déclarer une fonction, la signature de celle-ci suffit
    Pour implémenter une fonction, il faut que la signature de celle-ci soit suivi de son corps placé entre accolades "{" et "}"
    De manière générale, la signature sera proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <type de retour> <nom de la fonction> ( <liste des arguments qu'elle accepte, séparés par une virgule>)
    Pour définition d'un argument de fonction se fait exactement comme la déclaration d'une variable, c'est à dire que nous pourrions modifier le code précédant sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #include <iostream>
    /* on déclare une fonction (qui ne renvoie rien) foo() qui prend un entier 
     * comme argument
     */
    void foo(int valeur);
    /* que l'on peut alors appeler depuis "n'importe où" */
    int main()
    {
     
        std::cout<<"nous sommes au debut de main()"<<std::endl;
        for(int i=1;i<=10;++i)
        {
            foo(i);
            std::cout<<"nous somme de retour dans main()"<<std::endl;
        }
        std::cout<<"merci et au revoir"<<std::endl;
    }
    void foo(int valeur)
    {
        /* les argument passés à une fonction s'utilisent exactement comme s'il
         * s'agissait d'une variable classique au sein de la fonction
         */
        std::cout<<"bienvenue dans foo()"<<std::endl;
        std::cout<<"c'est la "<<valeur<<" ieme fois qu'on passe dedans"<<std::endl;
    }
    Après, il y a quelques particularités au sujet du passage de paramètres aux fonctions, mais, si tu as déjà compris ceci, tu devrais arriver à comprendre la suite dans n'importe quel tutoriel
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    ^Attention, tu as oublié le point-virgule sur la première déclaration anticipée.
    Quant à ton dernier bloc de code, il a un commentaire non-terminé.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    ^Attention, tu as oublié le point-virgule sur la première déclaration anticipée.
    Quant à ton dernier bloc de code, il a un commentaire non-terminé.
    Bien vu!

    Corrigé, merci
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre très actif
    Homme Profil pro
    Etudiant en génie mécanique
    Inscrit en
    Mars 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Etudiant en génie mécanique

    Informations forums :
    Inscription : Mars 2011
    Messages : 146
    Par défaut
    Youpi, c'est tout nice!
    En fait, on écrit tout dans le même fichier, mais pas tout dans le main...
    C'est cool.

  6. #6
    Membre très actif
    Homme Profil pro
    Etudiant en génie mécanique
    Inscrit en
    Mars 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Etudiant en génie mécanique

    Informations forums :
    Inscription : Mars 2011
    Messages : 146
    Par défaut
    Génial!
    Avec les pototypes c'est parfait! =D

    Maintenant, un petit détail: est-ce qu'on peut créer une fonction dont le type soit une structure?
    (Je crois savoir que les structures sont des types personnalisés, en gros, mais est-ce que ça marche?)

    Ou plus simple: comment fait-on pour que toutes les fonctions puissent utiliser (et modifier) un groupe de variables déclarées (une seuls fois) dans le main?

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Armulis Voir le message
    Youpi, c'est tout nice!
    En fait, on écrit tout dans le même fichier, mais pas tout dans le main...
    C'est cool.
    On PEUT le faire, mais ce n'est vraiment pas recommandé.

    L'idéal est
    • de séparer la déclaration des fonction de leur implémentation en
      • plaçant la déclaration dans un fichier d'en-tête (souvent portant l'extension *.h ou *.hpp)
      • plaçant l'implémentation de la fonction dans un fichier *.cpp
    • de séparer clairement les différents groupes de fonctionnalités en "autant de fichiers que nécessaire"
    Cela t'évitera, entre autres, de te retrouver avec un fichier dans lequel même une chatte ne retrouverait pas ses jeunes

    Mais cela te permettra aussi de réutiliser plus facilement les différents groupes de fonctionnalités et, sans doute, d'accélérer un peut le temps de compilation
    Citation Envoyé par Armulis Voir le message
    Maintenant, un petit détail: est-ce qu'on peut créer une fonction dont le type soit une structure?
    Cette question n'a pas beaucoup de sens étant donné qu'une fonction n'a pas de type à proprement parler et qu'une fonction est un ensemble d'instructions manipulant des données alors qu'une structure est un ensemble de données que l'on met ensemble afin de leur faire rendre certains services, mais
    1. on peut parfaitement passer une structure à une fonction
    2. on peut parfaitement faire en sorte que la fonction renvoie une structure
    3. on peut même placer la fonction dans la structure afin qu'elle permette à la structure de rendre les services que l'on attend d'elle
    NOTA Il y a quelques subtilités, je te conseille donc fortement d'essayer de trouver un tutoriel correct (la page "cours" de ce site en propose quelques uns
    Ou plus simple: comment fait-on pour que toutes les fonctions puissent utiliser (et modifier) un groupe de variables déclarées (une seuls fois) dans le main?
    on fait en sorte que la fonction appelante les transmette en argument à la (aux) fonction(s) appelée(s).

    A noter que, si tu places une fonction dans une structure, elle pourra accéder à tous les élément de cette structure (pour l'instance au départ de laquelle elle est appelée) sans avoir besoin de les recevoir en paramètres
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Membre très actif
    Homme Profil pro
    Etudiant en génie mécanique
    Inscrit en
    Mars 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Etudiant en génie mécanique

    Informations forums :
    Inscription : Mars 2011
    Messages : 146
    Par défaut
    Salut!
    Merci pour cette réponse, c'est un peu plus clair maintenant.

    1.
    Mais apparemment, faire des fonctions qui renvoient une structure n'est pas la solution idéale. Même question qu'avant, donc: comment dois-je faire pour que mon "main" partage gentiment ses variables avec les autres fonctions du programme (et qu'elles aient un droit d'écriture sur ces variables)?

    2.
    J'ai déjà lu des codes source, et j'ai vu qu'il y avait plusieurs fichiers avec chacun une fonction. Le truc, c'est que je ne sais pas comment séparer le code (je m'attends à une technique assez précise et rigoureuse pour que les fichiers "gardent le contact"). À moins que vous soyez prêts à m'expliquer ça ici et maintenant, je pense que je vais me contenter de continuer à lire mon cours =D

    Remarque sur les cours de C++, si j'ose: la plupart des cours de C++ sont écrits par gens qui connaissent le C, et ils croient pouvoir enseigner le C++ parce que les compilateurs C++ tolèrent le C. Du coup, je me suis emmerdé pendant 3 mois avec des fonctions à-chier comme "printf" et "scanf", alors que le crétin qui a écrit le cours aurait juste dû apprendre qu'en C++, on utilise "cin" et "cout". C'est génial, non? =D
    C'est typiquement le genre d'abrutis qui font croire aux jeunes qu'il faut savoir le C pour apprendre le C++, alors que c'est faux =D

  9. #9
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Armulis Voir le message
    Salut!
    Merci pour cette réponse, c'est un peu plus clair maintenant.

    1.
    Mais apparemment, faire des fonctions qui renvoient une structure n'est pas la solution idéale.
    Le fait qu'une fonction puisse renvoyer "quelque chose" à la fonction appelante est une solution parmi d'autres, qui a ses avantages, même si elle peut présenter quelques problèmes dans des cas particuliers (comme le fait de renvoyer une référence sur une variable locale ), mais qui mérite toujours d'être évaluée .

    Ce sera parfois "la meilleure chose à faire", alors que ce peut ne pas l'être dans d'autres circonstances.

    Il est très difficile de donner une règle particulière à ce sujet, il n'y a vraiment que l'expérience et le bon sens qui te permettront d'évaluer l'opportunité de le faire
    Même question qu'avant, donc: comment dois-je faire pour que mon "main" partage gentiment ses variables avec les autres fonctions du programme (et qu'elles aient un droit d'écriture sur ces variables)?
    La solution passe souvent par le passage d'argument sous forme (idéalement) de référence, voir, au pire, sous forme de pointeurs.

    Soit quand même attentif au fait que si une fonction modifie un de ses paramètres de manière durable (comprend : que la modification est appliquée à la variable d'origine dans la fonction appelante), cela peut être considéré comme un "effet de bord", et que cela peut imposer certaines restrictions (entre autres, même si tu n'en est certes pas encore à ce stade, lorsque tu voudras jouer avec des threads).
    2.
    J'ai déjà lu des codes source, et j'ai vu qu'il y avait plusieurs fichiers avec chacun une fonction. Le truc, c'est que je ne sais pas comment séparer le code (je m'attends à une technique assez précise et rigoureuse pour que les fichiers "gardent le contact"). À moins que vous soyez prêts à m'expliquer ça ici et maintenant, je pense que je vais me contenter de continuer à lire mon cours =D
    En fait, le principe est "relativement" simple:

    Comme je te l'ai dit plus tôt, le compilateur doit juste savoir qu'une fonction existe pour pouvoir l'utiliser.

    L'idée est donc de placer la déclaration de la fonction dans un fichier d'en-tête (*.h ou *.hpp) et d'inclure ce fichier d'en-tête dans les différents fichier qui ont besoin de la déclaration de la fonction)

    On peut d'ailleurs faire pareil avec les structures, si ce n'est que le compilateur doit savoir, en plus:
    1. quels sont les membres qui la composent (pour s'assurer que tu essayes d'accéder à quelque chose qui existe dans la structure)
    2. la taille totale utilisée par la structure
    Il faut donc mettre toute la définition de la structure dans le fichier d'en-tête

    Pour le reste, il faut "juste" que tu crées un projet qui compilera les différents fichiers d'implémentation (*.cpp) et trouver le moyen de fournir la liste des fichiers objet à l'éditeur de liens (c'est le dernier outil utilisé lors de la compilation, qui fait le liens entre les différents symboles et l'endroit où ils se trouvent dans le programme )
    Remarque sur les cours de C++, si j'ose: la plupart des cours de C++ sont écrits par gens qui connaissent le C, et ils croient pouvoir enseigner le C++ parce que les compilateurs C++ tolèrent le C. Du coup, je me suis emmerdé pendant 3 mois avec des fonctions à-chier comme "printf" et "scanf", alors que le crétin qui a écrit le cours aurait juste dû apprendre qu'en C++, on utilise "cin" et "cout". C'est génial, non? =D
    C'est typiquement le genre d'abrutis qui font croire aux jeunes qu'il faut savoir le C pour apprendre le C++, alors que c'est faux =D
    Hé bien, je suis content qu'un débutant fasse ce genre de remarque, parce que c'est un aspect sur lequel la plupart des intervenants de ce forum reviennent très régulièrement

    S'il y a bien quelques concepts directement issus du C qu'il est "utile" de connaitre pour apprendre C++, l'opinion générale que tu croisera sur le forum est que ce n'est pas une raison pour faire de la connaissance de C un prérequis à l'apprentissage de C++, pour la simple et bonne raison que ces différents concepts peuvent très bien être intégrés dans un cours de C++
    De manière plus ou moins exhaustive, on peut citer:
    • la chaine de compilation et le comportement général du compilateur
    • la séparation du contenu en fichiers d'en-tête et d'implémentation
    • le système d'inclusion des fichiers d'en-tête
    • la syntaxe (je ne parle pas des fonctions fournies en standard, juste de la manière d'écrire un code qui sera accepté par le compilateur )
    • les concepts de variables, d'arguments, de fonction et de valeur de retour
    • les "types définis par l'utilisateur" (union, typedef, structure), même s'il y a quelques différences
    • le principe de ce qu'est un pointeur (mais attention: on les utilise de manière totalement différente en C++ moderne, et pas seulement parce qu'on a remplacé *alloc par new et free par delete )
    • j'ai peut etre oublié quelque chose, mais je crois avoir fait le tour

    Il est à noter que ces différents aspects ne devraient pas nécessiter plus de quelques pages dans un cours, dés lors, pourquoi faire d'un langage malgré tout assez difficile à maitriser un prérequis à l'apprentissage de C++, d'autant plus qu'il y a certains points qui diffèrent parfois de manière subtile, parfois de manière plus importante, et que certains aspects (indispensables en C) ne méritent d'être abordés que bien plus tard dans l'enseignement de C++

    En résumé : oui, il est utile de connaitre C, mais c'est loin d'être indispensable, et c'est à la seule condition de faire strictement la part des choses en se souvenant que C++ est un langage totalement différent de C
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. sous programme et parametre
    Par Krispy dans le forum Langage
    Réponses: 2
    Dernier message: 10/04/2006, 17h55
  2. Envoi d'informations dans un sous programme
    Par Tanguy Sarela dans le forum Linux
    Réponses: 3
    Dernier message: 22/12/2005, 16h57
  3. Réponses: 31
    Dernier message: 30/08/2005, 13h10
  4. Réponses: 2
    Dernier message: 04/06/2004, 10h36
  5. [langage] les sous programmes
    Par giverny dans le forum Langage
    Réponses: 6
    Dernier message: 21/07/2003, 19h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo