Fonction(char *s1, char **s2) segfault [Résolu/Fermé]

Signaler
-
 tranduil -
Bonjour,

Je cherche à réaliser une fonction qui prend une chaine s1 en paramètre et qui la copie dans l'adresse d'une chaine s2. Le prototype ressemble donc à ceci:
void      fonction(char *s1, char **s2);


A priori j'arrive à tout gérer sauf quand on appelle ma fonction avec NULL à la place de l'adresse d'une chaine de caractère. Soit:
int        main(void){
char     s1 = "coucou", *s2;
fontion(s1, &s2); //malloc &s2 a la bonne taille et y copie s1 char par char.
printf("%s", s2); //affiche s2 contenant alors s1.
fonction(s1, NULL); //segfault.
return (0);
}


l'idée est que mes premiers tests dans la fonction "fonction" doivent me mener à un return si je me rend compte que je n'ai pas une adresse de chaine de char dans char **s2. Soit un début qui correspondrais à ceci:
void      fonction(char *s1, char **s2){
if (s2 != NULL && *s2 != NULL)
   return ;
... //malloc de *s2 à strlen(s1)+1, copie de s1 dans s2, return.
}


Donc ce code marche mais bloque tout le reste de la fonction, c'est à dire que si il s'arrête bien quand on veut, il s'arrête aussi quand il y a des choses à exécuter après, il rentre donc toujours dans cette condition mais si j'enlève un bout de la condition, quand &s2 = NULL, le code passe le if et segfault.

4 réponses


Bonjour

Si je comprends bien tes explications malgré le code que tu ne donnes pas, dans fonction, si s2==NULL tu fais un malloc de *s2.
Mais si s2 est NULL, *s2 entraîne inévitablement une segfault.
Si tu as appelé ta fonction avec NULL en second paramètre, tu ne peux rien récupérer. Soit tu fais un return direct dans ta fonction si s2==NULL, soit tu l'appelles avec :
s2=NULL;
fonction(s1, &s2);

Cette dernière solution me semblant infiniment plus logique d'après ce que je comprends, puisqu'elle te permet de récupérer effectivement le résultat de l'allocation faite dans ta fonction.
actuellement, la fonction marche mais dans le cas de cet appel:
char     s1 = "coucou";
fonction(s1, NULL); //segfault.

il y a un segfault.

Quand j'appelle la fonction avec une adresse de chaine de char comme elle est faite pour recevoir, tout se passe bien.

Du coup, pour sécuriser contre les appels avec NULL à la place de l'adresse d'une chaine de caractère, j'ai réussi à trouver une condition ci-dessous:
void      fonction(char *s1, char **s2){
if (s2 != NULL && *s2 != NULL)
return ;
...
}

mais cette condition bloque la fonction quoi qu'il arrive car même lors d'un appel classique, on se retrouve avec une adresse de chaine de caractère qui vaut NULL vu qu'on as pas encore malloc la zone mémoire et donc je cherche une condition qui permette de bloque uniquement quand NULL est envoyé et non pas une adresse de chaine contenant elle NULL.

soit:
char    *s1 = "coucou", *s2;
fonction(s1, NULL); //segfault mais on veux obtenir un simple arrêt de la fonction.
fonction(s1, &s2); //ok.
s2 = NULL;
fonction(s1, &s2); //ok



PS: après le code de la fonction à rien de bien exceptionnel, il s'agit juste de manipulation de chaine de caractère..

J'ai l'impression que tu dis des choses contradictoires, probablement parce que tu ne fais pas très nettement la distinction entre un char* et un char **.
Si tu veux te protéger du cas ou le second paramètre de l'appel est NULL, il suffit de faire
if (s2==NULL) return;
, pourquoi fais-tu le test contraire ?
Et à quoi sert le test sur *s2 dans ta fonction ?
Messages postés
11066
Date d'inscription
samedi 5 mai 2007
Statut
Contributeur
Dernière intervention
18 octobre 2016
1 742
Déjà, il y a un gros problème.
char s1 = "coucou";
"coucou" est de type char* et tu veux le stocker dans un char. Problème...
if (s2 != NULL && *s2 != NULL)
return ;

C'est exactement l'inverse. Tu quittes si s2 est NULL ou si *s2 est NULL.
Bonsoir fiddy

J'avais aussi remarqué char s1 = "coucou"; mais ce n'est visiblement qu'une erreur de recopie, tranduil ne pourrait pas dire que son premier appel marche s'il avait vraiment écrit ça.
Hypothèse confortée par une autre erreur, fontion(s1, &s2); au lieu de fonction
Par contre, je ne vois pas pourquoi il doit quitter si *s2 est NULL, s'il veut justement faire une allocation ensuite et mettre le pointeur obtenu dans *s2.
Messages postés
11066
Date d'inscription
samedi 5 mai 2007
Statut
Contributeur
Dernière intervention
18 octobre 2016
1 742
Hello,
Pour char s1, je préfère le signaler plutôt que supposer que c'est une coquille (mauvaise expérience...).
Pour s2, j'avoue que je n'ai pas tout compris son besoin... Il y a des contradictions.

je ne vois pas pourquoi il doit quitter si *s2 est NULL, s'il veut justement faire une allocation ensuite et mettre le pointeur obtenu dans *s2.
C'est le contraire qu'il souhaite. Il veut que la fonction prenne fin si s2 n'est pas un pointeur sur une chaîne de caractères. Il faut donc vérifier que s2 et *s2 sont différents de NULL.
Utilisateur anonyme
Pour commencer, levons une ambiguïté : je parle du s2 de la fonction, qui est un pointeur sur un pointeur, et pas de la variable s2 du main qui est un pointeur sur un caractère.

Il veut que la fonction prenne fin si s2 n'est pas un pointeur sur une chaîne de caractères
Je ne le crois,pas, même s'il dit quelque chose qui ressemble à ça.
Il a décrit le rôle de sa fonction dans un commentaire :
//malloc de *s2 à strlen(s1)+1, copie de s1 dans s2, return.
Il veut simplement dupliquer une chaîne et rendre un pointeur sur cette copie, il a donc besoin que s2 ne soit pas null et pointe sur un 'vrai' pointeur (la variable s2 du main par exemple),mais la valeur initiale de ce 'vrai' pointeur (donc de *s2) peut bien être quelconque y compris null, il va l'écraser avec le résultat du malloc. D'ailleurs il dit bien que son premier appel fontion(s1, &s2); marche, alors que s2 (variable du main cette fois) n'est pas initialisé.
Je crois vraiment que le test de *s2 à l'intérieur de la fonction est inutile car la valeur pointée par s2 n'est pas utilisée, mais modifié par la fonction.
Messages postés
11066
Date d'inscription
samedi 5 mai 2007
Statut
Contributeur
Dernière intervention
18 octobre 2016
1 742
Il me semble que tu interprètes beaucoup ce qu'il souhaite :-).
Grosso modo, soit ce qu'il souhaite est juste et son code est faux. Soit c'est le contraire...
De toute façon, sans plus d'explication de sa part, on ne fait que des hypothèses. Donc...
Mes interprétations sont, il me semble, clairement justifiées à chaque fois.
Pour ses erreurs (caractère initialisée à une chaîne, erreur dans le nom de la fonction) son programme n'aurait simplement pas tourné du tout. Or il a une segfaut, donc le prograrmme compile.
Qu'est-ce qui est le plus plausible : qu'il a mal recopié le programme, ou qu'il a cru voir une segfault alors que c'était une erreur de compilation ?

D'autre part, il exprime très clairement qu'il souhaite recopier une chaîne et que pour ça il fait un malloc, et il retourne le pointeur obtenu. Pour ça, il a besoin que s2 ne soit pas NULL mais il n'a besoin d'aucune condition sur *s2.
Qu'est-ce qui est le plus plausible :
que son besoin soit simplement celui que je viens de décrire, et qu'il se prend les pieds dans le tapis en parlant de pointeurs, ou qu'il a vraiment besoin d'une condition sur *s2 qu'il ne justifie d'aucune manière ?
Je veux bien que tu parles d'interprétation (on interprète toujours), et il est très possible que je me trompe, mais accorde-moi que mes interprétations sont solidement étayées.

D'ailleurs je viens de m'apercevoir qu'il a lui même écrit :
une condition qui permette de bloque uniquement quand NULL est envoyé et non pas une adresse de chaine contenant elle NULL.
Il indique ici clairement lui-même qu'il ne veut pas de condition sur *s2
Alors le père as bien supposé, ce sont deux erreurs de frappes.
donc ma déclaration de coucou est en vrai écrite comme ceci:
char   s1[] = "coucou";

et mon appel de la fonction "fonction" as bien un c en plus a l'endroit indiqué.

Avec quelques tests plus poussé, en modifiant la condition comme ceci:
if (s2 == NULL) 
   return ;

Je me suis rendu compte que je vérifiait en effet plusieurs choses absurde mais bon, je commence à peine avec les pointeurs donc je m'embrouille encore un peu.

Merci beaucoup à fiddy de s'être penché sur le sujet et à "le père" de m'avoir fait réfléchir sur mon code avec ses remarques.

je passe le sujet en résolu ;)
Messages postés
11066
Date d'inscription
samedi 5 mai 2007
Statut
Contributeur
Dernière intervention
18 octobre 2016
1 742
Tant mieux :-).
Juste pour info, attention char *s1="coucou" est différent de char s1[]="coucou"
>
Messages postés
11066
Date d'inscription
samedi 5 mai 2007
Statut
Contributeur
Dernière intervention
18 octobre 2016

je crois avoir compris que le char *s1 nécessitait un appel de malloc pour définir un espace mémoire de la taille voulu alors que je char s1[] vas simplement attendre qu'on assigne une chaine à s1 pour allouer la mémoire mais de taille fixe.

l'avantage d'un char *s1, c'est qu'on peut réalloc, free, modifier le contenu par la suite...

c'est bien ça ?
Messages postés
11066
Date d'inscription
samedi 5 mai 2007
Statut
Contributeur
Dernière intervention
18 octobre 2016
1 742
char *s1 ne nécessite pas un malloc.
Tu peux par exemple faire : char *s1="coucou";
char *s1="coucou"; créé dans la mémoire en lecture seule la chaîne "coucou" et stocke l'adresse mémoire dans le pointeur s1.
Si tu fais s1[0]='C'; tu auras un joli segfault.
Si tu fais char s1[]="coucou"; la logique est différente. Cela créé un tableau de 7 caractères et stocke 'c', 'o', 'u', 'c', 'o', 'u', '\0' dans la chaîne.
Donc s1[0]='C'; ne plantera pas.
Après, effectivement, tu peux faire un malloc() et faire pointer s1 sur la zone allouée.
hmm, tout n'es pas encore très clair pour moi mais des année de développement me mènerons à la connaissance absolue.

en tout cas merci pour ces quelques précisions, tu semble t'y connaitre ^^.