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

Retour chaine litterale

46 réponses
Avatar
candide
Bonjour

Le code suivant est correct je pense :

char *f(void)
{
char *ok="OK";
return ok;
}

Et le suivant l'est-il

char *f(void)
{
return "OK";
}

?

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.


Merci.

10 réponses

1 2 3 4 5
Avatar
candide
-ed- a écrit :
On 13 juil, 04:07, candide wrote:
Sinon, concernant le code que j'ai écrit, ok est une variable locale qui renvoie
l'adresse d'un objet statique, donc ça ne pose pas de problème de retour. En
revanche celui-ci en pose :

char *h(void)
{
char ok[]="OK";
return ok;

}



Ouah, au bout de 3 ans de C, candide a découvert qu'on avait pas le




Pire : bientôt 4 ans !!!

droit de retourner l'adresse d'une variable locale (enfin, on a
surtout pas le droit de l'utiliser...)





Je sais cela depuis longtemps tout simplement pour avoir reçu le warning en
pleine face et m'être lourdement interrogé.

Ce qu'il y a de trompeur ici c'est que ok est une recopie exacte de "OK" (et
c'est même le compilateur qui calcule la taille du tableau ok) et qu'on a vite
fait de confondre l'original et la copie.

En outre, c'est quand même très similaire à

char *f(void)
{
char *ok="OK";
return ok;
}

qui lui n'est pas fautif. Comme dans le premier cas, ok est transformé en
pointeur, et que "OK" est en mémoire statique, on a vite fait de penser que les
deux (celui de h et de f) codes sont équivalents.


Je dirais même que le code de f peut être troublant pour la raison suivante :

on renvoie ok qui est une variable locale et qui est une adresse (mais on ne
renvoie pas une adresse d'une variable locale)


Il y a une nuance qui peut troubler (ou faire seulement hésiter) :

ok est une variable locale dont on renvoie la valeur qui est une adresse

mais pour autant on ne renvoie pas l'adresse d'une variable locale (bon, pas sûr
que vous compreniez ce que je veux dire parce que justement il faut pas être
trop fort en C pour voir ce qu'il faut pas voir).





comme me le confirme le compilateur alors que l'action est la même.



Bah non. Dans le premier cas, tu retournes l'adresse d'une variable de
durée de vie permanente et dans ce dernier cas, celle d'une variable
dont la durée de vie s'interrompt précisément avec le return... C'est
nouveau ? Ou tu fais l'andouille pour voir si on est réveillés ?




Certes, mais tu vois bien que l'_intention_ est la même bien que dans le
deuxième cas, la récupération est impossible.




OK mais je n'ai pas souvent vu cela (ce qui évidemment ne prouve rien ! ;) ).



Extrait de

http://www.bien-programmer.fr/clib/ed/src/buf.c
Module BUF:
(...)



Vu. Mais je trouve cela assez rare d'après le code-source que je consulte. Mais
peut-être que renvoyer une chaîne littérale est assez rare en soi. Bon en tous
cas les livres n'évoquent pas cette question.




Mais alors pourquoi le prototype de strchr() n'est-il pas :

const char *strchr(const char *s, int c);

au lieu de

char *strchr(const char *s, int c);

?



Parce que rien n'oblige à ce que l'adresse retournée soit celle d'une
chaine non modifiable.C'est à l'utilisateur de savoir ce qu'il fait.
Le const du paramètre indique simplement que la fonction accepte
l'adresse d'une chaine non-modifiable parce que la fonction ne fait
aucun accès en écriture à cette chaine.



Ah oui, en effet, c'est juste pendant l'exécution de la fonction ce qui
n'empêche pas une modification ailleurs.



Ceci compile sans erreur et produit un comportement indéterminé.

char const *s = Hellon";

char *p = strchr(s, 'n');

if (p != NULL)
{
*p = 0;
}

Encore une fois, c'est la compétence du programmeur qui est en cause.
Si l'adresse passée est celle d'une chaine non modifiable, il faut
coder :

char const *s = Hellon";

char const *p = strchr(s, 'n');

if (p != NULL)
{
*p = 0; /* et la le compilateur detecte l'erreur */
}




En effet, voilà qui est plus convaincant (mais déjà assez compliqué quand on est
en phase d'apprentissage).
Avatar
Pierre Maurette
candide, le 13/07/2009 a écrit :
Merci à vous trois de vos réponses.

Je me réjouis que Pierre Maurette et Emmanuel Delahaye soient capables de
tenir une conversation cordiale avec moi ;)



Vous vous trompez. Là aussi.

... le tout étant de savoir combien de temps ça va durer ... ;)



A peu près autant que la rose de Malherbe, l'espace d'un matin. Je vous
emmerde...

--
Pierre Maurette
Avatar
Pierre Maurette
candide, le 13/07/2009 a écrit :
Pierre Maurette a écrit :

char ok[]="OK";
est strictement équivalent à:
char ok[] = {'O', 'K', ''};
ou
char ok[3] = {'O', 'K', ''};




Oui, en effet, il y a "recopie", ah ! c'était pourtant élémentaire.



Je dirais plutôt une initialisation qu'une recopie ou une copie. Il n'y
a pas d'objet à copier. "OK" est un élément (token ?) du code source,
c'est à dire un élément du dialogue entre le programmeur et le
compilateur. Mais il n'existe plus dans le code généré.
A l'inverse, dans:
const char* ok = "OK";
les objets "OK" et ok (sauf optimisation) existent dans le code généré.

--
Pierre Maurette
Avatar
Pierre Maurette
candide, le 13/07/2009 a écrit :
Eric Levenez a écrit :

L'action n'est pas la même car là tu ne retournes pas l'adresse de la chaîne
constante, mais l'adresse d'une variable dans la pile. Ton code est
identique à :

int *h(void)
{
int ok = 42;
return &ok;
}





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. 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.


--
Pierre Maurette
Avatar
Pierre Maurette
(supersedes )

candide, le 13/07/2009 a écrit :
Eric Levenez a écrit :

L'action n'est pas la même car là tu ne retournes pas l'adresse de la
chaîne
constante, mais l'adresse d'une variable dans la pile. Ton code est
identique à :

int *h(void)
{
int ok = 42;
return &ok;
}





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. 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.

J'ajoute:
Le même raisonnement s'applique à:

char *h(void)
{
char ok[]="OK";
char* dummy = ok;
return dummy;
}

qui ne génère pas de warning. On exprime encore clairement sa volonté
de retourner une copie de valeur. 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...

--
Pierre Maurette
Avatar
candide
Pierre Maurette a écrit :

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.




Sauf que je ne vois pas comment on peut réutiliser le retour de f puisque
l'objet renvoyé n'existe plus ou n'est plus censé exister d'ailleurs la Norme dit :

6.2.4

If an object is referred to outside of its lifetime, the behavior is undefined.

(...)

An object whose identifier is declared with no linkage and without the
storage-class specifier static has automatic storage duration. For such an object
that does not have a variable length array type, its lifetime extends from entry
into the block with which it is associated until execution of that block ends in
any way.


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.



et la volonté de la soeur du programmeur ?
Avatar
candide
Pierre Maurette a écrit :
candide, le 13/07/2009 a écrit :
Pierre Maurette a écrit :

char ok[]="OK";
est strictement équivalent à:
char ok[] = {'O', 'K', ''};
ou
char ok[3] = {'O', 'K', ''};




Oui, en effet, il y a "recopie", ah ! c'était pourtant élémentaire.



Je dirais plutôt une initialisation qu'une recopie ou une copie.



Oui, c'est effectivement une "initialisation" :

6.7.8 Initialization
(...) An array of character type may be initialized by a character string
literal, (...)

et selon la Norme c'est effectivement équivalent à ce que tu as dit, la Norme
donne même un exemple :

§6.7.8
EXAMPLE 8
The declaration
char s[] = "abc", t[3] = "abc"; defines "plain" char array objects s and t whose
elements are initialized with character string literals. This declaration is
identical to char s[] = { 'a', 'b', 'c', '' }, t[] = { 'a', 'b', 'c' };


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 :

6.4.5.
The multibyte character sequence is then used to initialize an array of static
storage duration and length just sufficient to contain the sequence.


"OK" est un élément (token ?) du code source,




6.4 Lexical elements
Syntax 1 token: keyword identifier constant string-literal punctuator


c'est à dire un élément du dialogue entre le programmeur et le
compilateur.



Faudrait pas confondre programmation et romantisme.
Avatar
Pierre Maurette
candide, le 14/07/2009 a écrit :
Pierre Maurette a écrit :

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.




Sauf que je ne vois pas comment on peut réutiliser le retour de f puisque
l'objet renvoyé n'existe plus ou n'est plus censé exister



On se fout de l'objet. On dispose de la valeur d'un int*. On peut
faire:
printf("%pn", f());
sans danger...

d'ailleurs la Norme [blah]



Ça tourne à l'obsession, non ?

--
Pierre Maurette
Avatar
candide
Pierre Maurette a écrit :
candide, le 14/07/2009 a écrit :
Pierre Maurette a écrit :

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.




Sauf que je ne vois pas comment on peut réutiliser le retour de f puisque
l'objet renvoyé n'existe plus ou n'est plus censé exister



On se fout de l'objet. On dispose de la valeur d'un int*. On peut faire:
printf("%pn", f());
sans danger...




C'est vrai que maintenant j'ai un doute : on dispose juste de l'adresse d'un
objet qui n'existe plus.
Avatar
Pierre Maurette
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 :



Le code qui nous occupe est le suivant:

char *h(void)
{
char ok[]="OK";
return ok;
}

Si on outrepasse les warnings et qu'on compile quand même cet étrange
code, "OK" peut effectivement /exister/ pendant quelques nanosecondes
en mémoire locale - la pile, disons - durant la durée de vie de chaque
appel à h(). Mais en fait, la fonction renverra peut-être un truc comme
la valeur du pointeur de pile. Et alors "OK" ne sera nulle part.

[...]

c'est à dire un élément du dialogue entre le programmeur et le
compilateur.



Faudrait pas confondre programmation et romantisme.



Si vous voulez.
Mais l'idée de distinguer les objets des "éléments du dialogue entre le
programmeur et le compilateur" permet par exemple de bien piger les
références en C++.

--
Pierre Maurette
1 2 3 4 5