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 :

Strcpy et segmentation fault...


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    88
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 88
    Par défaut Strcpy et segmentation fault...
    Bonjour,

    Je désire écrire une fonction qui me renvoit le nom du username et hostname (sous linux) dont voici le code :
    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
     
    #include "string.h"
    #include "stdlib.h"
    #include "stdio.h"
    #include "shell.h"
     
    char
    *prompt(int size) {
         char *user = getenv("USERNAME");
         char* host = getenv("HOSTNAME");
         char invite[200];
         strcpy(invite,user);    // Là, il veut pas
    //    strcat(invite,"@");
    //    strcat(invite,host);
         return (invite);
    }
    Compil ok, et segmentation fault à l'exécution ? Si je remplace le getenv(X) par une chaine de caractères, ca fonctionne correctement. Pourtant dans le man de getenv il est dit que la fonction retourne un pointeur sur la valeur recherchée. Sur le man de strcpy, il est dit que le deuxieme parametre (src) doit être un pointeur sur chaine. Tout me parait correct et je ne vois pas ce qui bloque ?

  2. #2
    Rédacteur/Modérateur
    Avatar de Trap D
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Par défaut
    Teste simplement si le retour de getenv est non NULL, on ne sait jamais ...
    "La haine seule fait des choix" - Koan Zen
    "Il ne faut pas être meilleur que les autres, il faut être meilleur que soi." Albert Jacquard
    "Ceux qui savent où ils ont posé leur parapluie ne sont pas alcooliques." - pgibonne.
    Faites du Prolog, ça vous changera les idées !
    Ma page Prolog
    Mes codes sources commentés

    Mon avatar : La Madeleine à la veilleuse de Georges de La Tour

  3. #3
    Membre émérite
    Avatar de Pouic
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    669
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 669
    Par défaut
    Tout simplement parceque la viariable USERNAME n'existe pas, et qu'elle se nomme USER ... Du coup, le pointeur retourné est nul..
    Software becomes slower faster than hardware becomes faster
    [size=1]
    http://xrenault.developpez.com

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    88
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 88
    Par défaut
    C'est là ou je me suis fait avoir :

    J'ai testé les variables d'env en mode root, et USERNAME existe en root, et je te l'accorde Pouic, elle n'existe pas en mode non root.

    Merci à vous 2

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    88
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 88
    Par défaut
    Voila le code complet :
    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    #include "string.h"
    #include "stdlib.h"
    #include "stdio.h"
    #include "shell.h"
     
    char
    *prompt() {
        char *user  = getenv("USERNAME");
        char *host  = getenv("HOSTNAME");
        char *aux   = getenv("PWD");
        char *rep   = aux + strlen(aux);
        char *der   = "#";      /* Invite admin */
     
        while(rep>=aux && *rep !='/') rep--;
        rep++;
     
        if(!user) {
                user=getenv("USER");
                der ="$";       /* Invite user */
        }
        if(!user || !host) return NULL;
     
        char *invite = (char*)      malloc(sizeof(char)*(strlen(user)+strlen(host)+strlen(rep)+strlen(der))+6);
     
        strcpy(invite,"[");
        strcat(invite,user);
        strcat(invite,"@");
        strcat(invite,host);
        strcat(invite," ");
        strcat(invite,rep);
        strcat(invite,"]");
        strcat(invite,der);
        strcat(invite," ");
        return (invite);
    }
    Et ça fonctionne Est-ce que c'est 'propre' ?

    Merci

  6. #6
    Membre émérite
    Avatar de Pouic
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    669
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 669
    Par défaut
    Bof... Tu ne contrôles pas les eventuels débordements mémoire lors de la concaténation... Utilise les fonctions strn* ....

    <EDIT>
    Beaucoup plus grave :
    Ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char *invite=malloc(sizeof(user)+sizeof(host)+sizeof(aux)+6);
    est completement faux. sizeof(user) ne fais pas ce que tu crois...
    Software becomes slower faster than hardware becomes faster
    [size=1]
    http://xrenault.developpez.com

  7. #7
    Expert confirmé

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Par défaut
    Et ça fonctionne Smile Est-ce que c'est 'propre' ?
    Je me demande pourquoi...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include "string.h"
    #include "stdlib.h"
    #include "stdio.h"
    #include "shell.h"
    devrait plutot etre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    //Je n'ai pas de shell.h...
    Par contre,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char *invite=malloc(sizeof(user)+sizeof(host)+4);
    est carrement faux puisque sizeof(user) et sizeof(host) donnent les tailles d'un pointeur de caractere et non la taille de la chaine... Ce genre de chose marche si c'est un tableau alloue statiquement...

    Il faut faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    char *invite = (char*) malloc(sizeof(char)*(strlen(user)+strlen(host))+3);
    +3 parce que je compte deux fois '\0', un pour user et un autre pour host...

    Derniere remarque bizarre, chez moi getenv("HOSTNAME") ne marche pas pourtant il existe dans les variables d'environnement... Je ne sais pas trop pourquoi... Donc il faudrait rajouter avant le debut de ta creation d'invite:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    if( (!user) || (!host))
      return NULL;
    Pour etre sur...

    Jc

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    88
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 88
    Par défaut
    Mise en application, merci

  9. #9
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par laurent_ifips
    Voila le code complet :
    Et ça fonctionne Est-ce que c'est 'propre' ?
    Non. Atroce !

    EDIT : qualifié les pointeurs sur chaine avec const.
    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
     
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
     
    static char *prompt (void)
    {
       char *invite = NULL;
       char const *user = getenv("USERNAME");
     
       if (user == NULL)
       {
          user = getenv("USER");
     
          if (user == NULL)
          {
             user = "";
          }
     
       }
     
       {
          char const *host = getenv("HOSTNAME");
     
          if (host == NULL)
          {
             host = "";
          }
     
          {
             char const *aux = getenv("PWD");
     
             if (aux == NULL)
             {
                aux = "";
             }
             {
                char const *rep = aux + strlen(aux);
     
                while (rep >= aux && *rep != '/')
                {
                   rep--;
                }
     
                rep++;
     
                invite = malloc(strlen(user) + strlen(host) + strlen(rep) + 6);
     
                if (invite != NULL)
                {
                   strcpy(invite, "[");
                   strcat(invite, user);
                   strcat(invite, "@");
                   strcat(invite, host);
                   strcat(invite, " ");
                   strcat(invite, rep);
                   strcat(invite, "]");
                   strcat(invite, "$");
                   strcat(invite, " ");
                }
     
             }
     
          }
       }
       return invite;
    }
     
    int main (void)
    {
       int end = 0;
     
       while (!end)
       {
          char *s = prompt();
     
          if (s != NULL)
          {
             printf ("%s", s);
             fflush (stdout);
             free (s), s = NULL;
          }
     
          {
             char line[128];
             fgets(line, sizeof line, stdin);
             end = strcmp(line, "quit\n") == 0;
          }
     
       }
       return 0;
    }
    (testé sous windows XP, ça calme!)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    [Papou@ ]$ ls
    [Papou@ ]$ help
    [Papou@ ]$ quit

  10. #10
    Expert confirmé

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Par défaut
    Remarque important:

    Ce genre de programmation:

    Donne apres l'idee aux nouveaux programmeurs de faire:
    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
     
    void f(char *s)
    {
            s[0] = '1';
    }
     
     
    int main()
    {
      char *s;
     
      s = "Bonjour";
     
      printf("%s\n",s);
     
      f(s);
     
      printf("%s\n",s);
     
      return 1;
    }
    Bien que cela marche en C, c'est generalement une mauvaise idee de faire quelque chose dans ce genre. Lorsqu'on regarde ce qui se passe, le compilateur mets en memoire "Bonjour" dans la section texte du programme (section en lecture seule) donc tout marche correctement (et le code d'Emmanuel est correct, propre et gere tous les problemes possibles) mais il ne faut pas oublier que le texte pointe est en lecture seule. C'est pour cela que le programme fera une erreur de segmentation lorsqu'on arrivera a la modification
    Il est difficile de trouver l'erreur dans un programme complique. On affiche la chaine et on se dit: "Mais elle existe!!! Pourquoi il refuse que j'ecrive dedans?????"

    Voila, je pense avoir tout dit...

  11. #11
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par fearyourself
    Remarque important:

    Ce genre de programmation:

    Donne apres l'idee aux nouveaux programmeurs de faire:
    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
     
    void f(char *s)
    {
            s[0] = '1';
    }
     
     
    int main()
    {
      char *s;
     
      s = "Bonjour";
     
      printf("%s\n",s);
     
      f(s);
     
      printf("%s\n",s);
     
      return 1;
    }
    Bien que cela marche en C,
    Ca compile, mais le comportement est indéfini.

    Ok, j'aurais dû qualifier les pointeurs avec const. Je corrige.

  12. #12
    Expert confirmé

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Par défaut
    Ca compile, mais le comportement est indéfini.
    Ce n'est pas vrai si je titille sur le mot indefini. En effet, le comportement de mon code est largement defini.

    Ce code provoquera toujours une erreur de segmentation vu qu'on modifie une portion de la memoire en lecture seule (la fameuse text segment)...

    Quoique je dis ca car lorsque je regarde ce que font les compilateurs gcc, icc ou opencc, ils mettent les chaines de caracteres dans la section texte de la memoire. Je suppose que si un compilateur etait assez intelligent pour voir qu'on veut modifier la chaine de caracteres, il pourrait le mettre dans la pile comme si c'etait un tableau (sauf qu'il faudrait l'initialiser a chaque appel...). Mais je n'ai jamais vu un compilateur le faire.

    Ai-je tort?

  13. #13
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par fearyourself
    Ca compile, mais le comportement est indéfini.
    Ce n'est pas vrai si je titille sur le mot indefini. En effet, le comportement de mon code est largement defini.
    Il est indéfini vu du C (qui ne sait pas ce qu'est une plateforme). Le comportement est imprévisible.
    Citation Envoyé par n1124
    ISO/IEC 9899:TC2 Committee Draft — May 6, 2005 WG14/N1124
    6.4.5 String literals
    Syntax
    1 string-literal:
    " s-char-sequenceopt "
    L" s-char-sequenceopt "
    s-char-sequence:
    s-char
    s-char-sequence s-char
    s-char:
    any member of the source character set except
    the double-quote ", backslash \, or new-line character
    escape-sequence
    Description
    2 Acharacter string literal is a sequence of zero or more multibyte characters enclosed in
    double-quotes, as in "xyz". A wide string literal is the same, except prefixed by the
    letter L.
    3 The same considerations apply to each element of the sequence in a character string
    literal or a wide string literal as if it were in an integer character constant or a wide
    character constant, except that the single-quote ' is representable either by itself or by the
    escape sequence \', but the double-quote " shall be represented by the escape sequence
    \".
    Semantics
    4 In translation phase 6, the multibyte character sequences specified by any sequence of
    adjacent character and wide string literal tokens are concatenated into a single multibyte
    character sequence. If any of the tokens are wide string literal tokens, the resulting
    multibyte character sequence is treated as a wide string literal; otherwise, it is treated as a
    character string literal.
    5 In translation phase 7, a byte or code of value zero is appended to each multibyte
    character sequence that results from a string literal or literals.66) The multibyte character
    sequence is then used to initialize an array of static storage duration and length just
    sufficient to contain the sequence. For character string literals, the array elements have
    type char, and are initialized with the individual bytes of the multibyte character
    sequence; for wide string literals, the array elements have type wchar_t, and are
    initialized with the sequence of wide characters corresponding to the multibyte character
    66) A character string literal need not be a string (see 7.1.1), because a null character may be embedded in
    it by a \0 escape sequence.
    sequence, as defined by the mbstowcs function with an implementation-defined current
    locale. The value of a string literal containing a multibyte character or escape sequence
    not represented in the execution character set is implementation-defined.
    6 It is unspecified whether these arrays are distinct provided their elements have the
    appropriate values. If the program attempts to modify such an array, the behavior is
    undefined.

    7 EXAMPLE This pair of adjacent character string literals
    "\x12" "3"
    produces a single character string literal containing the two characters whose values are '\x12' and '3',
    because escape sequences are converted into single members of the execution character set just prior to
    adjacent string literal concatenation.
    Forward references: common definitions <stddef.h> (7.17), the mbstowcs
    function (7.20.8.1).
    • Sur un PC sous DOS, pas de réaction visible si on compile avec Borland C.
    • Sur une machine embarquée, genre 8051, ca risque d'écrire dans la zone de donnée, alors qu'on croyait écrire dans la zone code!
    • Sur les machines à MMU, la réaction va dépendre de l'endroit où le compilateur a rangé les 'strings' et de la config de la MMU...

    Bref, c'est pas si simple...
    Ce code provoquera toujours une erreur de segmentation vu qu'on modifie une portion de la memoire en lecture seule (la fameuse text segment)...
    Tu raisonnes pour une impleméntation particulière (combinaison machine + système + compilateur/linker + otions). Tu n'as pas le droit de généraliser comme ça.
    Quoique je dis ca car lorsque je regarde ce que font les compilateurs gcc, icc ou opencc, ils mettent les chaines de caracteres dans la section texte de la memoire.
    Ils font ce qu'il veulent, le C n'en sait rien. Le C dit simplement 'une chaine littérale n'est pas modifiable'. Point.
    Je suppose que si un compilateur etait assez intelligent pour voir qu'on veut modifier la chaine de caracteres, il pourrait le mettre dans la pile comme si c'etait un tableau (sauf qu'il faudrait l'initialiser a chaque appel...). Mais je n'ai jamais vu un compilateur le faire.
    Ai-je tort?
    Mmmm... C'est au programmeur de faire ce qu'il faut :
    est parfaitement modifiable.

  14. #14
    Expert confirmé

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Par défaut
    Effectivement, tu n'as pas tort... J'ai une tendance a oublier qu'il y a beaucoup de facteurs externes!

    Comme tous les soirs, je dormirais moins bete, donc aujourd'hui n'aura pas ete perdu!

  15. #15
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    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 391
    Par défaut
    De plus, si vous êtes sous GCC, vous pouvez activer une optiosn qui déclare explicitement toutes les constantes-chaînes en const : L'option -Wwrite-strings, qui n'est jamais activée si on le la spécifie pas (même -Wall -Wextra ne l'active pas)
    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.

  16. #16
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    88
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 88
    Par défaut
    Bonjour,

    Merci pour toutes ces réponses, ca marchait avant, ca marche toujours que demander de plus

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

Discussions similaires

  1. Segmentation fault avec strcpy()
    Par tasna dans le forum C++
    Réponses: 13
    Dernier message: 26/04/2012, 01h05
  2. segmentation fault lors de l'utilisation de strcpy
    Par simplyc dans le forum Débuter
    Réponses: 18
    Dernier message: 17/01/2011, 19h52
  3. strcpy segmentation fault
    Par tipaquo dans le forum C
    Réponses: 10
    Dernier message: 03/01/2007, 15h24
  4. Réponses: 13
    Dernier message: 13/07/2004, 15h41
  5. Comment contrer la "segmentation fault" ?
    Par guillaume_pfr dans le forum C
    Réponses: 15
    Dernier message: 08/08/2003, 13h43

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