• # Décomposer les opérations

    Posté par . Évalué à 6.

    Pour comprendre, il est utile de décomposer les opérations effectuées.

    int * p; // déclaration d'un pointeur p
    p = dix(); // assignation du pointeur p à la valeur retournée par la fonction dix()

    Si tu as compris ce que retourne la fonction dix(), tu as la réponse à ta question. La question qui est intéressante pour comprendre la portée et la durée de vie des variables est : que vaut i à la fin du main ?

    • [^] # Re: Décomposer les opérations

      Posté par . Évalué à 2.

      Merci de ta réponse.

      Dix() retourne l'adresse de la variable locale i, n'est-ce pas ?

      De plus, je crois que i reste inchangé, à la fin du main, il vaut 20.

      Merci :)

    • [^] # Re: Décomposer les opérations

      Posté par (page perso) . Évalué à 3.

      C'est surtout que vaut le i déclaré dans dix() lorsque dix() a fini de s'exécuter… et qu'on veut utiliser sa valeur via son adresse (retour de &i stocké dans p et utilisation de *p).

      Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

      • [^] # Re: Décomposer les opérations

        Posté par . Évalué à 2.

        Lorsque dix() a fini de s'éxecuter, le i déclaré au sein de cette fonction disparait, donc on ne peut pas utiliser son adresse si ? Merci :)

        • [^] # Re: Décomposer les opérations

          Posté par . Évalué à 4. Dernière modification le 30/10/19 à 16:02.

          C'est exactement ça ! Le comportement n'est pas prévisible, on ne sait pas ce qui sera à stocké à l'adresse où était la variable locale i. On ne sait pas si cette adresse sera accessible.

          • [^] # Re: Décomposer les opérations

            Posté par . Évalué à 3.

            Merci !

            Mais du coup, en essayant de modifier le programme, j'obtiens ça:

            int *dix(void){
            static int j=10;
            return &j;
            }
            int zero(void){
            int i=0;
            return i;
            }
            int main(void){
            int i=20;
            int *p=dix();

            zero();
            i=*p;
            }

            Pourquoi cela ne marche pas ? Merci

          • [^] # Re: Décomposer les opérations

            Posté par (page perso) . Évalué à 2.

            Le comportement n'est pas prévisible,

            Si, il l'est, le résultat sera "0" ou quoi que soit écrit dans la première variable (ici i) de la fonction zero.
            - la ou les variables selon l'empreinte mémoire du type de celles -ci. -

            C'est un exercice pour expliquer le fonctionnement de la pile du thread d'un processus.

            Attention, il suffit d'activer les optimisations pour que ça ne fonctionne plus.
            ( les variables qui ne font rien vont disparaître et les fonctions aussi, du coup).

            on ne sait pas ce qui sera à stocké à l'adresse où était la variable locale i.

            Il s'agit de ce qui se trouve sous le pointeur de pile (stack pointer), donc de l'état de la dernière variable 'locale' (au type près) de la dernière fonction appelée.

            On ne sait pas si cette adresse sera accessible

            Elle le sera.

            • [^] # Re: Décomposer les opérations

              Posté par . Évalué à 2.

              Il s'agit de ce qui se trouve sous le pointeur de pile (stack pointer), donc de l'état de la dernière variable 'locale' (au type près) de la dernière fonction appelée.

              Bof, moi la question que je me pose, c'est ce que dit la norme à ce sujet. Si elle ne dit rien, on a effectivement un comportement indéterminé. Les compilateurs implémente le comportement que tu décris, mais il n'est pas dit que tous soient tenus de le faire de la même façon. D'ailleurs, tu le dis toi-même, il semble que quand les optimisations sont activées, ça ne marche plus pareil.

              • [^] # Re: Décomposer les opérations

                Posté par (page perso) . Évalué à 2. Dernière modification le 31/10/19 à 13:29.

                Bof, moi la question que je me pose, c'est ce que dit la norme à ce sujet.

                Que l'on accède à une variable hors de sa portée, c'est un cas d' undefined behavior.

                If an object is referred to outside of its lifetime, the behavior is undefined. §6.2.4/2

                C'est pourquoi ce code n'est pas un exercice de langage C.
                Je le considère comme un exercice pour expliquer le mécanisme des fonctions et de leurs piles, voire de l'espace mémoire utilisateur.C'est même un classique.
                De mon point de vue, c'est la raison pour laquelle il est important de savoir pourquoi la valeur de i est malgré tout déterminée sur les architectures/OS les plus courantes.

                Le fait est que le C et le C++ doivent être les seul langages (sans intégrer directement de l'assembleur) dans lequel on peut écrire et compiler de telles choses.

                Si elle ne dit rien, on a effectivement un comportement indéterminé

                Au contraire, c'est parce qu'elle le dit que c'en est un. Cela aurait pu être unspecified ou implementation-defined voire invalide.

                D'ailleurs, tu le dis toi-même, il semble que quand les optimisations sont activées, ça ne marche plus pareil

                C'est plus que ça.
                Les compilateurs vont faire ce qu'ils veulent de ce code, même sans optimisation.
                Ils peuvent le réduire à :

                int main(void)
                {
                *((int*)(0))=0; // crash
                }

                si ça leur chante.

                Mais on sort du sujet de l’exercice, AMHA.

        • [^] # Re: Décomposer les opérations

          Posté par (page perso) . Évalué à 3.

          Oui. D'ailleurs gcc te préviens :

          toto.c: In function ‘dix’:
          toto.c:4:9: warning: function returns address of local variable [-Wreturn-local-addr]
            return &i;
                   ^~
          

          Ta solution à base de static fonctionne pour corriger le problème de l'existence de la variable. Après il faut voir le sens qu'ont les fonctions, c'est au delà de l'exemple.

          Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

          • [^] # Re: Décomposer les opérations

            Posté par . Évalué à 1.

            Merci :)

            int *dix(void){
            static int j=15;
            return &j;
            }
            int zero(void){
            j=0;
            return &j;
            }
            int main(void){
            int i=20;
            int *p=dix();
            i=*p;
            p=zero();
            i=*p;
            }

            Et pourquoi cela ne marche pas ? Le mot clé static ne rend pas la variable j valable pour tout le programme ? Merci :)

            • [^] # Re: Décomposer les opérations

              Posté par (page perso) . Évalué à 2. Dernière modification le 30/10/19 à 16:31.

              Tu as un j local à dix() et un autre j local à zero(), la classification static de l'un n'a pas d'impact sur l'autre. Donc tu remets dans zero() le même problème que tu avais précédement dans dix(). Une variable locale statique n'est pas une variable globale.

              De plus, tu déclares que zero() retourne un entier, mais tu lui fais retourner quoi ?

              Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

            • [^] # Re: Décomposer les opérations

              Posté par . Évalué à 2. Dernière modification le 30/10/19 à 16:41.

              Ca fonctionne ! Que t'attendais-tu à avoir ?

              Il faut faire la différence en durée de vie et portée des variables. En ajoutant le mot clé "static" à la variable i/j dans la fonction dix(), tu changes sa durée de vie mais pas sa portée. La variable n'est accessible que dans la fonction dix()… sauf à utiliser un pointeur.

              Il faut garder à l'esprit que dans ton programme initial, tu avais 3 variables i différentes. Ajouter le mot clé static ne change pas la portée de ces variables et tu as toujours 3 variables i différentes (le changement de nom de i en j n'ayant absolument aucune importance).

              Edit : pardon, ça ne fonctionne pas car tu utilises la fonction zero() comme la fonciton dix() était utilisée initialement, tu retombes donc sur le même problème. Et en plus, j n'est pas déclaré dans la fonction zero() donc ça ne doit même pas compiler.

            • [^] # Re: Décomposer les opérations

              Posté par (page perso) . Évalué à 3.

              Et pourquoi cela ne marche pas ?

              qu'est ce qui "marche" selon vous ?

              Le mot clé static ne rend pas la variable j valable pour tout le programme ?

              Non, il étend la portée de la variable au fichier qui la contient.
              De fait, elle ne peut plus alors être allouée sur la pile.

      • [^] # Re: Décomposer les opérations

        Posté par (page perso) . Évalué à 2.

        Note: pour cet exemple il aurait été plus propre de nommer différemment les variables locales entre les fonctions, car ce n'est pas un problème de portée de nom.

        Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

        • [^] # Re: Décomposer les opérations

          Posté par . Évalué à 2.

          C'est un exemple de cours, donc je suppose que c'est fait exprès…Pourriez-vous m'expliquer ? Merci :)

          • [^] # Re: Décomposer les opérations

            Posté par (page perso) . Évalué à 2.

            Que la variable dans dix() s'appelle truc ou machin ou i n'a pas d'impact sur le problème qui est un problème de durée de vie.

            Le fait qu'il puisse y avoir différente variables i dans différentes fonctions, avec chacune une portée (visiblité) qui est celle de la fonction, est un peu différent.

            Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

  • # Compilation ?

    Posté par . Évalué à 1.

    As-tu essayé de le compiler et de l'exécuter ?
    ça devrait merder à la dernière ligne, car l'adresse pointée par p pointe sur une variable locale de la fonction "dix", et l'emplacement mémoire occupé par cette variable est libéré lorsqu'on sort de la fonction. C'est très mal de faire ça…

    Pour la petite anecdote, j'ai eu ce genre de bug récemment, et je l'ai résolu en faisant pipi… :) :) :)

    C'est sans doute un exercice pour comprendre la portée des variables non ?

  • # merci

    Posté par . Évalué à 2.

    Merci à tous de vos réponses claires, j'ai compris, c'est parfait :-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.