Je dirais que oui (en plus, pas de warning) mais quand même j'ai un doute : bien
que les chaînes littérales soient en mémoire statique, le compilateur va-t-il
retourner la même adresse à chaque appel ? Bon en fait quand on dit que les
chaînes sont en mémoire statique, je trouve que c'est un peu vague. La Norme,
elle, dit :
A character string literal has static storage duration and type
"array of char ,"
mais dans un programme on écrit
int toto=42, titi;
titi=toto + strlen("zozo");
le compilateur place-t-il en mémoire statique la chaîne "zozo" parce que je vois
pas l'intérêt de le faire ? Par contre si on écrit
char *s="zozo";
je vois l'intérêt puisque l'adresse est utilisée par une variable
D'ailleurs, dans le premier exemple, la variable ok a-t-elle toujours la même
valeur ? Je crois que oui mais j'ai un petit doute.
OK. Dans le même genre il y a quelque chose qui peut troubler le débutant, c'est l'absence de warning ici :
int *f(void) { int okB; int *p=&ok; return p; }
Il n'y a pas de vrai problème dans ce code.
Bah, on retourne l'adresse d'un objet qui n'existe plus ... A part ça tout va bien ...
La fonction retourne [une
copie de] la valeur du int* p. p a été initialisé par &ok, le programmeur exprime clairement sa volonté de faire ainsi. &ok est une *valeur numérique immédiate*, résolue éventuellement après la compilation, au niveau du lieur ou du loader, mais peu importe.
qui ne génère pas de warning. On exprime encore clairement sa volonté de retourner une copie de valeur.
... de coder une ânerie ... Tu devrais relire ton livre, je suis sûr que c'est expliqué ...
C'est peut-être un peu comme un
if(a=b){} qui va warner, alors que if((a=b) != 0){} voire if((a=b)){} vont faire taire le warning: je ne double pas les parenthèses par plaisir...
pfff... strictement aucun rapport...
Tu nous avais habitué à de meilleurs raisonnements... l'age, sans doute ...
C'est la musique qui marche au pas qui vous donne ce regain de forme, hein ?
-- Pierre Maurette
-ed-, le 14/07/2009 a écrit :
On 13 juil, 17:48, Pierre Maurette <maurettepie...@wanadoo.fr> wrote:
OK. Dans le même genre il y a quelque chose qui peut troubler le débutant,
c'est l'absence de warning ici :
int *f(void)
{
int okB;
int *p=&ok;
return p;
}
Il n'y a pas de vrai problème dans ce code.
Bah, on retourne l'adresse d'un objet qui n'existe plus ... A part ça
tout va bien ...
La fonction retourne [une
copie de] la valeur du int* p. p a été initialisé par &ok, le
programmeur exprime clairement sa volonté de faire ainsi. &ok est une
*valeur numérique immédiate*, résolue éventuellement après la
compilation, au niveau du lieur ou du loader, mais peu importe.
OK. Dans le même genre il y a quelque chose qui peut troubler le débutant, c'est l'absence de warning ici :
int *f(void) { int okB; int *p=&ok; return p; }
Il n'y a pas de vrai problème dans ce code.
Bah, on retourne l'adresse d'un objet qui n'existe plus ... A part ça tout va bien ...
La fonction retourne [une
copie de] la valeur du int* p. p a été initialisé par &ok, le programmeur exprime clairement sa volonté de faire ainsi. &ok est une *valeur numérique immédiate*, résolue éventuellement après la compilation, au niveau du lieur ou du loader, mais peu importe.
> Si on outrepasse les warnings et qu'on compile quand même cet étran ge > code, "OK" peut effectivement /exister/ pendant quelques nanosecondes e n > mémoire locale
Je crois que la discussion devient byzantine et qu'on va bientôt aborde r la question du sexe des chaînes. Bon, moi j'avais compris que les chaîne s littérales ("truc") étaient systématiquement placées en mémoire statique
Oui, mais "xx" ne représenta forcément un chaine littérale. C'est la cas dans
puts("xxx"); et char const *p = "xxx";
c'est tout.
Dans les cas que tu cites ;
(par exemple sizeof "truc"; ou char ok[]="OK";)
Ce ne sont pas des chaines littérales, mais simplement des valeurs immédiates (expressions constantes). Elles n'ont pas d'adresses et servent simplement au compilateur à faire un calcul (sizeof) ou à initialiser un tableau de char. Il est cependant techniquement possible qu'il existe une zone mémoire contentant la chaine d'initialisation qui sert uniquement à faire l'init, comme si on avait :
Par contre, si le tableau est définie const, il est possible, mais ce n'est pas garanti, que la chaine existe en mémoire permanente. Mais comme ce n'est pas garanti par le langage, on se garde bien d'en faire une hypothèse générale.
Par contre, si on a char *ok="OK"; là on ne s'en fout pas puisque le tableau est converti en pointeur,
Le tableau n'est pas converti en pointeur (cette notion ne s'applique qu'aux appels de fonction, et encore...AMA cette notion n'existe tout simplement pas.). Un tableau de char initialisé contenant la chaine est crée en mémoire permanente (static duration), c'est garanti. Un pointeur local est initialisé avec l'adresse du premier caractère de ce tableau,
d'ailleurs je suppose qu'on aurait pu écrire de façon équivalente : char *p=&"OK"[0];
et cette adresse est retournée. L'utilité du pointeur est faible, et le codage sera probablement équivallent à :
<...> return "OK"; }
si le pointeur n'est pas utilisé autrement ...
On 14 juil, 01:56, candide <cand...@free.invalid> wrote:
Pierre Maurette a écrit :
> candide, le 14/07/2009 a écrit :
>> Pierre Maurette a écrit :
>>> candide, le 13/07/2009 a écrit :
> [...]
>>> Il n'y
>>> a pas d'objet à copier.
>> La question est de savoir si "OK" existe en mémoire ou pas. J'avais crû
>> comprendre qu'il n'y avait pas de garantie que l'objet n'existe pas :
> Si on outrepasse les warnings et qu'on compile quand même cet étran ge
> code, "OK" peut effectivement /exister/ pendant quelques nanosecondes e n
> mémoire locale
Je crois que la discussion devient byzantine et qu'on va bientôt aborde r la
question du sexe des chaînes. Bon, moi j'avais compris que les chaîne s
littérales ("truc") étaient systématiquement
placées en mémoire statique
Oui, mais "xx" ne représenta forcément un chaine littérale. C'est la
cas dans
puts("xxx");
et
char const *p = "xxx";
c'est tout.
Dans les cas que tu cites ;
(par exemple sizeof "truc"; ou char ok[]="OK";)
Ce ne sont pas des chaines littérales, mais simplement des valeurs
immédiates (expressions constantes). Elles n'ont pas d'adresses et
servent simplement au compilateur à faire un calcul (sizeof) ou à
initialiser un tableau de char. Il est cependant techniquement
possible qu'il existe une zone mémoire contentant la chaine
d'initialisation qui sert uniquement à faire l'init, comme si on
avait :
Par contre, si le tableau est définie const, il est possible, mais ce
n'est pas garanti, que la chaine existe en mémoire permanente. Mais
comme ce n'est pas garanti par le langage, on se garde bien d'en faire
une hypothèse générale.
Par contre, si on a char *ok="OK"; là on ne s'en fout
pas puisque le tableau est converti en pointeur,
Le tableau n'est pas converti en pointeur (cette notion ne s'applique
qu'aux appels de fonction, et encore...AMA cette notion n'existe tout
simplement pas.). Un tableau de char initialisé contenant la chaine
est crée en mémoire permanente (static duration), c'est garanti. Un
pointeur local est initialisé avec l'adresse du premier caractère de
ce tableau,
d'ailleurs je suppose qu'on
aurait pu écrire de façon équivalente : char *p=&"OK"[0];
et cette adresse est retournée. L'utilité du pointeur est faible, et
le codage sera probablement équivallent à :
> Si on outrepasse les warnings et qu'on compile quand même cet étran ge > code, "OK" peut effectivement /exister/ pendant quelques nanosecondes e n > mémoire locale
Je crois que la discussion devient byzantine et qu'on va bientôt aborde r la question du sexe des chaînes. Bon, moi j'avais compris que les chaîne s littérales ("truc") étaient systématiquement placées en mémoire statique
Oui, mais "xx" ne représenta forcément un chaine littérale. C'est la cas dans
puts("xxx"); et char const *p = "xxx";
c'est tout.
Dans les cas que tu cites ;
(par exemple sizeof "truc"; ou char ok[]="OK";)
Ce ne sont pas des chaines littérales, mais simplement des valeurs immédiates (expressions constantes). Elles n'ont pas d'adresses et servent simplement au compilateur à faire un calcul (sizeof) ou à initialiser un tableau de char. Il est cependant techniquement possible qu'il existe une zone mémoire contentant la chaine d'initialisation qui sert uniquement à faire l'init, comme si on avait :
Par contre, si le tableau est définie const, il est possible, mais ce n'est pas garanti, que la chaine existe en mémoire permanente. Mais comme ce n'est pas garanti par le langage, on se garde bien d'en faire une hypothèse générale.
Par contre, si on a char *ok="OK"; là on ne s'en fout pas puisque le tableau est converti en pointeur,
Le tableau n'est pas converti en pointeur (cette notion ne s'applique qu'aux appels de fonction, et encore...AMA cette notion n'existe tout simplement pas.). Un tableau de char initialisé contenant la chaine est crée en mémoire permanente (static duration), c'est garanti. Un pointeur local est initialisé avec l'adresse du premier caractère de ce tableau,
d'ailleurs je suppose qu'on aurait pu écrire de façon équivalente : char *p=&"OK"[0];
et cette adresse est retournée. L'utilité du pointeur est faible, et le codage sera probablement équivallent à :
<...> return "OK"; }
si le pointeur n'est pas utilisé autrement ...
-ed-
On 14 juil, 10:43, Eric Levenez wrote:
Le 14/07/09 00:00, dans <4a5bae81$0$421$, « can dide » a écrit :
> Sauf que je ne vois pas comment on peut réutiliser le retour de f pui sque > l'objet renvoyé n'existe plus ou n'est plus censé exister d'ailleur s la Norme > dit : > [snip]
Donc d'après toi le code précédent ne va pas afficher 2 fois la m ême chose et une opération magique va changer la valeur de la variable locale p a u moment du free ?
Non, mais le système pourrait très bien rendre l'adresse invalide et sa simple lecture pourrait provoquer un trap... C'est techniquement possible, mais probablement peu utilisé...
Ce sujet a été débattu à l'extême sur clc...
On 14 juil, 10:43, Eric Levenez <use...@levenez.com> wrote:
Le 14/07/09 00:00, dans <4a5bae81$0$421$426a7...@news.free.fr>, « can dide »
<cand...@free.invalid> a écrit :
> Sauf que je ne vois pas comment on peut réutiliser le retour de f pui sque
> l'objet renvoyé n'existe plus ou n'est plus censé exister d'ailleur s la Norme
> dit :
> [snip]
Donc d'après toi le code précédent ne va pas afficher 2 fois la m ême chose
et une opération magique va changer la valeur de la variable locale p a u
moment du free ?
Non, mais le système pourrait très bien rendre l'adresse invalide et
sa simple lecture pourrait provoquer un trap... C'est techniquement
possible, mais probablement peu utilisé...
Le 14/07/09 00:00, dans <4a5bae81$0$421$, « can dide » a écrit :
> Sauf que je ne vois pas comment on peut réutiliser le retour de f pui sque > l'objet renvoyé n'existe plus ou n'est plus censé exister d'ailleur s la Norme > dit : > [snip]
Donc d'après toi le code précédent ne va pas afficher 2 fois la m ême chose et une opération magique va changer la valeur de la variable locale p a u moment du free ?
Non, mais le système pourrait très bien rendre l'adresse invalide et sa simple lecture pourrait provoquer un trap... C'est techniquement possible, mais probablement peu utilisé...
Ce sujet a été débattu à l'extême sur clc...
Richard Delorme
Le 14/07/2009 13:02, Eric Levenez a écrit :
§ 6.2.4.2 [...] The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
Et tu connais une plateforme où, dans mon exemple, la variable locale p est modifiée entre les 2 print ?
Malheureusement non. A mon avis, la plateforme idéale serait celle qui, dans le second cas, afficherait "indeterminate pointer value".
J'imagine bien une appli avec pleins de malloc où les adresses sont stockées dans des structures, et un free fait que de façon immédiate quelque chose change tous les pointeurs des structures pour rendre "indéterminé" ces mêmes pointeurs.
J'aimerais bien un compilateur ou un environnement d'exécution (à la valgrind) le faire, ne serait-ce qu'en mode débogage.
mais en pratique, je ne vois pas l'intérêt d'un pointeur que l'on ne peut pas déférencer (sauf comme borne d'un tableau).
Oui, il n'y a pas d'intérêt, et c'est peut-être pour cela que la norme possède la phrase floue que tu cites.
Je ne vois rien de floue dans la phrase de la norme.
Ce fameux principe de précaution qui permet tous les excès.
? Les précautions que prend la norme facilitent l'implémentation d'un compilateur conforme sur les plateformes les plus diverses.
-- Richard
Le 14/07/2009 13:02, Eric Levenez a écrit :
§ 6.2.4.2 [...] The value of a pointer becomes indeterminate when the
object it points to reaches the end of its lifetime.
Et tu connais une plateforme où, dans mon exemple, la variable locale p est
modifiée entre les 2 print ?
Malheureusement non. A mon avis, la plateforme idéale serait celle qui,
dans le second cas, afficherait "indeterminate pointer value".
J'imagine bien une appli avec pleins de malloc où les adresses sont stockées
dans des structures, et un free fait que de façon immédiate quelque chose
change tous les pointeurs des structures pour rendre "indéterminé" ces mêmes
pointeurs.
J'aimerais bien un compilateur ou un environnement d'exécution (à la
valgrind) le faire, ne serait-ce qu'en mode débogage.
mais en
pratique, je ne vois pas l'intérêt d'un pointeur que l'on ne peut pas
déférencer (sauf comme borne d'un tableau).
Oui, il n'y a pas d'intérêt, et c'est peut-être pour cela que la norme
possède la phrase floue que tu cites.
Je ne vois rien de floue dans la phrase de la norme.
Ce fameux principe de précaution qui permet tous les excès.
?
Les précautions que prend la norme facilitent l'implémentation d'un
compilateur conforme sur les plateformes les plus diverses.
§ 6.2.4.2 [...] The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
Et tu connais une plateforme où, dans mon exemple, la variable locale p est modifiée entre les 2 print ?
Malheureusement non. A mon avis, la plateforme idéale serait celle qui, dans le second cas, afficherait "indeterminate pointer value".
J'imagine bien une appli avec pleins de malloc où les adresses sont stockées dans des structures, et un free fait que de façon immédiate quelque chose change tous les pointeurs des structures pour rendre "indéterminé" ces mêmes pointeurs.
J'aimerais bien un compilateur ou un environnement d'exécution (à la valgrind) le faire, ne serait-ce qu'en mode débogage.
mais en pratique, je ne vois pas l'intérêt d'un pointeur que l'on ne peut pas déférencer (sauf comme borne d'un tableau).
Oui, il n'y a pas d'intérêt, et c'est peut-être pour cela que la norme possède la phrase floue que tu cites.
Je ne vois rien de floue dans la phrase de la norme.
Ce fameux principe de précaution qui permet tous les excès.
? Les précautions que prend la norme facilitent l'implémentation d'un compilateur conforme sur les plateformes les plus diverses.
-- Richard
espie
In article , Pierre Maurette wrote:
On se fout de l'objet. On dispose de la valeur d'un int*. On peut faire: printf("%pn", f()); sans danger...
Euh non. Ca marche sur la plupart des implementations que j'ai rencontrees mais ca n'est pas conforme, ni portable.
Si on lit bien la norme, c'est une des grosses bizarreries du langage: lorsqu'un objet en memoire devient invalide, c'est carrement les *pointeurs* qui sont invalidés. Il n'y a meme pas besoin de dereferencer le pointeur pour pondre du undefined behavior !
6.2.4.2: the lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is refered to outside of its lifetime, THE BEHAVIOR IS UNDEFINED. The value of a pointer BECOMES INDETERMINATE when the object it points to reaches the end of its lifetime.
3.17.2: indeterminate value: either an unspecified value or a trap representation
Note les termes employes par la norme: il s'agit bien de INDETERMINATE, et non UNSPECIFIED. Ce qui veut dire que ton pointeur peut *tres bien* devenir une trap representation lorsque l'objet pointé meurt...
In article <mn.70287d975d36123f.79899@wanadoo.fr>,
Pierre Maurette <maurettepierre@wanadoo.fr> wrote:
On se fout de l'objet. On dispose de la valeur d'un int*. On peut
faire:
printf("%pn", f());
sans danger...
Euh non. Ca marche sur la plupart des implementations que j'ai rencontrees
mais ca n'est pas conforme, ni portable.
Si on lit bien la norme, c'est une des grosses bizarreries du langage:
lorsqu'un objet en memoire devient invalide, c'est carrement les *pointeurs*
qui sont invalidés. Il n'y a meme pas besoin de dereferencer le pointeur
pour pondre du undefined behavior !
6.2.4.2: the lifetime of an object is the portion of program execution
during which storage is guaranteed to be reserved for it. An object exists,
has a constant address, and retains its last-stored value throughout its
lifetime. If an object is refered to outside of its lifetime, THE BEHAVIOR
IS UNDEFINED. The value of a pointer BECOMES INDETERMINATE when the object
it points to reaches the end of its lifetime.
3.17.2: indeterminate value: either an unspecified value or a trap
representation
Note les termes employes par la norme: il s'agit bien de INDETERMINATE, et
non UNSPECIFIED. Ce qui veut dire que ton pointeur peut *tres bien* devenir
une trap representation lorsque l'objet pointé meurt...
On se fout de l'objet. On dispose de la valeur d'un int*. On peut faire: printf("%pn", f()); sans danger...
Euh non. Ca marche sur la plupart des implementations que j'ai rencontrees mais ca n'est pas conforme, ni portable.
Si on lit bien la norme, c'est une des grosses bizarreries du langage: lorsqu'un objet en memoire devient invalide, c'est carrement les *pointeurs* qui sont invalidés. Il n'y a meme pas besoin de dereferencer le pointeur pour pondre du undefined behavior !
6.2.4.2: the lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is refered to outside of its lifetime, THE BEHAVIOR IS UNDEFINED. The value of a pointer BECOMES INDETERMINATE when the object it points to reaches the end of its lifetime.
3.17.2: indeterminate value: either an unspecified value or a trap representation
Note les termes employes par la norme: il s'agit bien de INDETERMINATE, et non UNSPECIFIED. Ce qui veut dire que ton pointeur peut *tres bien* devenir une trap representation lorsque l'objet pointé meurt...
Pierre Maurette
Marc Espie, le 14/07/2009 a écrit :
In article , Pierre Maurette wrote:
On se fout de l'objet. On dispose de la valeur d'un int*. On peut faire: printf("%pn", f()); sans danger...
Euh non. Ca marche sur la plupart des implementations que j'ai rencontrees mais ca n'est pas conforme, ni portable.
Si on lit bien la norme, c'est une des grosses bizarreries du langage: lorsqu'un objet en memoire devient invalide, c'est carrement les *pointeurs* qui sont invalidés. Il n'y a meme pas besoin de dereferencer le pointeur pour pondre du undefined behavior !
6.2.4.2: the lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is refered to outside of its lifetime, THE BEHAVIOR IS UNDEFINED. The value of a pointer BECOMES INDETERMINATE when the object it points to reaches the end of its lifetime.
3.17.2: indeterminate value: either an unspecified value or a trap representation
Note les termes employes par la norme: il s'agit bien de INDETERMINATE, et non UNSPECIFIED. Ce qui veut dire que ton pointeur peut *tres bien* devenir une trap representation lorsque l'objet pointé meurt...
Effectivement. Richard Delorme avait ressorti ce paragraphe. Et il me semble que ce point a déjà été débattu ici-même. J'ai répondu à 13:03:10.
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me semble qu'un printf de (uintptr_t)p est sûr. Non ? Il s'agit d'aller chercher un entier à l'adresse &p, &p est une constante sans danger, donc...
-- Pierre Maurette
Marc Espie, le 14/07/2009 a écrit :
In article <mn.70287d975d36123f.79899@wanadoo.fr>,
Pierre Maurette <maurettepierre@wanadoo.fr> wrote:
On se fout de l'objet. On dispose de la valeur d'un int*. On peut
faire:
printf("%pn", f());
sans danger...
Euh non. Ca marche sur la plupart des implementations que j'ai rencontrees
mais ca n'est pas conforme, ni portable.
Si on lit bien la norme, c'est une des grosses bizarreries du langage:
lorsqu'un objet en memoire devient invalide, c'est carrement les *pointeurs*
qui sont invalidés. Il n'y a meme pas besoin de dereferencer le pointeur
pour pondre du undefined behavior !
6.2.4.2: the lifetime of an object is the portion of program execution
during which storage is guaranteed to be reserved for it. An object exists,
has a constant address, and retains its last-stored value throughout its
lifetime. If an object is refered to outside of its lifetime, THE BEHAVIOR
IS UNDEFINED. The value of a pointer BECOMES INDETERMINATE when the object
it points to reaches the end of its lifetime.
3.17.2: indeterminate value: either an unspecified value or a trap
representation
Note les termes employes par la norme: il s'agit bien de INDETERMINATE, et
non UNSPECIFIED. Ce qui veut dire que ton pointeur peut *tres bien* devenir
une trap representation lorsque l'objet pointé meurt...
Effectivement. Richard Delorme avait ressorti ce paragraphe. Et il me
semble que ce point a déjà été débattu ici-même. J'ai répondu à
13:03:10.
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me
semble qu'un printf de (uintptr_t)p est sûr. Non ? Il s'agit d'aller
chercher un entier à l'adresse &p, &p est une constante sans danger,
donc...
On se fout de l'objet. On dispose de la valeur d'un int*. On peut faire: printf("%pn", f()); sans danger...
Euh non. Ca marche sur la plupart des implementations que j'ai rencontrees mais ca n'est pas conforme, ni portable.
Si on lit bien la norme, c'est une des grosses bizarreries du langage: lorsqu'un objet en memoire devient invalide, c'est carrement les *pointeurs* qui sont invalidés. Il n'y a meme pas besoin de dereferencer le pointeur pour pondre du undefined behavior !
6.2.4.2: the lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is refered to outside of its lifetime, THE BEHAVIOR IS UNDEFINED. The value of a pointer BECOMES INDETERMINATE when the object it points to reaches the end of its lifetime.
3.17.2: indeterminate value: either an unspecified value or a trap representation
Note les termes employes par la norme: il s'agit bien de INDETERMINATE, et non UNSPECIFIED. Ce qui veut dire que ton pointeur peut *tres bien* devenir une trap representation lorsque l'objet pointé meurt...
Effectivement. Richard Delorme avait ressorti ce paragraphe. Et il me semble que ce point a déjà été débattu ici-même. J'ai répondu à 13:03:10.
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me semble qu'un printf de (uintptr_t)p est sûr. Non ? Il s'agit d'aller chercher un entier à l'adresse &p, &p est une constante sans danger, donc...
-- Pierre Maurette
Xavier Roche
Pierre Maurette a écrit :
J'imagine que ça peut être effectif pour un pointeur qui serait un offset par rapport à un "truc" qui changerait
Ce que la norme suggère, c'est que dans la mesure où l'adresse est indéterminée, le retour peut aussi bien être un pointeur nul.
En clair, pour d'obscures raisons d'optimisation, le compilateur peut très bien omettre de calculer correctement l'adresse en question du moment où celle-ci n'est pas supposer entrer en jeu. Le fait de caster ou non n'est pas important: les "variables" sont souvent aliasées par des registres, avec des optimisations assez compliquées.
Ce qui peut donner des bizarreries comme dans ce code: (je ressort un vieil exemple, mais c'est toujours amusant)
void foo(int *f) { (*f)++; } int main() { long f = 0; foo((int*)&f); /* note: sizeof(int) == sizeof(long) on linux/32 */ printf("f == %sn", f == 0 ? "zero" : "non-zero"); return 0; }
Sur un gcc standard (sur un linux i386), le programme ne se comportera pas de la même manière selon que vous compilez en optimisant, ou non (-O0 vs. >= -O2). (et le compilateur ne vous donnera un warning que si vous utilisez des paramètres particulièrement exigeants lors de la compilation)
Ici, le compilateur a juste respecté la norme: l'adresse de f dans main() n'est pas "supposée" exister sous une autre forme, et donc l'incrément dans la fonction foo() a simplement été oublié.
On pourrait imaginer un cas aussi tordu après la libération d'un objet ..
Pierre Maurette a écrit :
J'imagine que ça peut être effectif pour un pointeur qui serait un
offset par rapport à un "truc" qui changerait
Ce que la norme suggère, c'est que dans la mesure où l'adresse est
indéterminée, le retour peut aussi bien être un pointeur nul.
En clair, pour d'obscures raisons d'optimisation, le compilateur peut
très bien omettre de calculer correctement l'adresse en question du
moment où celle-ci n'est pas supposer entrer en jeu. Le fait de caster
ou non n'est pas important: les "variables" sont souvent aliasées par
des registres, avec des optimisations assez compliquées.
Ce qui peut donner des bizarreries comme dans ce code: (je ressort un
vieil exemple, mais c'est toujours amusant)
void foo(int *f) { (*f)++; }
int main() {
long f = 0;
foo((int*)&f); /* note: sizeof(int) == sizeof(long) on linux/32 */
printf("f == %sn", f == 0 ? "zero" : "non-zero");
return 0;
}
Sur un gcc standard (sur un linux i386), le programme ne se comportera
pas de la même manière selon que vous compilez en optimisant, ou non
(-O0 vs. >= -O2). (et le compilateur ne vous donnera un warning que si
vous utilisez des paramètres particulièrement exigeants lors de la
compilation)
Ici, le compilateur a juste respecté la norme: l'adresse de f dans
main() n'est pas "supposée" exister sous une autre forme, et donc
l'incrément dans la fonction foo() a simplement été oublié.
On pourrait imaginer un cas aussi tordu après la libération d'un objet ..
J'imagine que ça peut être effectif pour un pointeur qui serait un offset par rapport à un "truc" qui changerait
Ce que la norme suggère, c'est que dans la mesure où l'adresse est indéterminée, le retour peut aussi bien être un pointeur nul.
En clair, pour d'obscures raisons d'optimisation, le compilateur peut très bien omettre de calculer correctement l'adresse en question du moment où celle-ci n'est pas supposer entrer en jeu. Le fait de caster ou non n'est pas important: les "variables" sont souvent aliasées par des registres, avec des optimisations assez compliquées.
Ce qui peut donner des bizarreries comme dans ce code: (je ressort un vieil exemple, mais c'est toujours amusant)
void foo(int *f) { (*f)++; } int main() { long f = 0; foo((int*)&f); /* note: sizeof(int) == sizeof(long) on linux/32 */ printf("f == %sn", f == 0 ? "zero" : "non-zero"); return 0; }
Sur un gcc standard (sur un linux i386), le programme ne se comportera pas de la même manière selon que vous compilez en optimisant, ou non (-O0 vs. >= -O2). (et le compilateur ne vous donnera un warning que si vous utilisez des paramètres particulièrement exigeants lors de la compilation)
Ici, le compilateur a juste respecté la norme: l'adresse de f dans main() n'est pas "supposée" exister sous une autre forme, et donc l'incrément dans la fonction foo() a simplement été oublié.
On pourrait imaginer un cas aussi tordu après la libération d'un objet ..
Antoine Leca
Le 14/07/2009 11:16Z, Emmanuel Delahaye écrivit :
On 14 juil, 01:56, candide wrote:
Bon, moi j'avais compris que les chaînes littérales ("truc") étaient systématiquement placées en mémoire statique
Oui, mais "xx" ne représenta forcément un chaine littérale. C'est la cas dans puts("xxx"); et char const *p = "xxx"; [mais pas dans]
(par exemple sizeof "truc"; ou char ok[]="OK";)
Euh... moi j'ai la lecture inverse : toutes sont bien des chaînes littérales, mais seules les deux premières sont des objets, anonymes. Les deux autres sont escamotés avant la fin de l'élaboration (au sens Ada), de l'expression dans le premier cas et de l'initialisation dans le second.
Il est cependant techniquement possible qu'il existe une zone mémoire contentant la chaine d'initialisation qui sert uniquement à faire l'init, comme si on avait :
On est en dehors du domaine définie par la norme ; de même, le fait de savoir si l'objet "xxx" existe réellement comme entité séparée (et non pas intégré dans db 'toto:',13,10,'xxx',13,10,'fin du programme$'... les puts ayant été fusionnés en un seul appel système OUTSTR)
Antoine
Le 14/07/2009 11:16Z, Emmanuel Delahaye écrivit :
On 14 juil, 01:56, candide wrote:
Bon, moi j'avais compris que les chaînes littérales ("truc") étaient
systématiquement placées en mémoire statique
Oui, mais "xx" ne représenta forcément un chaine littérale. C'est la
cas dans puts("xxx"); et char const *p = "xxx"; [mais pas dans]
(par exemple sizeof "truc"; ou char ok[]="OK";)
Euh... moi j'ai la lecture inverse : toutes sont bien des chaînes
littérales, mais seules les deux premières sont des objets, anonymes.
Les deux autres sont escamotés avant la fin de l'élaboration (au sens
Ada), de l'expression dans le premier cas et de l'initialisation dans le
second.
Il est cependant techniquement
possible qu'il existe une zone mémoire contentant la chaine
d'initialisation qui sert uniquement à faire l'init, comme si on
avait :
On est en dehors du domaine définie par la norme ; de même, le fait de
savoir si l'objet "xxx" existe réellement comme entité séparée (et non
pas intégré dans db 'toto:',13,10,'xxx',13,10,'fin du programme$'... les
puts ayant été fusionnés en un seul appel système OUTSTR)
Bon, moi j'avais compris que les chaînes littérales ("truc") étaient systématiquement placées en mémoire statique
Oui, mais "xx" ne représenta forcément un chaine littérale. C'est la cas dans puts("xxx"); et char const *p = "xxx"; [mais pas dans]
(par exemple sizeof "truc"; ou char ok[]="OK";)
Euh... moi j'ai la lecture inverse : toutes sont bien des chaînes littérales, mais seules les deux premières sont des objets, anonymes. Les deux autres sont escamotés avant la fin de l'élaboration (au sens Ada), de l'expression dans le premier cas et de l'initialisation dans le second.
Il est cependant techniquement possible qu'il existe une zone mémoire contentant la chaine d'initialisation qui sert uniquement à faire l'init, comme si on avait :
On est en dehors du domaine définie par la norme ; de même, le fait de savoir si l'objet "xxx" existe réellement comme entité séparée (et non pas intégré dans db 'toto:',13,10,'xxx',13,10,'fin du programme$'... les puts ayant été fusionnés en un seul appel système OUTSTR)
Antoine
Antoine Leca
Le 14/07/2009 12:09, Pierre Maurette écrivit :
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me semble qu'un printf de (uintptr_t)p est sûr. Non ?
Non. Sur le DS9k, la conversion ptr vers entier nécessite un appel à la fonction _Jvaistmater(p,&tmp), dont la première instruction consiste à appeler _Çavapasspassercommeça(p) qui déréférence le pointeur pour vérifier qu'il est toujours valide. Et là, ça va coincer, puisque comme tu le sais la pile a bougé.
Antoine
Le 14/07/2009 12:09, Pierre Maurette écrivit :
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me
semble qu'un printf de (uintptr_t)p est sûr. Non ?
Non. Sur le DS9k, la conversion ptr vers entier nécessite un appel à la
fonction _Jvaistmater(p,&tmp), dont la première instruction consiste à
appeler _Çavapasspassercommeça(p) qui déréférence le pointeur pour
vérifier qu'il est toujours valide.
Et là, ça va coincer, puisque comme tu le sais la pile a bougé.
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me semble qu'un printf de (uintptr_t)p est sûr. Non ?
Non. Sur le DS9k, la conversion ptr vers entier nécessite un appel à la fonction _Jvaistmater(p,&tmp), dont la première instruction consiste à appeler _Çavapasspassercommeça(p) qui déréférence le pointeur pour vérifier qu'il est toujours valide. Et là, ça va coincer, puisque comme tu le sais la pile a bougé.
Antoine
Pierre Maurette
Antoine Leca, le 14/07/2009 a écrit :
Le 14/07/2009 12:09, Pierre Maurette écrivit :
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me semble qu'un printf de (uintptr_t)p est sûr. Non ?
Non. Sur le DS9k, la conversion ptr vers entier nécessite un appel à la fonction _Jvaistmater(p,&tmp), dont la première instruction consiste à appeler _Çavapasspassercommeça(p) qui déréférence le pointeur pour vérifier qu'il est toujours valide. Et là, ça va coincer, puisque comme tu le sais la pile a bougé.
Peut-être un plantage chaque 1er avril...
Dans:
void* bidon = &p; uintptr_t* u = (uintptr_t)bidon; [utiliser *u]
A quel moment ça coince ?
-- Pierre Maurette
Antoine Leca, le 14/07/2009 a écrit :
Le 14/07/2009 12:09, Pierre Maurette écrivit :
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me
semble qu'un printf de (uintptr_t)p est sûr. Non ?
Non. Sur le DS9k, la conversion ptr vers entier nécessite un appel à la
fonction _Jvaistmater(p,&tmp), dont la première instruction consiste à
appeler _Çavapasspassercommeça(p) qui déréférence le pointeur pour
vérifier qu'il est toujours valide.
Et là, ça va coincer, puisque comme tu le sais la pile a bougé.
Peut-être un plantage chaque 1er avril...
Dans:
void* bidon = &p;
uintptr_t* u = (uintptr_t)bidon;
[utiliser *u]
Je vois à peu près comment un pintf avec %p pourrait foirer. Mais il me semble qu'un printf de (uintptr_t)p est sûr. Non ?
Non. Sur le DS9k, la conversion ptr vers entier nécessite un appel à la fonction _Jvaistmater(p,&tmp), dont la première instruction consiste à appeler _Çavapasspassercommeça(p) qui déréférence le pointeur pour vérifier qu'il est toujours valide. Et là, ça va coincer, puisque comme tu le sais la pile a bougé.
Peut-être un plantage chaque 1er avril...
Dans:
void* bidon = &p; uintptr_t* u = (uintptr_t)bidon; [utiliser *u]