Affectation et opérateurs logiques

Le
Romuald
Bonjour,

L'autre jour j'ai pu écrire, en pensant que c'était tout à fait lég=
al,
quelque chose comme :

v = a + (p != NULL) ? 0 : 1;

J'ai été surpris de voir que le résultat n'était pas celui que
j'attendais. Je me suis mis à chercher (dans la norme C99) où était l=
e
comportement indéfini là-dedans et je pense l'avoir trouvé :

A propos de l'affectation : "The side effect of updating the stored
value of the left operand shall
occur between the previous and the next sequence point." et "If an
attempt is made to modify
the result of an assignment operator or to access it after the next
sequence point, the
behavior is undefined."
Sachant que, à propos de l'opérateur ternaire : "The first operand is
evaluated; there is a sequence point after its evaluation.".

Après l'évaluation de "p != NULL" l'affectation n'est pas terminée =
et
pourtant c'est là le "sequence point" suivant, d'où le comportement
indéfini.

Je pense que j'ai bon ?

Vu comment les opérateurs de comparaison && et || fonctionnent ça
voudrait dire qu'on ne peux pas affecter à une variable le résultat de
ces opérateurs ? Tout porte à le croire mais j'ai du mal à comprendre
pourquoi le langage l'interdirait* ?

Merci d'avance !

* Ok, c'est juste "comportement indéfini", c'est pas "interdit", mais
on se comprend !
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 3
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
espie
Le #23041441
In article Romuald
Bonjour,

L'autre jour j'ai pu écrire, en pensant que c'était tout à fait légal,
quelque chose comme :

v = a + (p != NULL) ? 0 : 1;

J'ai été surpris de voir que le résultat n'était pas celui que
j'attendais. Je me suis mis à chercher (dans la norme C99) où était le
comportement indéfini là-dedans et je pense l'avoir trouvé :



Sans contexte, ca va etre un peu dur. C'est quoi les declarations de
v, a, p dans ton programme ?

Et d'ou sort NULL ? c'est le NULL des entetes standards, ou un NULL que
tu as defini tout seul ?
Romuald
Le #23041561
On 21 jan, 18:34, (Marc Espie) wrote:
Sans contexte, ca va etre un peu dur. C'est quoi les declarations de
v, a, p   dans ton programme ?

Et d'ou sort NULL ? c'est le NULL des entetes standards, ou un NULL que
tu as defini tout seul ?



Voilà un programme complet :

#include
int main(void) {
int v;
int a = 1;
int* p = NULL;

v = a + (p != NULL) ? 0 : 1;

printf("%dn", v);

return 0;
}

(J'ai compile avec GCC 4.4.3)

J'avais donne aucune info de ce genre car comme on peut le voir ça va
pas chercher aussi loin que d'aller definir son propre NULL :-).
Le probleme me semble independant des types qu'on donne, c'est juste
l'utilisation de l'operateur ternaire la au milieu.
Pierre Maurette
Le #23041661
Romuald, le 1/21/2011 a écrit :
On 21 jan, 18:34, (Marc Espie) wrote:
Sans contexte, ca va etre un peu dur. C'est quoi les declarations de
v, a, p   dans ton programme ?

Et d'ou sort NULL ? c'est le NULL des entetes standards, ou un NULL que
tu as defini tout seul ?



Voilà un programme complet :

#include
int main(void) {
int v;
int a = 1;
int* p = NULL;

v = a + (p != NULL) ? 0 : 1;

printf("%dn", v);

return 0;
}

(J'ai compile avec GCC 4.4.3)

J'avais donne aucune info de ce genre car comme on peut le voir ça va
pas chercher aussi loin que d'aller definir son propre NULL :-).
Le probleme me semble independant des types qu'on donne, c'est juste
l'utilisation de l'operateur ternaire la au milieu.



Je vous suggère de placer des parenthèses pour forcer l'associativité
que vous souhaitez. Là, c'est (a + (p != NULL)) ? 0 : 1) donc ça vaut
toujours 0 sauf pour a == 0 où ça vaut 1.
Il y a une façon plus simple d'écrire ça.

--
Pierre Maurette
Romuald
Le #23041711
On 21 jan, 19:02, Pierre Maurette

Je vous sugg re de placer des parenth ses pour forcer l'associativit
que vous souhaitez. L , c'est (a + (p != NULL)) ? 0 : 1) donc a vaut
toujours 0 sauf pour a == 0 o a vaut 1.
Il y a une fa on plus simple d' crire a.




En effet j'avais pas remarque ça.
J'ai donc voulu dire "a + ((p != NULL) ? 0 : 1)".
Cependant dans les deux cas le probleme du "sequence point" se pose
toujours, non ?
Pierre Maurette
Le #23041731
Romuald, le 1/21/2011 a écrit :
On 21 jan, 19:02, Pierre Maurette

Je vous sugg re de placer des parenth ses pour forcer l'associativit
que vous souhaitez. L , c'est (a + (p != NULL)) ? 0 : 1) donc a vaut
toujours 0 sauf pour a == 0 o a vaut 1.
Il y a une fa on plus simple d' crire a.




En effet j'avais pas remarque ça.
J'ai donc voulu dire "a + ((p != NULL) ? 0 : 1)".
Cependant dans les deux cas le probleme du "sequence point" se pose
toujours, non ?



Non, avec mon gcc qui est au feu, en -stdÉ9, vite fait, ça fait ce
que j'attends. Notez que si a est un int et p un pointeur, votre truc
se remplace par "a + !p".

--
Pierre Maurette
espie
Le #23041721
In article Romuald
On 21 jan, 19:02, Pierre Maurette

Je vous sugg re de placer des parenth ses pour forcer l'associativit
que vous souhaitez. L , c'est (a + (p != NULL)) ? 0 : 1) donc a vaut
toujours 0 sauf pour a == 0 o a vaut 1.
Il y a une fa on plus simple d' crire a.




En effet j'avais pas remarque ça.
J'ai donc voulu dire "a + ((p != NULL) ? 0 : 1)".
Cependant dans les deux cas le probleme du "sequence point" se pose
toujours, non ?



Non, les "sequence points" ne sont importants que si on travaille
plusieurs fois avec la meme variable/le meme emplacement memoire.

C'est pour ca que j'avais demande tes definitions de a, v, p, me demandant
s'il y avait une liaison entre eux.
Antoine Leca
Le #23047571
Romuald écrivit :
L'autre jour j'ai pu écrire, en pensant que c'était tout à fait légal,
quelque chose comme :

v = a + (p != NULL) ? 0 : 1;



mais c'est légal !

J'ai été surpris de voir que le résultat n'était pas celui que
j'attendais.



Ah ! le compilateur n'a pas été assez intelligent pour deviner ta pensée


Je me suis mis à chercher (dans la norme C99) où était le
comportement indéfini là-dedans



... et il n'y en a pas.

et je pense l'avoir trouvé :
"If an attempt is made to modify
the result of an assignment operator or to access it after the next
sequence point, the behavior is undefined."



Sauf qu'ici, l'affectation a lieu en dernier, donc un éventuel
comportement indéfini, concernant l'utilisation de la valeur contenue
dans v avant l'affectation, n'a pas l'occasion de se présenter.

En gros, les comportements indéfinis de ce genre se produisent quand la
même variable (au sens du contenu d'une adresse mémoire) est utilisée à
la fois en lecture et en écriture dans une même sous-expression (une
expression délimitée par des points de séquence); ici chaque variable
n'est utilisée qu'une fois, d'où l'absence de problème (et la question
de Marc, qui suspectait des alias tordus).


Sachant que, à propos de l'opérateur ternaire : "The first operand is
evaluated; there is a sequence point after its evaluation.".

Après l'évaluation de "p != NULL" l'affectation n'est pas terminée et
pourtant c'est là le "sequence point" suivant, d'où le comportement
indéfini.



Non. Les points de séquence de la norme ne servent qu'à créer un ordre
_partiel_ d'évaluation, mais ce sont des points de passages
intermédiaires (qui ne définissent pas l'état complet), pas des étapes.
La véritable étape, c'est plutôt le ; qui termine l'instruction. C'est
pour cela que si tu as la même variable deux fois ou plus entre deux ;
tu peux éventuellement avoir des soucis, /sauf/ si un point de séquence
intermédiaire (d'un opérateur ?:, &&, etc.) est opportunément placé pour
éviter les soucis.


Vu comment les opérateurs de comparaison && et || fonctionnent ça
voudrait dire qu'on ne peux pas affecter à une variable le résultat de
ces opérateurs ?



Si, on peut le faire (même si c'est peu courant).


Antoine
Romuald
Le #23055211
On 24 jan, 10:03, Antoine Leca
Romuald écrivit :
> J'ai été surpris de voir que le résultat n'était pas celui que
> j'attendais.

Ah ! le compilateur n'a pas été assez intelligent pour deviner ta pen sée



J'ai jamais dit que le compilateur s'était trompé.

J'imagine bien que vous avez raison, mais je n'arrive pas à déduire
tout ce que vous avez dit à partir notamment des deux premières
citations de mon premier message. Je n'arrive pas à les lire "de votre
manière".

Je prends volontiers des explications pas à pas, pourquoi pas sur un
exemple qui ne respecte pas ces règles en particulier (celles des deux
citations), pour achever de me convaincre.

Merci d'avance.
Jean-marc
Le #23055561
Romuald wrote:
On 24 jan, 10:03, Antoine Leca
Romuald écrivit :





Hello,

J'ai jamais dit que le compilateur s'était trompé.

J'imagine bien que vous avez raison, mais je n'arrive pas à déduire
tout ce que vous avez dit à partir notamment des deux premières
citations de mon premier message. Je n'arrive pas à les lire "de votre
manière".



Oui mais les 2 premières citations du messages ne sont pas utiles dans
ce contexte.

Je prends volontiers des explications pas à pas, pourquoi pas sur un
exemple qui ne respecte pas ces règles en particulier (celles des deux
citations), pour achever de me convaincre.



Ici, c'est tout simple : C'est juste un problème de priorité des opérateurs.

Vous serez d'accord avec le fait que si j'écris :

v = a * b + c , c'est équivalent à v = (a*b) + c;
(car la multiplication est prioritaire sur l'addition).


De façon équivalente, Ici, tout se joue entre la priorité de
l'opérateur ternaire "?:" et la priorité de l'addition.

L'expression (p!=NULL) sera toujours évaluée en premier à cause des
parenthèses.

posons q = (p != NULL);

votre expression devient :

v = a + q ? 0 : 1;

Et bien pour évaluer cette expression, il suffit de connaitre les
priorités respectives de "?:" et de "+".

C'est ici : http://c.developpez.com/cours/bernard-cassagne/node101.php

Et la réponse est : l'addition est prioritaire.

Du coup l'expression est équivalente (= est interprétée comme) :

v = (a + q) ? 0 : 1;

a vaut 1
q est faux et vaut 0
(a + q) = 1

1 est différent de zéro donc vrai

donc v reçoit le premier membre de l'expression "0 : 1" => donc 0


Merci d'avance.



En espérant avoir aidé.

Cordialement,

--
Jean-marc
Romuald
Le #23077041
J'ai bien compris que dans mon exemple il y a un problème de priorité.
Cependant il me sert surtout de prétexte pour tenter de comprendre les
fameuses deux citations, et notamment en quoi le "next sequence point"
de l'affectation n'est pas celui de l'opérateur ?: .

J'y ai encore réfléchis depuis et j'ai trouvé une autre manière de
lire cette phrase ("The side effect of updating the stored value of
the left operand shall occur between the previous and the next
sequence point."). Le "next sequence point" ferait plutôt référence à
celui après l'affectation toute entière (c.à.d. à partir de la fin de
l'opérande de droite), indépendamment de sequence points à l'intéri eur
des opérandes. Donc dans :

v = a + (p != NULL) ? 0 : 1;

la fin de l'affectation est le point-virgule et le "next sequence
point" est justement celui induit par le point-virgule. Il n'y a donc
pas de problème avec cette construction.

Autre exemple, en admettant que l'ordre d'évaluation soit de gauche à
droite, quelque chose comme :

b = 2;
a = (b = 3) + b;

pose problème car l'affectation de la valeur 3 à b n'est tenue de se
faire qu'avant le point-virgule de fin, pas avant l'évaluation du
deuxième b de l'addition.
Et donc :

a = (b = 3) + ((1) ? b : b);

est valide car le "next sequence point" associé à l'affectation (b =
3) est ici celui de l'opérateur ?: donc b aura reçu sa nouvelle valeur
avant d'être utilisé comme opérande de droite dans l'addition.

J'ai bon ?

La deuxième citation me pose encore problème par contre, et selon une
première lecture rendrait incorrecte la construction ci-dessus.
Cependant je suis tombé sur une discussion de presque 800 messages sur
les sequence point sur comp.std.c et qui à certains endroits traite
justement de cette citation (ou très proche). De ce que j'ai lu
apparemment je ne suis pas le seul troublé !

Merci d'avance pour vos remarques.
Publicité
Poster une réponse
Anonyme