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 :

Priorité des opérateurs * et ++


Sujet :

C

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut Priorité des opérateurs * et ++
    Bonjour,

    Je lisais hier soir et ce matin le chapitre 5 sur les pointeurs et les tableaux dans le K&R (j'ai la 2e édition en français, chez Dunod. Je citerai les pages dans la suite, si vous avez une édition différente, dommage...).

    A la page 93, ils expliquent que (*p)++ incrémente le contenu du pointeur p et que les parenthèses sont obligatoires sous peine d'incrémenter le pointeur p. En effet, les opérateurs unaires s'évaluent de la droite vers la gauche.

    Ensuite, à la page 104, ils disent que *p++ = val; met val sur la pile. J'en déduis que le contenu de p prend la valeur de val et que p est incrémenté.

    Or, j'ai l'impression que les deux codes sont contradictoires. En suivant la règle du 1er code, j'aurais tendance à penser que le code 2 revient à :
    • incrémenter le pointeur p (car l'opérateur unaire ++ est à droite et s'évalue en premier)
    • le contenu de p ainsi décalé prend la valeur de val


    Qu'est ce que j'ai mal compris ?

  2. #2
    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
    Or, j'ai l'impression que les deux codes sont contradictoires. En suivant la règle du 1er code, j'aurais tendance à penser que le code 2 revient à :
    incrémenter le pointeur p (car l'opérateur unaire ++ est à droite et s'évalue en premier)
    le contenu de p ainsi décalé prend la valeur de val
    Non, cela signifie que l'opérateur ++ s'applique à p et non à *p. Les règles de priorité et d'associativité déterminent à quoi s'applique l'opérateur, cela n'a pas de rapport avec l'ordre d'évaluation des opérandes (qui sauf quelques exceptions ( opérateurs (virgule) , &&, ||, ?:) n'est pas déterminé).
    Comme il s'agit ici d'un post incrément, p sera modifié après l'évaluation de p.
    Donc on a
    -1 Evaluation de la valeur de p
    -2 Evaluation de val
    -3 Incrément du contenu de p
    -4 Mise de val à l'adresse évaluée en 1

    Sur l'ordre des étapes, on sait seulement que 3 est postérieur à 1 et 4 est postérieur à 1 et 2. On peut avoir 1-2-3-4 ou 2-1-3-4 ou 1-2-4-3 ou 2-1-4-3 ou ...
    Mais comme ici le résultat obtenu sera le même quel que soit l'ordre, l'expression est bien formée.

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Effectivement avec les étapes détaillées, c'est beaucoup plus clair ! Merci ! En fait, c'est (surtout) l'étape 4 qui me manquait. On garde la valeur trouvée à l'étape 1.

    Si j'ai bien compris :

    implique que a contient le contenu de p avant incrémentation et que p est incrémenté après exécution.

    implique que le contenu de p est incrémenté et que a contient ce nouveau contenu.

  4. #4
    Membre émérite
    Avatar de bpy1401
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2003
    Messages
    511
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2003
    Messages : 511
    Par défaut
    Bonjour à tous,

    un conseil , ne faite jamais confiance à votre connaissance de la priorité des opérateurs, moi, je préfère la forcer avec les parenthèse. Ca évite les bug, et facilite la relecture.
    Page sur Developpez : http://pbriand.developpez.com

  5. #5
    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
    Dans les deux cas, ce n'est pas le contenu de p qui est placé dans a, mais la valeur qui est à l'adresse contenue dans p

    Le premier équivaut à a = *(p++);. a contiendra ce qui est à l'adresse contenue dans p et le contenu de p est incrémenté (donc p contient alors l'adresse de l'élément suivant)

    Le second a = (*p)++;. a contiendra la valeur qui est à l'adresse contenue dans p et cette valeur est incrémentée

    Un exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int main(void)
    {
      int tab[] = {1,5,10,15};
      int * p = tab;
      int a;
     
      a = *p++;    // a = 1 , tab inchangé, p == tab+1
      a = *p;      // a = 5 , On vérifie p == tab+1
      a = (*p)++;  // a = 5 , *p == tab[1]== *(tab+1) a changé : tab = {1,6,10,15}, p inchangé : p==tab+1
      a = *p;      // a = 6 , On vérifie p == tab+1
      return 0;
    }

  6. #6
    Membre chevronné
    Homme Profil pro
    Enseignant
    Inscrit en
    Janvier 2012
    Messages
    190
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Janvier 2012
    Messages : 190
    Par défaut
    @bpy1401 : il faut aussi pouvoir lire le code des autres (et parfois la réciproque).

    j'ai trouvé dans le draft de C99
    EXAMPLE 7 The grouping of an expression does not completely determine its evaluation. In the following fragment
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #include <stdio.h>
    int sum;
    char *p;
    /* ... */
    sum = sum * 10 - '0' + (*p++ = getchar());
    the expression statement is grouped as if it were written as
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));
    but the actual increment of p can occur at any time between the previous sequence point and the next sequence point (the ;), and the call to getchar can occur at any point prior to the need of its returned value.
    ça me laisse songeur ...

    A+

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Ce qui sous-entend que mettre p++ entre parenthèses ne garantit pas que ce sera incrémenté avant qu'on évalue p ?

    Ce sujet et aussi les écritures (ultra compactes, sans accolades aux IF et FOR, l'utilisation massive de ++ et --, etc. ) que j'ai lu dans le K&R m'ont confirmé dans mon opinion qu'un code sur plusieurs lignes est plus sûr et plus compréhensible qu'un code compact. A moins d'extrêmement bien maitriser les finesses du langage et que les gens qui liront votre code également...

  8. #8
    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
    La priorité et l'associativité des opérateurs permettent de savoir quelles sont leurs opérandes, mais ne dit rien sur l'évaluation de ces opérandes.
    Dans l'expression a=b+(c-d) , on sait que les opérandes du = sont a et l'expression b+(c-d), ceux du + sont b et l'expression c-d, ceux du - sont c et d.

    Reste à savoir comment sont évaluées ces différentes opérandes.
    L'ordre d'évaluation des opérandes est (sauf quelques exceptions) indéterminé. C'est également le cas des arguments lors de l'appel d'une fonction. Si on veut imposer un ordre d'évaluation, il faut passer par une variable intermédiaire avec l'expression qui doit être évaluée en premier.
    Exemple :
    Dans l'expression a = getchar()-getchar(), le résultat est indéfini : il dépend de l'ordre d'évaluation entre les deux getchar(). Si l'opérateur entre '0' et '1', le résultat peut être -1 si le premier getchar() est évalué en premier ou 1 si c'est le second.
    Pour avoir un résultat déterminé, on peut écrire, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    b = getchar();
    a = b-getchar();
    Si il y a un effet de bord, (comme le ++, ou le getchar()) il peut intervenir à tout moment avant le point de séquencement suivant de l'expression. L'expression tab[i] = i++; a un résultat indéterminé et est donc mal formée : on ne sait pas si l'effet de bord du ++ va avoir lieu avant ou après l'évaluation du i de l'opérande à gauche du =.

    Une expression qui comporte deux fois (ou plus) l'évaluation d'une même variable et qui modifie (au moins une fois) cette variable peut (ce n'est pas obligatoire) être mal formée.

  9. #9
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Ce qui sous-entend que mettre p++ entre parenthèses ne garantit pas que ce sera incrémenté avant qu'on évalue p ?

    Ce sujet et aussi les écritures (ultra compactes, sans accolades aux IF et FOR, l'utilisation massive de ++ et --, etc. ) que j'ai lu dans le K&R m'ont confirmé dans mon opinion qu'un code sur plusieurs lignes est plus sûr et plus compréhensible qu'un code compact. A moins d'extrêmement bien maitriser les finesses du langage et que les gens qui liront votre code également...


    Absoument.

    Et on arrive extrêmement rapidement à ce qui s'appelle de ''l'obsfuscation"..

    Peut-être l'auteur est-il très fier de son code, mais non seulement le résultat en assembleur est le même que si on avait décortiqué les lignes, et de plus c'est illisible pour toute personne suivante - et souvent même par l'auteur même, à 6 mois d'intervalle...

    Donc une régle simple est : sauf cas des boucles, où il est "normal" d'avoir une écriture i++, en général toute opération sur les pointeurs est préférablement écrite en clair : p = p + 1 à l'endroit où elle doit l'être..

    Et à éviter sont les incrémentations de pluseurs pointeurs dans le même "for' ou "while"..

    Et de plus toujours mettre les parenthèses -pas seulement lors d'opérations sur des varaibles contenant des pointeurs, mais de manière générale dans toute équation : suivre la logique et ne JAMAIS présumer des précédences ou non des opérateurs...

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

Discussions similaires

  1. Priorité des opérateurs % et ++
    Par G3G3 dans le forum Débuter
    Réponses: 16
    Dernier message: 12/02/2008, 09h40
  2. [OCaml & F#] Priorité des opérateurs
    Par SpiceGuid dans le forum Caml
    Réponses: 7
    Dernier message: 01/01/2008, 15h00
  3. priorité des opérateurs
    Par new_wave dans le forum SQL
    Réponses: 13
    Dernier message: 08/11/2007, 21h44
  4. Priorité des opérateurs
    Par neuromencien dans le forum Langage
    Réponses: 3
    Dernier message: 14/05/2007, 17h06
  5. Réponses: 3
    Dernier message: 31/08/2006, 10h39

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