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

C Discussion :

Pointeurs valeurs inattendues


Sujet :

C

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 47
    Par défaut Pointeurs valeurs inattendues
    Bonjour,

    J'ai un problème avec le programme suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    main()
       {
          int A = 1;
          int B = 2;
          int C = 3;
          int *P1, *P2;
          P1=&A;
          P2=&C;
          *P1=(*P2)++;
    	printf("%d\n",A);
    }
    D'après moi A=4 et C reste égal à 3.
    En effet *P1=(*P2)++; équivaut à :
    A=C+1;
    donc A=4 et C ne change pas...
    Pouvez-vous m'expliquer pourquoi on obtient A=3 et C=4 ?


    Merci par avance

  2. #2
    Invité(e)
    Invité(e)
    Par défaut
    Bonjour,
    ++ a la priorité sur =, mais ++ est un post incrément : il s'effectuera après l'instruction dans laquelle il est.

    Donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Pas 1 : *P1 = *P2; -> A = 3;
    Pas 2 : *P2++; -> C =4;

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 47
    Par défaut
    salut mabu,
    mais ici, il n'y a pas de boucle for ou while ?
    Moi je pensais que i++ était équivalent à i+1 (notre prof nous à dis ça un jour)
    peut-être que ce n'est pas tout le temps valable ^^

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    mais ici, il n'y a pas de boucle for ou while ?
    Quel rapport ?

    Moi je pensais que i++ était équivalent à i+1 (notre prof nous à dis ça un jour)
    Non, l'opérateur ++ incrémente de 1 la variable (ou le pointeur, si c'en est un).

    Les deux instructions sont équivalentes :

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 419
    Par défaut
    i++ n'est pas équivalent à i+1.

    Ce serait équivalent à une fonction qui fait ceci:
    Code C : 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
    int PostIncrementInt(int *pi)
    {
    	int ret = *pi;
    	*pi = *pi + 1;
    	return ret;
    }
     
    int main(void)
    {
    	int a = 1;
    	int b;
    	b = PostIncrementInt(&a);
     
    	printf("%d %d\n", a, b);
    	return 0;
    }
    On voit avec cette traduction que l'opérateur ++ retourne l'ancienne valeur de i.

    Voyons ce que ça donnerait dans ton code:
    Code C : 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
    int PostIncrementInt(int *pi)
    {
    	int ret = *pi;
    	*pi = *pi + 1;
    	return ret;
    }
     
    int main(void)
    {
    	int A = 1;
    	int B = 2;
    	int C = 3;
    	int *P1 = &A;
    	int *P2 = &C;
     
    	*P1 = PostIncrementInt(&(*P2)); /* ou plus simplement PostIncrementInt(P2) */
    	printf("%d %d %d\n", A, B, C);
    	return 0;
    }
    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.

  6. #6
    Membre confirmé Avatar de fomazou
    Inscrit en
    Mars 2004
    Messages
    220
    Détails du profil
    Informations forums :
    Inscription : Mars 2004
    Messages : 220
    Par défaut
    Moi je pensais que i++ était équivalent à i+1 (notre prof nous à dis ça un jour)
    peut-être que ce n'est pas tout le temps valable ^^


    ça ne serai pas pour le bohneur des programmeur qu'une expression change de sens selon l'endroit.

    i++ n'est même pas équivalent à i=i+1 on peut dire pseudoequivalent. puisque i++ et ++i sont différents et pour incrémente tous de 1 comme i=i+1.
    la diff est que i++ et ++i sont des expressions(donc peuvent faire partir d'une instruction) l'un est évaluer avant d'être incrémenté et l'autre incrémenté avant d'être évalué

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 47
    Par défaut
    Merci à vous, je vais étudier tout ça

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 47
    Par défaut
    Ça y est j'ai compris la post-incrémentation,mais j'obtiens toujours des valeurs inattendues dans le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    main()
    {
     
    	int A[50];
    	int *p1,*p2;
    	int N;
    	int temp;
    	printf("combien d'element aura le tableau (max:50)");
    	scanf("%d",&N);
    	/*affichage*/
    	for (p1=A;p1<A+N;p1++)
    		printf("resultat %d %d  %d\n\n",p1-A,p1,A);
    }
    voici ce que j'obtiens :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
                  p1-A        p1        A
     
    resutat       0      1244828   1244828  (ça fonctionne)
    resultat      1       1244832   1244828  (ça fonctionne plus)
    resultat      2       1244836   1244828
    c'est difficile

  9. #9
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par gbsatti Voir le message
    voici ce que j'obtiens :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
                  p1-A        p1        A
     
    resutat       0      1244828   1244828  (ça fonctionne)
    resultat      1       1244832   1244828  (ça fonctionne plus)
    resultat      2       1244836   1244828
    c'est difficile
    Oui et ? Qu'est-ce qui te choque dans le résultat ?

    Au passage, pour afficher une adresse il ne faut pas utiliser %d mais %p (et convertir le pointeur en void*)

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 47
    Par défaut
    ce qui me choque c'est que si l'on fait :
    1244832-1244828, on ne trouve plus 1 ...

  11. #11
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    Quand on incrémente de 1 un pointeur, cela ajoute en fait la taille du type de l'objet pointé.
    Comme là, il s'agit d'un int, cela ajoute 4 à chaque fois qu'on incrémente de 1.

    Dans ton tableau, tu as 50 éléments de type int.
    Si tu as un pointeur qui pointe sur le 1er élement d'un tableau, et que tu l'incrémente de 1, il va pointer sur le second élement du tableau. Or, un élément a une certaine taille, qui dépend du type de donnée. Donc, pour accéder à chaque élément suivant, on ajoute la taille du type.
    Ce système de calcul entre dans le domaine de l' "arithmétique des pointeurs".

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 47
    Par défaut
    merci , mais dans ce cas pourquoi à t-on p1-A=1 ?

  13. #13
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 104
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 104
    Par défaut
    merci , mais dans ce cas pourquoi à t-on p1-A=1 ?
    Car la soustraction de deux pointeurs pointant vers des éléments d'un même tableau renvoie un entier signé correspondant à la différence des deux indices correspondants, c'est-à-dire au nombre d'éléments qui les sépare. Ce qui est le cas de ton exemple.

    Par contre, si tu soustraits ces mêmes pointeurs castés tous les deux en un autre type identique (i.e. pointeur sur void, sur char, etc), cela modifie donc le résultat car le résultat dépend aussi de la longueur du type.

    Maintenant, si on soustrait deux pointeurs pointant ailleurs que sur un tableau, il me semble que le comportement est indéfini. Je lutte un peu pour comprendre les explications données dans la norme ISO/IEC 9899:1990 à ce sujet, à la fin du chapitre 6.3.6.
    Si quelqu'un pouvait confirmer ou infirmer ?...

  14. #14
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    fomazou :
    i++ n'est même pas équivalent à i=i+1 on peut dire pseudoequivalent. puisque i++ et ++i sont différents et pour incrémente tous de 1 comme i=i+1.
    la diff est que i++ et ++i sont des expressions(donc peuvent faire partir d'une instruction) l'un est évaluer avant d'être incrémenté et l'autre incrémenté avant d'être évalué.
    Cela ne forme pas une différence entre i=i+1, i++ et++i :
    i=i+1 est aussi une expression.
    C'est pour cela qu'on peut écrire j = i = i+1;

    jeroman :
    Maintenant, si on soustrait deux pointeurs pointant ailleurs que sur un tableau, il me semble que le comportement est indéfini. Je lutte un peu pour comprendre les explications données dans la norme ISO/IEC 9899:1990 à ce sujet, à la fin du chapitre 6.3.6.
    C'est vrai qu'en plus, le résultat n'aurait pas grand sens. (ils doivent en plus pointer sur des éléments d'un même tableau)
    Toutefois, les pointeurs peuvent pointer sur l'élément (fictif) situé après le tableau (qui n'appartient pas en fait au tableau). Donc, il est possible d'écrire en toute sécurité si tab est un tableau de N élements
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    for(p = tab; p < tab+N; p++)....
    Naturellement, on ne peut écrire *(tab+N) ni par conséquent tab[N]

  15. #15
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 69
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par gbsatti Voir le message
    J'ai un problème avec le programme suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    main()
       {
          int A = 1;
          int B = 2;
          int C = 3;
          int *P1, *P2;
          P1=&A;
          P2=&C;
          *P1=(*P2)++;
    	printf("%d\n",A);
    }
    D'après moi A=4 et C reste égal à 3.
    En effet *P1=(*P2)++; équivaut à :
    A=C+1;
    donc A=4 et C ne change pas...
    Pouvez-vous m'expliquer pourquoi on obtient A=3 et C=4 ?
    Ton code est incomplet...
    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
     
     
    -------------- Build: Debug in hello ---------------
     
    Compiling: main.c
    Linking console executable: bin\Debug\hello.exe
    C:\dev\hello\main.c:2: warning: return type defaults to `int'
    C:\dev\hello\main.c:2: warning: function declaration isn't a prototype
    C:\dev\hello\main.c: In function `main':
    C:\dev\hello\main.c:10: warning: implicit declaration of function `printf'
    C:\dev\hello\main.c:4: warning: unused variable `B'
    C:\dev\hello\main.c:11: warning: control reaches end of non-void function
    Output size is 17.05 KB
    Process terminated with status 0 (0 minutes, 1 seconds)
    0 errors, 5 warnings
    Ceci est complet, mais incorrect :
    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
     
    #include <stdio.h>
     
    int main (void)
    {
       int A = 1;
       int B = 2;
       int C = 3;
       int *P1, *P2;
       P1 = &A; /* OK */
       P2 = &C; /* OK */
       *P1 = (*P2)++; /* Comportement indéfini. on ne sait pas quand est evalue le ++ */
       printf ("%d\n", A);
     
       return 0;
    }
    Le code correct est probablement :
    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 <stdio.h>
     
    int main (void)
    {
       int A = 1;
       int B = 2;
       int C = 3;
       int *P1, *P2;
       P1 = &A; /* OK */
       P2 = &C; /* OK */
       (*P2)++;
       *P1 = *P2;
       printf ("%d\n", A);
     
       return 0;
    }
    qui donne 4.

    Nota : B n'est pas utilisée.

    Pour avoir un comportement clair, le mieux est de n'utiliser ++ ou -- que dans une expression unaire (i++; --i; etc.)

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 419
    Par défaut
    Sauf qu'à ma connaissance, ce comportement-là n'est PAS indéfini: Il n'y a qu'un seul ++, la même variable n'est pas modifiée deux fois dans l'expression, donc le résultat est garanti.
    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.

  17. #17
    Expert confirmé
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
       *P1 = (*P2)++; /* Comportement indéfini. on ne sait pas quand est evalue le ++ */
    Citation Envoyé par Médinoc Voir le message
    Sauf qu'à ma connaissance, ce comportement-là n'est PAS indéfini: Il n'y a qu'un seul ++, la même variable n'est pas modifiée deux fois dans l'expression, donc le résultat est garanti.
    +1 (sauf si P1 et P2 pointent sur le même objet)

  18. #18
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    47
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 47
    Par défaut
    Merci à vous j'ai compris ça
    a+

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 03/05/2012, 08h37
  2. Paramètres de fonction : pointeurs ou valeurs ?
    Par Xandar dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 30/11/2005, 16h50
  3. Réponses: 3
    Dernier message: 20/09/2005, 08h59
  4. Réponses: 3
    Dernier message: 11/08/2005, 15h54
  5. Echange de valeurs par pointeur et réf
    Par smag dans le forum C++
    Réponses: 6
    Dernier message: 01/03/2005, 18h39

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