IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

histoire de char * qui fait Segmentation fault (core dumped)


Sujet :

C

  1. #1
    Membre confirmé
    Homme Profil pro
    Assistant aux utilisateurs
    Inscrit en
    Décembre 2011
    Messages
    61
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Assistant aux utilisateurs
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Décembre 2011
    Messages : 61
    Par défaut histoire de char * qui fait Segmentation fault (core dumped)
    Bonjour,
    Pour m'adonner aux joies du C (plus particulièrement les pointeurs) j'ai fait le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void deuxChaine(char *src)
    {
        /* ligne 1*/
        strcpy(src,"babar"); 
    }
     
    int main()
    {
        char *s;
        s="elephant";
        deuxChaine(s);
        printf("la première chaine est %s",s);
        printf("=============");
        return 0;
    }
    mais après avoir lu ça http://c.developpez.com/faq/?page=strings#STRINGS_pointeur
    la ligne 1 est devenue:
    le problème c'est lors de l'exécution qui me met un magnifique segmentation fault (core dumped) alors d'après ce que j'ai lu c'est un problème de mémoire mais je ne voie vraiment pas, si vous pouviez me donner une piste.
    PS: Le but du programme c'est de remplacer une chaîne par une autre chaîne qui se trouve dans une fonction

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonsoir,

    Si tu as bien lu le lien vers la FAQ que tu as donné, tu sais que la chaine vers laquelle tu fais pointer s : s="elephant"; n'est pas modifiable, or tu essaies justement de la modifier avec strcpy.
    Lis bien les commentaires des codes présentés.

  3. #3
    Membre confirmé
    Homme Profil pro
    Assistant aux utilisateurs
    Inscrit en
    Décembre 2011
    Messages
    61
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Assistant aux utilisateurs
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Décembre 2011
    Messages : 61
    Par défaut
    mince autant pour moi.... un copié coller malheureux
    la ligne 1 est:
    mais bon tu dis que s n'est pas modifiable.... donc la FAQ devrait me dire comment rendre s modifiable... je repars dans ma lecture

  4. #4
    Invité
    Invité(e)
    Par défaut
    Cette nouvelle ligne est d'autant plus fausse.
    *src ne représente que le premier caractère de ta chaine.
    Et si tu enlèves l'étoile, le paramètre src n'étant qu'une copie, s pointera toujours vers "elephant".

    Si tu souhaites rendre modifiable ta chaine, tu peux utiliser soit un tableau, soit allouer de la mémoire avec malloc().

  5. #5
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Tu peux aussi faire en sorte que ta fonction modifie le pointeur:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void deuxChaine(char const **ps)
    {
    	*ps = "babar";
    }
     
    int main()
    {
    	char const *s; /*Le pointeur peut être modifié, mais pas les données pointées*/
    	s="elephant";
    	deuxChaine(&s);
    	printf("la première chaine est %s",s);
    	printf("=============");
    	return 0;
    }
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Salut
    Comme l'a dit Winjerome, la zone mémoire contenant la chaine "elephant" est non modifiable.

    Citation Envoyé par Spirale21 Voir le message
    mais bon tu dis que s n'est pas modifiable.... donc la FAQ devrait me dire comment rendre s modifiable... je repars dans ma lecture
    Toutes les chaines "en dur" sont invariantes. Tu peux le vérifier en enlevant ta fonction et en écrivant simplement char *s="elephant";s[0]='A';.
    Donc tu ne peux rien faire pour changer ce fait. Si tu veux une zone modifiable, faut pas récupérer l'adresse d'une zone non modifiable.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void deuxChaine(char *src)
    {
        /* ligne 1*/
        strcpy(src,"babar"); 
    }
     
    int main()
    {
        char s[]="elephant";
        deuxChaine(s);
        printf("la première chaine est %s",s);
        printf("=============");
        return 0;
    }
    La différence avec ton code c'est que les caractères "elephant" (toujours situés dans une zone non modifiable) sont, cette fois, recopiés un à un dans le tableau lors de son initialisation. Et bien entendu ton tableau reste, lui, modifiable. Attention toutefois à ne pas lui en mettre plus que ce que la zone en contenait au départ...

    Citation Envoyé par Médinoc Voir le message
    Tu peux aussi faire en sorte que ta fonction modifie le pointeur:
    Ouais euh il débute quoi...
    Non sans rire c'est pas sympa. Tu lui fais croire qu'il peut modifier une zone qu'on annonce tous comme "non modifiable" alors qu'en fait tu changes simplement ton pointeur de zone (sauf que pour s'en rendre compte faut avoir déjà une bonne habitude...)
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #7
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Sauf que je dis explicitement dans les commentaires que c'est le pointeur qui change ici, pas les données pointées. Je déclare même le pointeur comme const pour bien montrer ça.


    ...Franchement, -Wwrite-strings devrait être actif par défaut, et son support rendu obligatoire dans la norme C.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #8
    Expert confirmé
    Avatar de Mat.M
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    8 527
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2006
    Messages : 8 527
    Par défaut
    Citation Envoyé par Spirale21 Voir le message
    mais bon tu dis que s n'est pas modifiable.... donc la FAQ devrait me dire comment rendre s modifiable... je repars dans ma lecture
    salut si tu veux rendre modifiable un pointeur sur une chaine de caractères c'est compliqué...
    pour éviter cela le mieux c'est d'allouer un pointeur de relativement grande taille avec malloc et puis mettons 100caractères comme ça tu peux faire un certain nombre de strcpy.
    Par contre il faut bien veiller à lorsque tu fais strcpy ou strcat de ne pas dépasser le nombre de caractères que tu as alloué avec malloc sinon au mieux tu as des fuites mémoires au pire des plantages.
    Gérer les chaines de caractère en langage C c'est pas facile et beaucoup moins qu'avec d'autres langages
    Car si tu as pris des cours de langage C , une chaîne de caractères en C c'est ni plus ni moins qu'un tableau de char que tu alloues de manière statique avec des crochets et un dimensionnement ou de manière dynamique par malloc.
    Les tableaux de caractères doivent toujours être terminés par le code \0 sinon encore une fois soit c'est plantage soit fuite mémoire.

    Le mieux encore c'est de créer une structure chaine_caractere qui alloue et réalloue le nombre de caractères requis en fonction de la taillle de chaine de caractères bref un embryon de std::string du C++ .
    Mais à l'utilisation ça ne sera pas très souple parce que en C il n'y pas de constructeur et évidemment encore moins de notion de classe comme il y a en C++
    Par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    typedef struct 
    {
     char *chaine_caracteres;
     int nombre_caracteres;
     
    } mon_type_chaine;
    pour corser un peu le tout tu peux modifier la structure avec un pointeur de fonction

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    typedef struct 
    {
     char *chaine_caracteres;
     int nombre_caracteres;
     void (*alloue)(int );//nombre_caracteres
    } mon_type_chaine;
    Et alloue() pointera sur une fonction qui fait un malloc

  9. #9
    Membre confirmé
    Homme Profil pro
    Assistant aux utilisateurs
    Inscrit en
    Décembre 2011
    Messages
    61
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Assistant aux utilisateurs
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Décembre 2011
    Messages : 61
    Par défaut
    Merci de vos réponses, j'avais pas tout lu et donc j'ai fait ma petite popote.. mon programme est le suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    void deuxChaine(char **src, char *m)
    {
        m=realloc(m,sizeof(char)*strlen("babar"));
        if (m==NULL) exit(-1);
        *src="babar";
    }
     
    int main()
    {
        char *s="elephant";
        char *mem_allouee;
        printf("\nla première chaine est %s",s);
        printf ("\nLa chaine fait %d car.",strlen(s));
        mem_allouee=malloc(sizeof(char)*strlen(s));
        if (mem_allouee==NULL) exit(-1);
        deuxChaine(&s,mem_allouee);
        printf("\nla première chaine est %s",s);
        printf("\n=============");
        free(mem_allouee);
        return 0;
    }
    c'est correct ou c'est du bricolage? ça marche mais j'ai l'impression que ce n'est pas propre!! et la ligne
    je ne maitrise pas très bien ça veut dire l'adresse d'un char * donc c'est un pointeur qui va contenir l'adresse d'un pointeur qui lui même contiendra l'adresse du début de la chaine ?
    Merci

  10. #10
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Spirale21 Voir le message
    c'est correct ou c'est du bricolage? ça marche mais j'ai l'impression que ce n'est pas propre!!
    Euh oui euh non euh en fait comment te dire... c'est ignoble !!!
    Commençons par ta fonction et ce char *m
    Elle reçoit donc une adresse issue d'un malloc (on va dire 0x10) qu'elle stocke dans m. Donc m contient 0x10.
    Ensuite, tu demandes une nouvelle zone mémoire pour remplacer la zone allouée en 0x10. Déjà réallouer une zone plus petite est inutile car par souci d'optimisation le C ne fera pas ce travail mais bon, la fonction se comporte comme si ça le faisait. Donc en admettant que ça marche, elle alloue une nouvelle zone (on va dire 0x20), y recopie tout ce qu'il y avait en 0x10 puis libère la zone 0x10 (c'est ça une reallocation, c'est allouer une zone ailleurs où il y a de la place, y recopier le contenu de la zone initiale et libérer la zone initiale).
    Ensuite la fonction se termine, et là ben la variable m disparait; et avec elle la valeur 0x20. Parce que t'as oublié que même pour une adresse, un paramètre de fonction n'est que copié. Et donc ta variable "mem_allouee" du main() contient toujours sa valeur initiale 0x10.
    Donc tu te retrouves avec une adresse 0x10 libérée par le realloc que tu vas de nouveau libérer dans le main (ici gros crash probable); et une zone allouée en 0x20 dont t'as perdu l'adresse et que tu ne pourras jamais libérer. T'es finaliste pour le programmeur de l'année !!!

    Comme je l'ai dit plus haut, il se trouve que ici le realloc() n'a rien fait parce que réallouer une zone plus petite que la zone initiale est inutile (si t'avais la place pour 100 ben t'as forcément la place pour 50 alors si tu demandes à changer en 50 c'est plus économique de ne rien faire et de te laisser tes 100). Donc au retour de ta fonction t'as toujours ta zone mémoire en 0x10 et t'as rien perdu. Mais c'est plutôt de la chance et la chance n'a rien à voir en prog. Surtout que ta zone réallouée tu en fais quoi dans ta fonction ???

    Maintenant ton main. Ben lui il alloue une zone et ensuite il en fait quoi ???

    Autre détail: on ne sort jamais d'une fonction par exit(). Si la fonction ne peut pas faire son travail alors elle renvoie le pb à l'appelant qui prendra une décision (le plus souvent lui-même renverra le pb à l'appelant et etc etc jusqu'au main).
    Citation Envoyé par Spirale21 Voir le message
    et la ligne
    je ne maitrise pas très bien ça veut dire l'adresse d'un char * donc c'est un pointeur qui va contenir l'adresse d'un pointeur qui lui même contiendra l'adresse du début de la chaine ?
    A la base c'est ça. Mais c'est aussi une façon de modifier une adresse.
    Prenons un exemple: si ta fonction doit modifier un int, tu dois lui passer l'adresse de ce int. Et la fonction, elle, stockera cette adresse dans un "int étoile".
    Maintenant si ta fonction doit modifier un "char étoile", c'est le même raisonnement. Tu dois lui passer l'adresse de ce "char étoile" et la fonction stockera cette adresse dans un "char étoile étoile". C'est ce que fait l'exemple de Médinoc. Il a une zone statique contenant les chaines statiques "elephant" et "babar" et un "char étoile s" qui contient l'adresse de la zone où est stockée la chaine "elephant" (on va dire 0x10). Il passe l'adresse de ce "s" à sa fonction. La fonction, connaissant l'adresse de "s" peut donc aller y écrire l'adresse de "babar" (on va dire 0x20). Et quand la fonction se termine, la variable "s" contient 0x20 qui correspond à l'adresse de "babar". Mais la zone 0x10 n'a pas disparu, c'est juste qu'elle est devenue inutilisée et inutile.

    Concernant ton code malheureusement il n'y a rien à en tirer. Si on corrige tout ce qui ne va pas et tout ce qui est inutile on retombe sur l'exemple de Médinoc ou sur le mien suivant si on décide de changer l'adresse de la zone ou de changer son contenu. Et si tu veux absolument faire un malloc() ben suffit de prendre mon exemple et remplacer le tableau par un pointeur que tu alloues au début et que tu libères à la fin...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  11. #11
    Membre confirmé
    Homme Profil pro
    Assistant aux utilisateurs
    Inscrit en
    Décembre 2011
    Messages
    61
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Assistant aux utilisateurs
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Décembre 2011
    Messages : 61
    Par défaut
    Merci pour ses explications : donc avec ta méthode le le string à affecter (babar) doit être inférieure à la chaîne d'origine (éléphant) même si ta déclaration ne comporte pas de limite? et avec l'exemple de Médinoc la taille peut être supérieure, mais comment libérer la chaîne originale (éléphant) qui prend de la place pour rien ?
    Au fait bon réveillon à tous

  12. #12
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Spirale21 Voir le message
    Merci pour ses explications : donc avec ta méthode le le string à affecter (babar) doit être inférieure à la chaîne d'origine (éléphant) même si ta déclaration ne comporte pas de limite?
    La limitation est créée par le compilo qui regarde la taille de la chaine à y mettre. C'est comme si j'avais écrit char s[9]="elephant"; (9 et non pas 8 car faut penser au '\0"). Mon tableau est alors prévu pour 9 caractères et je n'ai pas le droit d'en mettre plus. Toutefois si tu connais la taille max possible (par exemple tu sais que tu n'auras jamais de chaine de plus de 100 car) t'as tout à fait le droit d'écrire char s[101]="elephant";. Et dans ce cas, pour bien montrer aux autres lecteurs que tu as pris en compte le '\0' on écrit plus généralement char s[100 + 1]="elephant";...

    Citation Envoyé par Spirale21 Voir le message
    et avec l'exemple de Médinoc la taille peut être supérieure, mais comment libérer la chaîne originale (éléphant) qui prend de la place pour rien ?
    Inutile et impossible. Déjà rien qu'à taper printf("Bonjour") t'as une chaine "Bonjour" créée en mémoire qui ne sera utilisée qu'une seule fois...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. segmentation fault (core dumped)
    Par domdom974 dans le forum Fortran
    Réponses: 2
    Dernier message: 04/08/2009, 09h07
  2. [ASE 15.0.3] Segmentation Fault - core dumped
    Par dngaya dans le forum Adaptive Server Enterprise
    Réponses: 0
    Dernier message: 04/06/2009, 10h55
  3. segmentation fault (core dumped)
    Par miamiam dans le forum Débuter
    Réponses: 3
    Dernier message: 26/11/2008, 11h46
  4. Réponses: 1
    Dernier message: 10/12/2006, 21h37
  5. Segmentation fault (core dumped)
    Par Battosaiii dans le forum C
    Réponses: 13
    Dernier message: 25/11/2005, 18h36

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo