Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Point de sequencement dans un appel de function

9 réponses
Avatar
candide
Bonjour,

Soit le code :

----------------------
#include <stdio.h>

int main(void)
{
int i = 10;

printf("%d %d \n", i++,i);
return 0;
}
----------------------

qui, à la compilation, m'affiche ceci :

----------------------
candide@candide-desktop:~$ gcc -W -Wall -pedantic -o x test.c
test.c: In function «main":
test.c:7: attention : operation on «i" may be undefined
----------------------

J'ai cru que c'était un problème de point de séquencement ou d'ordre
d'évaluation des arguments mais j'ai l'impression que c'est plus grave
que ça.

Est-ce que le "undefined" dont parle gcc est un "undefined behavior" ?

Est-ce que je tomberais sous le coup de

A.6.2 Undefined behavior
(...)
An object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points

?

Le point de séquencement se produit à la fin de l'appel et non pas après
l'évaluation de _chaque_ argument, n'est-ce pas ? Car je trouve ceci pas
très clair :

A.2 SEQUENCE POINTS

The following are the sequence points described in [1211]2.1.2.3

* The call to a function, after the arguments have been evaluated

Merci.

9 réponses

Avatar
Marc Boyer
On 2008-04-28, candide wrote:
Bonjour,

Soit le code :

----------------------
#include <stdio.h>

int main(void)
{
int i = 10;

printf("%d %d n", i++,i);
return 0;
}
----------------------

qui, à la compilation, m'affiche ceci :

----------------------
:~$ gcc -W -Wall -pedantic -o x test.c
test.c: In function «main":
test.c:7: attention : operation on «i" may be undefined


Il est gentil gcc.

----------------------

J'ai cru que c'était un problème de point de séquencement ou d'ordre
d'évaluation des arguments mais j'ai l'impression que c'est plus grave
que ça.


Non, non, c'est ça.

Est-ce que le "undefined" dont parle gcc est un "undefined behavior" ?

Est-ce que je tomberais sous le coup de

A.6.2 Undefined behavior
(...)
An object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points


Exactement.

Le point de séquencement se produit à la fin de l'appel et non pas après
l'évaluation de _chaque_ argument, n'est-ce pas ? Car je trouve ceci pas
très clair :


En effet, les points de séquencement ne sont pas ce qu'il y a de plus
clair dans la norme. Mais bon, ce cas là étant tellement connu, la
réponse à ta question est "oui": le point de séquencement se produit
à la fin de l'appel et non pas après l'évaluation de _chaque_ argument.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
candide

(...)
An object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points


Exactement.


Très exactement, qu'est-ce qui n'est pas respecté ?

Ceci :

i) object is modified more than once,

ou bien ceci :

ii) object is modified and accessed other than to determine the new value

(ce dernier point ne me paraissant pas très clair).


Avatar
Jean-Marc Bourguet
candide writes:

ii) object is modified and accessed other than to determine the new value


printf("%d %d n", i++,i);

i est lu deux fois et la deuxieme (dans l'ordre d'ecriture, pas dans celle
d'execution et c'est la une partie du probleme) ne sert a calculer la
nouvelle valeur, donc c'est problematique (la premiere lecture -- a nouveau
dans l'ordre d'ecriture -- sert a determiner la nouvelle valeur).

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Marc Boyer
On 2008-04-28, candide wrote:

(...)
An object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points


Exactement.


Très exactement, qu'est-ce qui n'est pas respecté ?

Ceci :

i) object is modified more than once,


Ben non, puisqu'il y a un seul ++.

ou bien ceci :

ii) object is modified and accessed other than to determine the new value

(ce dernier point ne me paraissant pas très clair).


printf("%d:%d", i++ /*1*/ , i /*2*/ );

Ben, c'est ça: l'objet (i) est modifié et accédé dans i++ /*1i*/, et aussi
accedé dans i /*2*/, donc c'est le "other than to determine the new value".


Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)



Avatar
candide

ii) object is modified and accessed other than to determine the new value

(ce dernier point ne me paraissant pas très clair).





An object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points



J'ai lu vos explications mais ce n'est toujours pas limpide. La phrase
ci-dessus (extrait de C90, la version de C99 étant légèrement
différente) signifie-elle :

"[Le comportement est indéterminé si] Entre deux points de séquencement,
un objet est modifié plus d'une fois ou s'il est modifié et lu(*) dans
un autre but que de déterminer sa nouvelle valeur"
?
_________
(*)"est accédé" ne me semble pas français. C99 utilise le terme de "lu".

Est-ce que ce "other than ..." est destiné à nous autoriser à écrire :

printf("%d", i++);

(sans conviction : i est bien modifié et lu et i++ donne la nouvelle
valeur).


Mais, la deuxième expression i dans

printf("%d %d", i++, i );

a bien pour but de déterminer la nouvelle valeur, donc le "other than"
me dit que j'ai le droit, non ?



Sinon, je comprends pas fondamentalement pourquoi ceci

printf("%d %p", i++, (void *)&i );

m'envoie le même genre de warning que ci-dessus,
parce que (void *)&i ne cherche pas à déterminer la nouvelle valeur de
l'objet modifié, elle cherche à accéder à son adresse.


D'après ce que j'ai lu, la fin de la phrase est connue pour poser des
problèmes de compréhension, cf. clc faq :


-----------------------------------------------------------
"Furthermore, the prior value shall be accessed
only to determine the value to be stored."


The second sentence can be difficult to understand.
[des explications non convaincantes suivaient et disant pour faire
direct qu'il faut pas mélanger i et i++ entre deux sequence points]
-----------------------------------------------------------

et les explications sont assez contorsionnées, exemple d'un auteur
fiable lu dans les archives de clc :

-----------------------------------------------------------------
In a[i] = i++ the value of i is both read and modified and the process
of reading the value of i is independent of the process of calculating
the new value to be written to i. So a[i] = i++ violates the requirement

"Furthermore, the prior value shall be accessed only to determine the
value to be stored".
-----------------------------------------------------------------

A défaut d'expliquer les arcanes de la norme, je pense qu'un manuel de C
devrait mettre en garde contre ce genre de comportement tordu.


Sinon, toujours à propos des points de séquencement, dans la fclc-faq
(il y a l'équivalent du problème dans la clc-faq) :
-----------------------------------------------------------------
10.6 En est-il de même pour i++ * i++ ?



Oui. La norme ne précise pas pour les opérateurs binaires dans
quel ordre les opérandes sont évalués.
-----------------------------------------------------------------

Je trouve cette explication vraiment douteuse (désolé, je ne veux
blesser personne). Si c'était une affaire d'ordre d'évaluation, on
aurait a priori, un résultat dépendant de l'implémentation. Non, je
pense qu'il faut invoquer le point de la norme déjà signalé. S'il n'y
avait pas ce point, vu qu'il n'y de point de séquencement qu'à la fin de
l'expression, si au départ i=5, la valeur retournée devrait être 25
(indépendamment de l'ordre d'évaluation), non ?

Au passage, la réponse à la question précédente n'est vraiment pas
satisfaisante :

"
Ce genre d'expression fait partie des « undefined

behaviour », ou comportement indéfini. Cela signifie que le

résultat d'une telle opération dépend du compilateur.

L'opérateur ++ modifie la valeur de i, alors que

celle-ci est utilisée ailleurs dans l'expression.

C'est ce que l'on appelle un « effet de bords »."

Avatar
candide


Soit le code :

----------------------
#include <stdio.h>

int main(void)
{
int i = 10;

printf("%d %d n", i++,i);
return 0;
}
----------------------

qui, à la compilation, m'affiche ceci :

----------------------
:~$ gcc -W -Wall -pedantic -o x test.c
test.c: In function «main":
test.c:7: attention : operation on «i" may be undefined
----------------------



Une variante qui elle passe sans crier :

----------------------
#include <stdio.h>

int main(void)
{
int i = 10;

printf("%d n", 0*i++ + i);
return 0;
}

----------------------

:~$ gcc -W -Wall -pedantic -o x test.c
:~$ ./x
11

Avatar
Marc Boyer
On 2008-04-28, candide wrote:

ii) object is modified and accessed other than to determine the new value

(ce dernier point ne me paraissant pas très clair).





An object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points



J'ai lu vos explications mais ce n'est toujours pas limpide. La phrase
ci-dessus (extrait de C90, la version de C99 étant légèrement
différente) signifie-elle :

"[Le comportement est indéterminé si] Entre deux points de séquencement,
un objet est modifié plus d'une fois ou s'il est modifié et lu(*) dans
un autre but que de déterminer sa nouvelle valeur"


C'est comme ça que je le comprends.

(*)"est accédé" ne me semble pas français. C99 utilise le terme de "lu".

Est-ce que ce "other than ..." est destiné à nous autoriser à écrire :

printf("%d", i++);

(sans conviction : i est bien modifié et lu et i++ donne la nouvelle
valeur).


C'est comme ça que je le comprends.

Mais, la deuxième expression i dans

printf("%d %d", i++, i );

a bien pour but de déterminer la nouvelle valeur, donc le "other than"
me dit que j'ai le droit, non ?


A ben non. C'est un accès "normal".

Sinon, je comprends pas fondamentalement pourquoi ceci

printf("%d %p", i++, (void *)&i );

m'envoie le même genre de warning que ci-dessus,
parce que (void *)&i ne cherche pas à déterminer la nouvelle valeur de
l'objet modifié, elle cherche à accéder à son adresse.


Alors là... En fait, il faut voir si le warning détecte un
véritable UB, ou si c'est une fausse alerte.

Parce qu'à mon sens, "&i" n'est pas un accès à l'objet i.
J'aurais tendance à parier sur une fausse alerte (mais pas
trop d'argent quand même...)

-----------------------------------------------------------------
In a[i] = i++ the value of i is both read and modified and the process
of reading the value of i is independent of the process of calculating
the new value to be written to i. So a[i] = i++ violates the requirement

"Furthermore, the prior value shall be accessed only to determine the
value to be stored".
-----------------------------------------------------------------

A défaut d'expliquer les arcanes de la norme, je pense qu'un manuel de C
devrait mettre en garde contre ce genre de comportement tordu.


Oui. Mon cours le fait d'ailleurs.

Sinon, toujours à propos des points de séquencement, dans la fclc-faq
(il y a l'équivalent du problème dans la clc-faq) :
-----------------------------------------------------------------
10.6 En est-il de même pour i++ * i++ ?

Oui. La norme ne précise pas pour les opérateurs binaires dans
quel ordre les opérandes sont évalués.
-----------------------------------------------------------------

Je trouve cette explication vraiment douteuse (désolé, je ne veux
blesser personne).


Ton raisonnement ce tient.

Si c'était une affaire d'ordre d'évaluation, on
aurait a priori, un résultat dépendant de l'implémentation. Non, je
pense qu'il faut invoquer le point de la norme déjà signalé. S'il n'y
avait pas ce point, vu qu'il n'y de point de séquencement qu'à la fin de
l'expression, si au départ i=5, la valeur retournée devrait être 25
(indépendamment de l'ordre d'évaluation), non ?


Je suis plutôt d'accord avec toi.

Au passage, la réponse à la question précédente n'est vraiment pas
satisfaisante :

"
Ce genre d'expression fait partie des « undefined
behaviour », ou comportement indéfini. Cela signifie que le
résultat d'une telle opération dépend du compilateur.
L'opérateur ++ modifie la valeur de i, alors que
celle-ci est utilisée ailleurs dans l'expression.
C'est ce que l'on appelle un « effet de bords »."


Ben, la FAQ n'est pas parfaite. Si tu veux lancer une discussion sur
le sujet, vas-y.

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


Avatar
Erwan David
candide écrivait :



Soit le code :

----------------------
#include <stdio.h>

int main(void)
{
int i = 10;

printf("%d %d n", i++,i);
return 0;
}
----------------------

qui, à la compilation, m'affiche ceci :

----------------------
:~$ gcc -W -Wall -pedantic -o x test.c
test.c: In function «main":
test.c:7: attention : operation on «i" may be undefined
----------------------



Une variante qui elle passe sans crier :

----------------------
#include <stdio.h>

int main(void)
{
int i = 10;

printf("%d n", 0*i++ + i);
return 0;
}

----------------------


Le fait que ça passe à la compilation ne granti pas qu'on ait un
programme correct sans undefined behaviour. Typiquement c'ets le cas ici.

--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé


Avatar
Xavier Roche
printf("%d %d n", i++,i);


Pour compléter, de mémoire, en mode optimisé, les compilateurs auront
tendance a effectuer les effets de bord d'un seule traite, après
l'évaluation des arguments.

Et donc cette ligne pourrait très bien être décomposée par le
compilateur en:

printf("%d %d n", i,i);
i++;

[on oublie que le call force un point de séquence, et que donc le i++
est garanti d'être réalisé juste avant l'appel, même s'il n'est pas
garanti d'être réalisé lors de l'évaluation des arguments]