• # 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  (site web personnel) . É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 dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

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

        Posté par  (site web personnel) . É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 dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

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

          Posté par  (site web personnel) . É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 dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

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

          Posté par  . Évalué à 3.

          Qu'est-ce qui ne fonctionne pas ? Avec ton code modifié, la valeur de i à la fin du programme est prévisible.

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

          Posté par  . Évalué à 2.

          pourquoi dis-tu que ça ne marche pas ? j'ai essayé, on obtient i=10 à la fin ce qui me semble normal.
          tu t'attendais à quoi ?

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

          Posté par  (site web personnel) . É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  (site web personnel) . Évalué à 2. Dernière modification le 31 octobre 2019 à 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  (site web personnel) . Évalué à 2. Dernière modification le 30 octobre 2019 à 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 dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

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

          Posté par  . Évalué à 2. Dernière modification le 30 octobre 2019 à 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  (site web personnel) . É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  . Évalué à 4. Dernière modification le 30 octobre 2019 à 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  (site web personnel) . É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 dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

  • # 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 ?

  • # Re: merci

    Posté par  . Évalué à 0.

Suivre le flux des commentaires

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