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 :

affectation et pre-incrémentation, différence de compilateur


Sujet :

C

  1. #1
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Par défaut affectation et pre-incrémentation, différence de compilateur
    Bonjour à tous,

    je ne parviens pas à trouver si c'est un undefined behavior ou non.
    Voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <stdio.h>
     
    int main( void )
    {
            int tab[2] = {10, 20};
            int i = 1;
            tab[i] = tab[--i];
            printf( "%d, %d\n", tab[0], tab[1] );
            return 0;
    }
    En compilant avec gcc v4.2.0 : gcc -std=c99, voici la sortie : "10, 10"
    En compilant avec xlC v11.01 ou 13.01, voici la sortie : "10, 20"
    Le système est IBM Unix AIX.

    xlC semble d'abord effectuer la pré-incrémentation puis l'affectation.

    Avez-vous des explications sur cette différence ?
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par défaut
    Je ne connais pas ce compilo xlC mais je dirais que gcc a raison.
    tab[i] déplace ça sur tab[1], auquel on affecte tab[--i] qui devrait faire la décrémentation de i, et donc assigner tab[0] à tab[1]
    Sur un code aussi court tu devrais comparer le code asm généré, on dirait que xlC assigne tab[1] à tab[1] avant de décrémenter i, en fait une post-décrémentation de i et non une pré-décrémentation, ce qui serait donc un bug de leur part IMO.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Et moi je vote UB.

    D'après la norme (draft N1570), en 6.5§2 :

    If a side effect on a scalar object is unsequenced relative to either a different side effect
    on the same scalar object or a value computation using the value of the same scalar
    object, the behavior is undefined. If there are multiple allowable orderings of the
    subexpressions of an expression, the behavior is undefined if such an unsequenced side
    effect occurs in any of the orderings.
    Or il n'y a pas de sequence point au sein d'une affectation, si l'on en croit 6.8§4 (on apprendra en 6.8.3§2 qu'une affectation est un expression statement) :

    [...] Each of the following is a full expression: an initializer that is not part of a compound
    literal; the expression in an expression statement; [...] There is a sequence point between the evaluation of a full expression and the
    evaluation of the next full expression to be evaluated.
    Alors certes on peut toujours interpréter à loisir mais au mieux l'ordre d'évaluation de l'effet de bord et de l'affectation est indéterminé, c'est une raison suffisante pour bannir ce genre de chose.

  4. #4
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Par défaut
    Ok pour l'undefined behavior et la référence à la norme.
    Je ne ferais plus jamais comme ça ;-)
    Merci !
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  5. #5
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 838
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par dinobogan Voir le message
    En compilant avec gcc v4.2.0 : gcc -std=c99, voici la sortie : "10, 10"
    Bonjour

    De mon côté, j'ai compilé avec gcc v4.9.2 (debian8, gcc -std=c99) et j'ai eu "10, 20". Sans dec, j'aurais moi aussi parié sur "10, 10" !!!
    Surtout que si je change un seul détail...
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <stdio.h>
     
    int main( void )
    {
            int tab[2] = {10, 20};
            int i = 1;
            tab[1] = tab[--i];
            printf( "%d, %d\n", tab[0], tab[1] );
            return 0;
    }
    ... j'obtiens bien "10, 10".
    C'est bien le fait d'intéger "i" dans la même expression que "--i" qui met la zone !!!

    Essayons maintenant de jouer avec ce "i"...

    Ici...
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <stdio.h>
     
    int main( void )
    {
    	int tab[2] = {10, 20};
    	int i = 1;
    	tab[i=i] = tab[--i];
    	printf( "%d, %d\n", tab[0], tab[1] );
    	return 0;
    }
    ... j'obtiens de nouveau "10 20"

    Mais ici...
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <stdio.h>
     
    int main( void )
    {
    	int tab[2] = {10, 20};
    	int i = 1;
    	int j;
    	tab[j=i] = tab[--i];
    	printf( "%d, %d\n", tab[0], tab[1] );
    	return 0;
    }
    ... j'obtiens "10 10" !!!

    Intéressant non ?
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Membre très actif
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    550
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 550
    Par défaut
    Bonjour,

    Je ne suis pas tout à fait d’accord à la réponse donnée plus haut sans pour autant dire réellement d’où vient le problème et conclure directement sur ce que dit la norme.

    Le problème est effectivement dû à une violation des règles de séquençage et d’accès ; et non a un manque de points de séquence au sein d’une affectation et cela vient plus précisément du fait que la même variable (votre variable i), est modifiée plus d'une fois au sein d’une même expression. Ainsi donc, la valeur finale de votre variable i est indéterminé au moment où l’on exécute cette instruction. La norme à ce sujet peut tout à fait spécifier l'un des deux comportements possibles tab[i]/tab[--i] , mais comme le résultat de la variable ne peut être défini/déterminer en cours d’exécution au sein de cette expression tab[i] = tab[--i] alors tout type d’instruction de la sorte (Voir également du style i= i++); est considéré comme étant un comportement indéterminé et c’est le cas dans votre exemple : la variable i est bien ambigu.

    N’oublions pas que la façon dont votre compilateur évalue la chose est totalement laissée à la discrétion des concepteurs de votre compilateur pour différentes raisons; par exemple : le langage de programmation C garantie que les opérateurs && || ? : dont l’opérateur, le plus à gauche, sera évalué en premier ; quant aux autres, cela n’est pas spécifié et donc chaque compilateur fait à sa sauce. D’ailleurs, le résultat fourni par plusieurs de vos compilateurs reflète bien la manière de comment, chaque compilateur implémente la choses.

    On peut remarquer que votre expression sur un compilateur xlC fonctionne parce que les concepteurs ont probablement défini des priorités sur l'opérateur/inséré des points de séquences volontairement sur ce genre d’expression de telles manières, à obtenir un comportement bien défini et déterminé. En clair, le compilateur xlC peut tout à fait admettre comme vous l'avez mentionné; procéder d’abord à une pré-incrémentation puis en une affectation. (peut-être à cause des règles de priorité et associativité/tout simplement parce que le compilateur a placé un point de séquence entre les deux instructions.) quand a GCC cela n'est pas le cas, il implémente les choses de manière différente de telle sorte a respecté la norme. GCC va donc émettre un avertissement s’il rencontre ce genre d’instruction pour vous dire, que vous n’avez pas séquencé les instructions et qu'on même temps vous accéder ou modifier la valeur de la variable ‘i’ et donc le résultat de ’i’ ne peut être garantie/ déterminer.

    Ceci dit, on peut également se poser la question si le compilateur xlc respecte la norme ou comment il arrive à déterminer la valeur de i sur une telle expression. la ou je rejoins @Bousk c'est sur la lecture/comparaison du code au niveau langage assembleur de votre architecture.

    À bientôt,

  7. #7
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    La norme est respectée aussi bien par xlc que par gcc. Justement la norme dit clairement qu'il s'agit d'un comportement imprévisible. Les deux traitent bien des données et les deux obtiennent bien un résultat, et d'ailleurs un compilateur pour lequel le code planterait dans ce cas respecterait lui aussi la norme.
    gcc signale le problème, il en fait un peu plus que demandé par la norme.

  8. #8
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 838
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par dalfab Voir le message
    La norme est respectée aussi bien par xlc que par gcc. Justement la norme dit clairement qu'il s'agit d'un comportement imprévisible.
    Oui ça c'est un peu facile... "comportement imprévisible" signifie "tout peut arriver". Autrement dit, tout compilateur qui produit n'importe quoi dans un cas de ub respecte la norme puisque justement dans ce cas la norme dit "ici il peut se passer n'importe quoi"...

    Citation Envoyé par dalfab Voir le message
    gcc signale le problème, il en fait un peu plus que demandé par la norme.
    Moi je ne l'ai pas eu (options -Wall -Werror). Quelle option faut-il rajouter pour obtenir un retour ???
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  9. #9
    Membre très actif
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    550
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 550
    Par défaut
    @dalfab: Bonjour, Les deux compilateurs respecte la norme mais implémentent peut êtres les choses différemment au niveau de l’ordre d’évaluation des opérandes, car (sauf erreur de ma part) il existe aucune règle sur l’ordre des évaluations des opérandes à l’exception de && || ? : que tout les compilateurs respecte; ce qui est même précisé dans la documentation du compilateur GCC pages 75 (section 3.8).

    D'un autre côté, le champ libre est laissé à tout concepteur de compilateur de faire ce qu’il veut sur l'ordre d'évaluation des opérandes autres que ceux imposer par la norme. GCC se base sur la norme certes, mais considère purement et simplement que l'expression plus haut a un comportement non défini parce qu'une variable est modifiée plus d'une fois au sein d’une même expression, mais aussi parce que tout programme écrit de façon à dépendre des ordres d’évaluation des opérandes sont considérés comme étant incorrect ou ayant un résultat indéterminé.

    Donc, tout programme qui dépend de l’ordre d’évaluation des opérandes ne fonctionne pas forcement de la même façon sur un compilateur a un autre, même si les compilateurs respectent la norme. Car, sur ce points les concepteurs ont la possibilité d’imposer leur façon d’évaluer les opérandes. En clair si je ne me trompe pas et corriger moi si c'est la cas, au niveau du compilateur, les concepteurs peuvent opter pour une méthode d’ordre d’évaluation basée sur l’arbre syntaxique/sémantiques voire d’ordre fixe et donc même si xlc respecte la norme elle procède peut être différemment en ce qui concerne l’ordre d’évaluation des opérandes par rapport à GCC ce qui donnera forcement un résultat contraire a GCC voire autres. c’est une des raisons qu’il faut lire la documentation du compilateur que l’on utilise pour en savoir un peut plus.


    @Sve@r, pour l’option de votre compilateur (chez moi GCC 8.1) il vous faut compiler avec cette option -Wsequence-point et pour désactiver le warning -Wunsequence-point

    à bientôt

  10. #10
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 838
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 838
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par sambia39 Voir le message
    car (sauf erreur de ma part) il existe aucune règle sur l’ordre des évaluations des opérandes
    Me semble que pour les opérateurs binaires l'évaluation se fait de la gauche vers la droite (ex: 2+3+4 commence par calculer 2+3=5 puis calcule ensuite 5+4) et pour les opérateurs unaires et opérateurs d'affectations elle se fait de la droite vers la gauche (ex: X=2; Y=3; X *= Y += 1 on commence par faire Y+=1 (=> Y=4) puis X*=4 (=> X=8)...

    Citation Envoyé par sambia39 Voir le message
    pour l’option de votre compilateur (chez moi GCC 8.1) il vous faut compiler avec cette option -Wsequence-point et pour désactiver le warning -Wunsequence-point
    Merci ça fonctionne. Mais j'ai dit une bêtise hier car j'avais pas activé mes options (elles sont dans mes Makefile c'est pour ça que j'ai dit qu'elles sont activées mais hier j'ai fais mes tests dans "/tmp" qui n'a pas de Makefile) car en fait "-Wall" l'active aussi en réalité...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

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

Discussions similaires

  1. Réaliser interpréteur (différence avec compilateur)
    Par Reverse_ dans le forum Générateurs de compilateur
    Réponses: 3
    Dernier message: 03/07/2017, 16h21
  2. Réponses: 4
    Dernier message: 18/03/2010, 12h48
  3. Runtime et différences entre compilateurs
    Par dfg dans le forum Langages de programmation
    Réponses: 16
    Dernier message: 08/06/2008, 17h00
  4. Réponses: 2
    Dernier message: 08/11/2007, 12h34
  5. Réponses: 4
    Dernier message: 01/02/2007, 14h13

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