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

retourner une variable local (de type char[])

15 réponses
Avatar
matt
Bonjour,

voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

c'est pas beau !!!
mais si on copie directement le retour de la fonction dans un autre
tableau de char :

int main()
{
char contenant[20];
strcpy(contenant, mafonction());
return 0;
}

est ce qu'il y a danger que le contenu de chaine soit modifié ?
et une autre question : ou est la différence avec le code suivant

int mafonction()
{
int retour;
retour = 10;
return retour;
}

int main()
{
int contenant;
contenant = mafonction();
return 0;
}

merci pour vos réponses,

Matt.

10 réponses

1 2
Avatar
ALain Montfranc
matt a écrit
Bonjour,

voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

c'est pas beau !!!


Et surtout ca va planter car la chaine "chaine" sera désallouée à la
fin de l'execution "mafonction" sauf à la rendre "static"

mais si on copie directement le retour de la fonction dans un autre tableau
de char :

int main()
{
char contenant[20];
strcpy(contenant, mafonction());
return 0;
}

est ce qu'il y a danger que le contenu de chaine soit modifié ?


resultat *indetermine* cf supra

et une autre question : ou est la différence avec le code suivant

int mafonction()
{
int retour;
retour = 10;
return retour;
}

int main()
{
int contenant;
contenant = mafonction();
return 0;
}



La difference c'est que le pointeur chaine pointe vers une zone
desallouée en sortie de fonction et seule la valeur du pointeur sera
copiée en sortie.

Dans le second cas, c'est l'entier qui est copié et tout va bien

Avatar
matt
matt a écrit
Bonjour,

voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

c'est pas beau !!!


Et surtout ca va planter car la chaine "chaine" sera désallouée à la fin
de l'execution "mafonction" sauf à la rendre "static"



Ca ne plante pas, un warning à la compilation c'est tout.
Mais je suis d'accord avec ce que tu as écrit plus bas.

mais si on copie directement le retour de la fonction dans un autre
tableau de char :

int main()
{
char contenant[20];
strcpy(contenant, mafonction());
return 0;
}

est ce qu'il y a danger que le contenu de chaine soit modifié ?


resultat *indetermine* cf supra

et une autre question : ou est la différence avec le code suivant

int mafonction()
{
int retour;
retour = 10;
return retour;
}

int main()
{
int contenant;
contenant = mafonction();
return 0;
}



La difference c'est que le pointeur chaine pointe vers une zone
desallouée en sortie de fonction et seule la valeur du pointeur sera
copiée en sortie.

Dans le second cas, c'est l'entier qui est copié et tout va bien




ce que je veux dire, dans la deuxieme fonction

/* on reserve sizeof(int) octets pour y stocker la valeur de x */
int x = 0;
/* comme x est une variable local à la fonction, des qu'on sort, les
octets réserves sont donc a nouveau disponible ? mais on peut les copier
dans contenant. Pour la premier fonction, ça ne marche pas ? je copie la
valeur pointe par contenant dans la fonction dans une variable de main */

Matt...


Avatar
Pierre Maurette
Bonjour,


Bonjour,

Quelques généralités:

- Une variable créée localement n'existe pas hors de la fonction, et
n'existe plus après que le fonction ait retourné. Vous ne pouvez donc
rien faire de son adresse dans l'appelant.

- En C vous ne savez transmettre à une fonction et recevoir en retour
d'une fonction que des *copies de valeurs*.

- Dans ce cadre, celui des arguments et retours de fonctions, le nom
d'un tableau est remplacé par le compilateur par l'adresse du premier
élément du tableau, de type "pointeur sur type des éléments du
tableau". Certains affirmeront que "c'est logique", à mon avis ce ne
l'est pas. Ce n'est pas cohérent avec ce qui se passe pour les
structures. Donc, il faut admettre ce C-isme et vivre avec.

voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}


Ici, dans le return, chaine et &chaine[0] sont équivalents, de type
pointeur vers char, &chaine aurait la même valeur numérique mais un
type différent, pointeur vers tableau de 20 char.
Vous renvoyez l'adresse d'un tableau qui est mort au même instant.

c'est pas beau !!!


Même pire ;-)


mais si on copie directement le retour de la fonction dans un autre tableau
de char :

int main()
{
char contenant[20];
strcpy(contenant, mafonction());
return 0;
}

est ce qu'il y a danger que le contenu de chaine soit modifié ?
et une autre question : ou est la différence avec le code suivant

int mafonction()
{
int retour;
retour = 10;
return retour;
}

int main()
{
int contenant;
contenant = mafonction();
return 0;
}


Ici, vous renvoyez une copie de "retour", donc au retour de la fonction
vous copiez 10 dans "contenant". Il n'y a pas de différence avec votre
prelière fonction, dans laquelle vous renvoyiez une copie de "chaine",
soit &chaine[0]. Simplement quand vous utilisiez cette valeur dans un
printf(), un puts() ou un strcpy(), vous envoyiez à ces fonctions un
pointeur devenu invalide.

D'une façon générale, quand une fonction fabrique du texte (ou autre
problématique avoisinante), vous avez deux aproches + une:
- Vous allouez suffisamment d'espace dans l'appelant avant l'appel et
transmettez le pointeur non constant.
- Vous allouez en static dans la fonction, renvoyez le pointeur et
documentez pour expliquer qu'il appartiendra à l'appelant de libérer.
- Avatar de la première solution, en même temps que vous passez le
pointeur, vous le renvoyez, dans le but de permettre une écriture plus
fluide.
Vous devriez regarder les définitions des fonctions de la bibliothèque
standard, ces trois méthodes s'y trouvent.

--
Pierre Maurette

Avatar
Eric Levenez
Le 11/03/07 8:18, dans <45f3ad47$0$25923$, « matt »
a écrit :

voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

c'est pas beau !!!


C'est bien pire que juste "pas beau"

mais si on copie directement le retour de la fonction dans un autre
tableau de char :

int main()
{
char contenant[20];
strcpy(contenant, mafonction());
return 0;
}

est ce qu'il y a danger que le contenu de chaine soit modifié ?


Oui. Cela dépend totalement que l'architecture logicielle, du CPU... Après
l'appel de la fonction, la zone mémoire retournée peut être désallouée.

Dans la vraie vie, un sous-traitant dans ma boîte avait fait ce code et
l'avait testé sur son PC Windows en disant : "ça plante pas donc ça marche".
Le problème c'est le code marchait en embarqué et si une interruption
survenait au mauvais moment, la pile était écrasée et donc la variable.
Prise de bec avec ce sous traitant qui ne comprenait rien à rien. Et au
final la boîte du sous-traitant à fermé. On n'échappe pas à son Karma.

et une autre question : ou est la différence avec le code suivant

int mafonction()
{
int retour;
retour = 10;
return retour;
}

int main()
{
int contenant;
contenant = mafonction();
return 0;
}


Ce code n'utilise pas la pile mais le code de retour de la fonction. C'est
le principe même d'un code de retour.

Pour ton code avec la char, tu aurais pu faire un truc du genre :

char* mafonction()
{
static char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

Là le static indique que la variable chaine ne va pas en pile (et donc
disparaître dès le return), mais est dans une zone qui survit à la fonction.

--
Éric Lévénez -- <http://www.levenez.com/>
Unix is not only an OS, it's a way of life.

Avatar
Sylvain
Eric Levenez wrote on 11/03/2007 18:06:

Ce code n'utilise pas la pile mais le code de retour de la fonction. C'est
le principe même d'un code de retour.


euh, le code de retour est posé sur la pile soit mais comme l'est
sûrement le tableau non statique utilisé en paramètre local; la
différence est que l'appelant s'attend dans ce second cas à trouver
cette valeur de retour et la dépile correctement (préserve cette pile
avant de récupérer cette valeur) - dans le premier cas il dépile et
préserve l'adresse du tableau (et elle seule, non l'espace stockant son
contenu) ... une interruption et boum, comme tu l'as expliqué.

le C sait aujourd'hui retourner une structure, pour cela le compilo peut
utiliser la pile pour stocker le résultat complet.

Sylvain.

Avatar
linuxkiller
matt wrote:

voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

c'est pas beau !!!


exact, strcpy est réputé pour être un futur trou de sécurité

Avatar
Marc Boyer
Le 11-03-2007, matt a écrit :
voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

c'est pas beau !!!
mais si on copie directement le retour de la fonction dans un autre
tableau de char :

int main()
{
char contenant[20];
strcpy(contenant, mafonction());
return 0;
}

est ce qu'il y a danger que le contenu de chaine soit modifié ?


Un danger ? C'est d'après la norme un comportement indéfinit,
dans le sens ou le compilateur va considérer que la place utilisée
pour stocker mafonction::chaine est désormais disponible et qu'il peut
en faire ce qu'il veut.
Mes connaissances en compilateur m'incitent à penser que
*dans ce cas*, les risques sont faibles. Mais on pourrait aussi
faire

typedef struct {
char tab[20];
} stab;

stab mafonction(void){
stab res;
strncpy(res.tab, "Bonjour", sizeof(res.tab) );
return res;
}

et une autre question : ou est la différence avec le code suivant

int mafonction()
{
int retour;
retour = 10;
return retour;
}


considère plutot

int* mafonction(void){
int retour= 10;
return &retour;
}

Marc Boyer

Avatar
espie
In article <45f3ef37$0$27412$,
matt wrote:
matt a écrit
Bonjour,

voici une fonction retournant un pointeur sur un char.

char* mafonction()
{
char chaine[20];
strcpy(chaine, "Boujour");
return chaine;
}

c'est pas beau !!!


Et surtout ca va planter car la chaine "chaine" sera désallouée à la fin
de l'execution "mafonction" sauf à la rendre "static"



Ca ne plante pas, un warning à la compilation c'est tout.


Ca, c'est juste une question de chance. C'est un comportement indefini,
et ca va faire n'importe quoi dans des circonstances obscures. Le
compilo a parfaitement le droit de disposer de l'espace alloue a chaine
*des le retour* de la fonction, et je suis sur qu'il y a dans la nature
des implementations qui te giclent son contenu des que possible...

Pour le reste, ben c'est un gros classique de la gestion memoire en C.
Le code que tu as au dessu ne fonctionne pas. La variante ou tu declares
le `chaine' en static est a peine mieux.Bon dans ce cas precis, elle
fonctionne, mais sur des fonctions qui calculent des vrais trucs, ca
veut dire que la valeur de la fonction est utilisable jusqu'a ce que
tu l'appelles a nouveau, ce qui n'est pas forcement super-intuitif...
et les problemes de re-entrance ne tardent pas a montrer le bout de leur
nez.

En gros, trois solutions:
- gerer les allocations soi-meme:

char *ma_function()
{
char *chaine=malloc(20);
if (chaine)
strcpy(chaine, "Bonjour");
return chaine;
}

inconvenient: on se limite a l'utilisation de malloc, au detriment d'autres
allocateurs possibles.

- prevoir une fonction d'allocation de la memoire:
char *ma_fonction(void *(*alloc)(size_t, void *), void *udata)
{
char *chaine = alloc(20, udata);
...
}
inconvenient: un peu lourd.

- remonter la responsabilite d'un cran:
char *ma_fonction(char *espace_deja_alloue, int taille)
{
if (taille >= 20) {
strcpy(espace_deja_alloue, "Bonjour");
return espace_deja_alloue;
} else {
return 0;
}
}

inconvenient: faut savoir de quelle place on va avoir besoin, ou alors il
faut une deuxieme fonction qui dit de quelle place on va besoin, ce qui
revient presque a calculer deux fois les memes trucs...

La plupart des problemes d'allocation memoire en C reviennent plus ou
moins a gerer les compromis entre ces trois facons de faire... Tres souvent
la 3e technique est plus ou moins la bonne, sauf pour les calculs un peu
compliques et un peu long cote tailel de l'allocation... une bonne facon,
au moins pour les chaines, de gerer le probleme de la taille variable
consiste a avoir un gros tampon (eventuellement extensible) dans lequel
on fait tous les calculs et a faire l'allocation `just in time' a la
sortie de la fonction.



Avatar
Eric Levenez
Le 11/03/07 19:10, dans <45f445f5$0$5084$,
« Sylvain » a écrit :

Eric Levenez wrote on 11/03/2007 18:06:

Ce code n'utilise pas la pile mais le code de retour de la fonction. C'est
le principe même d'un code de retour.


euh, le code de retour est posé sur la pile


Normalement non. Peut-être que l'on peut trouver une architecture où ce que
tu dis est vrai, mais en règle générale le code de retour est dans un
registre du CPU, pas dans la pile.

soit mais comme l'est
sûrement le tableau non statique utilisé en paramètre local;


La tableau est dans un espace qui n'existe que DANS la fonction, pas à
l'extérieur, et là, effectivement, c'est généralement la pile.

la
différence est que l'appelant s'attend dans ce second cas à trouver
cette valeur de retour et la dépile correctement (préserve cette pile
avant de récupérer cette valeur) - dans le premier cas il dépile et
préserve l'adresse du tableau (et elle seule, non l'espace stockant son
contenu) ... une interruption et boum, comme tu l'as expliqué.


Conclusion basée sur des fausses hypothèses.

le C sait aujourd'hui retourner une structure, pour cela le compilo peut
utiliser la pile pour stocker le résultat complet.


Oui, le C peut retourner une structure, mais ce n'est pas dans le cas d'une
variable automatique, même si c'est une structure.

Et pour info, sur certaines architectures les petites structures sont
retournées dans des registres et pas dans la pile.

--
Éric Lévénez -- <http://www.levenez.com/>
Unix is not only an OS, it's a way of life.


Avatar
matt
Merci pour vos réponses...

une dernière question :

et ceci est ce correct ?

char* mafonction()
{
return "HELLO";
}

Merci encore

Matt...
1 2