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 :

Problème char *var[]


Sujet :

C

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    156
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 156
    Points : 306
    Points
    306
    Par défaut Problème char *var[]
    Dans un exemple de code, que je pensais parfaitement comprendre, je trouve une déclaration de variable comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    char *param[] = {
    	"time", "20130423133428",
    	"hp", "000000000",
    	"hc", "000000000",
    	"ptec", "0",
    	"iinst", "00" };
    Bon, je pensais bêtement "tableau de pointeurs sur des chaines avec une init".
    Je check avec un simple truc genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    printf("%s", param[1]);
    qui me donne bien mon "20130423133428".
    Ok, mais si je fais un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strcpy(param[1], "12345678901234");
    j'ai un plantage (je dois écrire dans une zone mémoire qui n'est pas celle de la variable). Bizarre non ?

  2. #2
    Membre régulier
    Profil pro
    Ingénieur
    Inscrit en
    Avril 2013
    Messages
    77
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur

    Informations forums :
    Inscription : Avril 2013
    Messages : 77
    Points : 107
    Points
    107
    Par défaut
    Bonjour,

    Je te redonne la définnition de strcpy:
    char * strcpy ( char * destination, const char * source );
    Le premier paramètre est un POINTEUR vers la destination, or tu appelles la fonction comme ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strcpy(param[1], "12345678901234");
    Au lieu de passer comme premier paramètre un pointeur, tu indiques la valeur à l'emplacement 1.
    Il faut que tu remplaces par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strcpy(&param[1], "12345678901234");

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 381
    Points : 41 578
    Points
    41 578
    Par défaut
    Les chaînes littérales sont considérées char* pour raisons de compatibilité antérieures, mais elles sont en réalité placées dans une zone mémoire en lecture seule sur la plupart des plate-formes modernes. Si tu tentes d'en modifier une, tu auras une violation d'accès/erreur de segmentation.

  4. #4
    Membre expert
    Avatar de Metalman
    Homme Profil pro
    Enseignant-Chercheur
    Inscrit en
    Juin 2005
    Messages
    1 049
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Enseignant-Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 049
    Points : 3 528
    Points
    3 528
    Par défaut
    Son essai était bon (car de type char **), mais je suppose que tu tombes sur un "bus error" (et non un "segmentation fault"), car tu essayes d'écrire dans une zone mémoire read-only (car déclarée à la compilation).

    Si tu souhaites recopier, tu dois mallocer, ou strdup, ou strcpy dans char[une_taille]

    EDIT : Médinoc plus rapide !

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    156
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 156
    Points : 306
    Points
    306
    Par défaut
    Ok, merci a tous.
    Mon compilateur étant "archaïque" (Microchip MPLAB MC30 pour un PIC), je suis un peu étonné. Et pas facile de debugger, car je n'ai aucun outils genre gdb ou autres Quand mon PIC fait un reset, je suppose que quelque chose ne s'est pas bien passé
    En tout cas, je contourne avec quelque chose dans le genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char timest[15];
     
    strcpy(timest, "12345678901234");
    param[1] = timest;

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    34
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 34
    Points : 44
    Points
    44
    Par défaut
    Citation Envoyé par alyma Voir le message
    Bonjour,

    Je te redonne la définnition de strcpy:
    char * strcpy ( char * destination, const char * source );
    Le premier paramètre est un POINTEUR vers la destination, or tu appelles la fonction comme ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strcpy(param[1], "12345678901234");
    Au lieu de passer comme premier paramètre un pointeur, tu indiques la valeur à l'emplacement 1.
    Il faut que tu remplaces par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strcpy(&param[1], "12345678901234");
    Euh non. param[1] est bien de type char *. Ta correction est pire que l'erreur.

    Après, c'est toujours très dangereux d'écrire dans une zone de ce genre, définie par des pointeurs non dimensionnés. Car, en déclarant
    le compilateur réserve la place pour ce qui a été affecté et pas plus.

    La bible du C de Kerdigan & Richie dit d'ailleurs que si on essaie d'écrire sur une zone mémoire définie par un pointeur et non un tableau, le résultat est indéterminé. Je suppose donc que certains compilateurs, comme le souligne Medinoc, considèrent ces zones comme en lecture seule.

    Ceci étant dit, même si on pouvait écrire dessus, le danger est, si on veut écrire plus de 14 caractères dans params[1], on commencerait à écrire dans params[2].

    C'est donc fortement déconseillé.

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 397
    Points : 23 761
    Points
    23 761
    Par défaut
    Hello,

    Citation Envoyé par olivier_u Voir le message
    La bible du C de Kerdigan & Richie dit d'ailleurs que si on essaie d'écrire sur une zone mémoire définie par un pointeur et non un tableau, le résultat est indéterminé. Je suppose donc que certains compilateurs, comme le souligne Medinoc, considèrent ces zones comme en lecture seule.
    Non, cela n'a rien à voir. Si on pense bien à la même chose, ça veut dire que si tu passes le nom d'un tableau quelque part, il sera bien résolu en son adresse et que, donc, tu vas bien écrire dans l'espace que tu as réservé en le déclarant. Si tu passes un pointeur à la place, c'est le contenu de ce pointeur qui sera évalué et qui, par défaut, est complètement indéfini tant que tu ne lui as pas affecté quelque chose. L'adresse effective peut alors renvoyer n'importe où, ce qui est d'autant plus vrai que tu n'as pas alloué d'espace.

    Les chaînes de caractères constantes, quant à elles, sont les chaînes que tu spécifies directement dans le code, entre guillemets. Ça veut donc dire qu'elles doivent exister avant l'exécution et qu'elles se retrouvent dans le fichier exécutable. Au chargement de celui-ci, les pointeurs qui les référencent pointent donc « dans le code » et, aujourd'hui, dans un section distincte. Mais cette section reste très différente de la pile ou de la mémoire allouée dans le tas.

    Ceci étant dit, même si on pouvait écrire dessus, le danger est, si on veut écrire plus de 14 caractères dans params[1], on commencerait à écrire dans params[2].
    Non plus, parce que ce que singman a déclaré, c'est un tableau de pointeurs, ce qui est tout-à-fait correct. On a donc un tableau de n pointeurs consécutifs, et chacun d'eux contient l'adresse d'une chaîne constante qui, elle, se trouve n'importe où dans la section en lecture seule. Et contrairement aux pointeurs d'un même tableau, ces différentes chaînes sont indépendantes entre elles et n'ont pas spécialement à être consécutives.

    Ça veut donc dire que le code de singman est tout-à-fait légal, mais que les valeurs initiales des pointeurs renvoient vers des données en lecture seule et qu'il n'est pas possible d'y écrire.

    Enfin, Il est important de bien mettre en lumière que c'est le programmeur C qui gère lui-même ses chaînes et la mémoire qu'elles occupent. « const char * » n'est pas l'équivalent d'un type « string ». Affecter une nouvelle valeur à l'un des pointeurs du tableau est tout-à-fait autorisé, il pointera alors une nouvelle chaîne, mais ça ne veut pas dire que la précédente sera automatiquement détruite. Dans le cas présent, ça ne change rien, mais dans le cas d'un buffer alloué à la volée, ça peut conduire à une fuite de mémoire.

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    34
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 34
    Points : 44
    Points
    44
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Hello,

    Non, cela n'a rien à voir. Si on pense bien à la même chose, ça veut dire que si tu passes le nom d'un tableau quelque part, il sera bien résolu en son adresse et que, donc, tu vas bien écrire dans l'espace que tu as réservé en le déclarant. Si tu passes un pointeur à la place, c'est le contenu de ce pointeur qui sera évalué et qui, par défaut, est complètement indéfini tant que tu ne lui as pas affecté quelque chose. L'adresse effective peut alors renvoyer n'importe où, ce qui est d'autant plus vrai que tu n'as pas alloué d'espace.

    Les chaînes de caractères constantes, quant à elles, sont les chaînes que tu spécifies directement dans le code, entre guillemets. Ça veut donc dire qu'elles doivent exister avant l'exécution et qu'elles se retrouvent dans le fichier exécutable. Au chargement de celui-ci, les pointeurs qui les référencent pointent donc « dans le code » et, aujourd'hui, dans un section distincte. Mais cette section reste très différente de la pile ou de la mémoire allouée dans le tas.
    Attends.
    Je veux bien être très fatigué, mais dans la définition suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    char *param[] = {
    	"time", "20130423133428",
    	"hp", "000000000",
    	"hc", "000000000",
    	"ptec", "0",
    	"iinst", "00" };
    cela va définir une zone mémoire contenant :
    time\020130423133428\0hp\0000000000\0... et ainsi de suite.
    param[0] sera un char * qui pointera sur le 't' de "time", param[1] sur le '2' de "20130423133428" et ainsi de suite, non?

    Donc, la zone est allouée directement dans le fichier exécutable, c'est donc pourquoi on ne peut écrire dessus (si je comprends bien). Je dois admettre que je n'avais jamais réfléchi à la question.


    Citation Envoyé par Obsidian Voir le message
    Non plus, parce que ce que singman a déclaré, c'est un tableau de pointeurs, ce qui est tout-à-fait correct. On a donc un tableau de n pointeurs consécutifs, et chacun d'eux contient l'adresse d'une chaîne constante qui, elle, se trouve n'importe où dans la section en lecture seule. Et contrairement aux pointeurs d'un même tableau, ces différentes chaînes sont indépendantes entre elles et n'ont pas spécialement à être consécutives.
    Par contre, il me semble bien que dans le bouquin que j'ai au boulot (je relirai demain), K&R disent que les chaînes seront collées les unes derrière aux autres. Ceci étant, ce n'est pas très gênant.


    Citation Envoyé par Obsidian Voir le message
    Ça veut donc dire que le code de singman est tout-à-fait légal, mais que les valeurs initiales des pointeurs renvoient vers des données en lecture seule et qu'il n'est pas possible d'y écrire.

    Enfin, Il est important de bien mettre en lumière que c'est le programmeur C qui gère lui-même ses chaînes et la mémoire qu'elles occupent. « const char * » n'est pas l'équivalent d'un type « string ». Affecter une nouvelle valeur à l'un des pointeurs du tableau est tout-à-fait autorisé, il pointera alors une nouvelle chaîne, mais ça ne veut pas dire que la précédente sera automatiquement détruite. Dans le cas présent, ça ne change rien, mais dans le cas d'un buffer alloué à la volée, ça peut conduire à une fuite de mémoire.
    Là, pour le coup, je ne crois pas avoir dit le contraire. Je suis tout à fait d'accord avec toi.

  9. #9
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 397
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 397
    Points : 23 761
    Points
    23 761
    Par défaut
    Citation Envoyé par olivier_u Voir le message
    cela va définir une zone mémoire contenant :
    time\020130423133428\0hp\0000000000\0... et ainsi de suite.
    param[0] sera un char * qui pointera sur le 't' de "time", param[1] sur le '2' de "20130423133428" et ainsi de suite, non?

    Donc, la zone est allouée directement dans le fichier exécutable, c'est donc pourquoi on ne peut écrire dessus (si je comprends bien). Je dois admettre que je n'avais jamais réfléchi à la question.
    En pratique, tu vas effectivement retrouver toutes tes chaînes accolées ensemble mais ce n'est pas une obligation. Ça devient flagrant lorsque tu écris :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char * tab[] = { "un", "deux", "trois", "quatre", "deux" ,"cinq" };

    Les compilateurs sont pour ainsi dire tous capables de repérer les chaînes en double dans un code et ne les définir qu'une fois. Donc, ici, les pointeurs tab[1] et tab[4] auront la même valeur.

    Par contre, il me semble bien que dans le bouquin que j'ai au boulot (je relirai demain), K&R disent que les chaînes seront collées les unes derrière aux autres. Ceci étant, ce n'est pas très gênant.
    Je n'ai pas regardé dans le K&R, mais je n'ai rien vu de tel dans la norme. Par contre, il y a deux choses qui restent vraies :

    • Les éléments d'un tableau sont toujours consécutifs. Ici, les éléments sont les pointeurs vers des chaînes qui, elles, peuvent se trouver n'importe où (même si elles seront probablement accolées dans les faits) ;
    • Le fait de faire se succéder plusieurs lexons tels que « "un" "deux" "trois" » est considéré comme une seule et unique chaîne.

Discussions similaires

  1. [PHP 5.0] Problème de var session sous IE
    Par Skyl_smoi dans le forum Langage
    Réponses: 1
    Dernier message: 18/06/2009, 18h39
  2. Problème 4D Var
    Par SwiD86 dans le forum 4D
    Réponses: 2
    Dernier message: 20/01/2009, 18h23
  3. problème char-actéristique avec des pointeurs
    Par Antigonos Ier Gonatas dans le forum C
    Réponses: 11
    Dernier message: 16/04/2007, 21h22
  4. [javascript] problème char-int
    Par LE NEINDRE dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 16/02/2006, 17h32
  5. [PostgreSql] Problème de cadreage de char !!!
    Par moipastoi dans le forum Requêtes
    Réponses: 3
    Dernier message: 08/05/2003, 18h01

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