C - structure

Résolu/Fermé
Woshi - Modifié par Woshi le 24/08/2011 à 11:28
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 - 24 août 2011 à 23:00
Bonjour,
J'ai un petit souci sur mon code, j'arrive à compiler, mais ça affiche pas les valeur définie dans mes variable.

typedef struct Personne Personne;    
struct Personne    
{    
char nom[100];    
char prenom[100];    
int age;    
int fille;    
};    

int main(int argc, char *argv[])    
{    
    Personne perso[2];    

    perso[0].nom[100] = "Dupuis";    
    perso[0].prenom[100] = "Jean";    
    perso[0].fille = 0;    

    perso[1].nom[100] = "Dupont";    
    perso[1].prenom[100] = "Jeanne";    
    perso[1].fille = 1;    

int i = 0;    
while( i<2 )    
{    

    printf("Nom : %s\nPrenom : %s\nFille : %d\n\n", perso[i].nom, perso[i].prenom, perso[i].fille);    
    i++;    
}    

    return 0;    
}    


Image de la compilation
http://imageshack.us/photo/my-images/59/captuready.png/

Merci pour votre aide :)

4 réponses

Bonjour

Ce que tu montres est l'image de l'exécution, pas de la compilation.
Pour copier une chaîne dans une autre en C, on n'utilise pas "=" mais la fonction strcpy : remplace
perso[0].nom[100] = "Dupuis";
par
strcpy (perso[0].nom,"Dupuis");
(idem pour les autres affectations de chaînes)
3
Groarh Messages postés 682 Date d'inscription vendredi 1 août 2008 Statut Membre Dernière intervention 28 juin 2015 185
Modifié par Groarh le 24/08/2011 à 11:47
Grillé ! J'allais dire la même chose.

Je rajoute qu'à la compilation avec GCC, j'ai ceci :
struct.c: In function 'main': 
struct.c:17:20: warning: assignment makes integer from pointer without a cast 
struct.c:18:23: warning: assignment makes integer from pointer without a cast 
struct.c:21:20: warning: assignment makes integer from pointer without a cast 
struct.c:22:23: warning: assignment makes integer from pointer without a cast

Ce n'est pas parce que ce sont des warnings qu'il faut les ignorer. Un warning en C est souvent équivalent à une erreur fatale en Java, car le compilateur C est très permissif. En l'occurence, ces messages indiquent tous un problème de pointeur qui aurait dû te mettre sur la voie, Woshi. Il suffit d'un peu d'habitude pour déchiffrer les messages du compilateur ;)

Ah aussi, un détail : pour utiliser strcpy, il faut importer string.h.
0
Le soucis vient de l'assignation des chaines de caracteres ("Dupuis", "Jean", etc)

Tu confonds 2 choses tres importantes, le fait de faire

char nom[100] = "Dupuis";

et

char nom[100];
nom[100] = "Dupuis";



sont des choses extremement differentes.
La premiere methode fonctionne, et evidemment, ce n'est pas celle que tu as utilisé, étant donné que tu as déclaré tes structures, et que ensuite tu as essayé d'y assigner des valeurs en chaine de caractère.

Ce que ça a fait exactement ?
Et bien en faisant

perso[0].nom[100] = "Dupuis";

Tu as mis la lettre D à la 100eme case de perso[0].nom

Bref, tu ne pourras pas reussir ce que tu essayes de faire sans utiliser de fonctions de la lib C (sauf si tu veux t'amuser a les recoder, mais bon tu n'en es pas encore la :p)

Bref, voila une solution

strcpy(perso[0].nom, "Dupuis");

(copie le 2eme argument dans le 1er)

Sinon, je te donne aussi une syntaxe pour faire un typedef sur une structure en meme temps que tu la declares, si ca peut t'aider

typedef struct Personne
{
char nom[100];
char prenom[100];
int age;
int fille;
} Personne;

Perso je trouve ça plus lisible ;)
2
J'ajoute quand même qu'il faut en général éviter d'utiliser des tableaux statiques quand son contenu est susceptible de changer, c'est un nid de failles ces choses là.

Et je ne parlerais même pas de strcpy, quand on sait comment il fonctionne, berk :(
0
Groarh Messages postés 682 Date d'inscription vendredi 1 août 2008 Statut Membre Dernière intervention 28 juin 2015 185
24 août 2011 à 12:14
Tu peux nous en dire plus sur strcpy ? Ça m'intéresse.
0
Profil bloqué
24 août 2011 à 12:18
suffit d'utiliser strncpy
0
à partir du moment ou on est à l'aise avec les pointeurs, il vaut mieux utiliser strdup et strndup qui réalloue la mémoire qu'il faut.

Ce que je n'aime pas avec strcpy ?
Premièrement, la mémoire dans la première chaine de caractère doit déjà être suffisament grande pour qu'on y colle une autre chaine.
Cela veut dire qu'avant l'appel de strcpy/strncpy, on se retrouve avec un tableau de par exemple 50 cases, alors qu'on ne voudrait en utiliser qu'une partie.
Ensuite, imaginons un cas concret (petit cours de sécurité ;))

Le programme consisterait a faire apparaitre des messages entrés par l'utilisateur.

J'ai donc une chaine de caractere

char message[100] = "message =";

j'ai volontairement utilisé un tableau statique pour cet exemple, avoir un pointeur sur une chaine allouée ne ferait aucune différence

l'utilisateur va donc entrer (en argv[1], en scanf ou autre) une chaine de caractere, qui sera passé en second argument de strcpy. Le code ressemblerait donc à ceci

strcpy(message, " j'ecris un message");
printf("%s\n", message);

Jusque la, pas de probleme, on aura bien "message = j'ecris un message" qui apparaitra lors de l'execution.

Cependant, si l'utilisateur entre un message suffisament grand pour remplir message[100] et va plus loin que la limite autorisé, il atteindra des cases mémoires ne faisant pas parti de ce tableau. Avec le script approprié, il pourra donc atteindre certaines parties sensibles.
un petit exemple (assez simplistes, mais qui suffira à montrer que ça marche pas comme on veut)

char test[2];
char test2[6] = "salut";

strcpy(test, test2);
ensuite, faites un printf de test et test2, vous devriez etre surpris.

On peut donc modifier les autres variables compris dans la fonction a volonté, mais il y a pire que ca. Apres les adresses des variables de la fonction, on arrive a l'adresse de retour de cette fonction. Cela veut dire que je peux ecrire un script dans ma chaine de caractere, et une fois que j'ai depassé cette chaine, je pourrais modifier l'adresse de retour de fonction pour qu'elle pointe sur le début de ma chaine. Le programme executera alors mon propre code, et je pourrais faire plein de bêtises avec :)
Il s'agit d'une faille de type "buffer overflow" (pour ceux que la sécurité intéresse, vous pourrez faire quelques recherches dessus).

Pour conclure, comme je le disais donc, si vous maitrisez les pointeurs, allouez vos chaines de caractères à la bonne taille et utilisez strdup/strndup, qui recréent une chaine de caractère avec la taille des 2 chaines passées en paramètre.

ps : je n'ai pas retesté l'exemple donné vu que je suis au boulot, mais si je ne me trompe pas, vous aurez comme résultat test = salut et test2 = lut, ou quelque chose dans le genre, à moins que ça seg fault tout simplement, auquel cas l'exemple n'illustrerait pas ce que j'essaye de vous montrer :p.
0
Groarh Messages postés 682 Date d'inscription vendredi 1 août 2008 Statut Membre Dernière intervention 28 juin 2015 185
24 août 2011 à 22:33
D'accord, c'est simplement que strcpy ne vérifie pas la longueur des deux chaînes. Je croyais que tu allais me parler d'un comportement imprévisible comme un effet de bord au autre...

strcpy n'est pas dangereux tant qu'on connaît la taille de ses chaînes. Et en l'occurence, c'est le cas. Et comme le fait remarquer Dualife, on peut toujours se rassurer en utilisant à la place strncpy.

Et sinon, il me semble que ça ne segfault pas tant qu'on reste dans la mémoire allouée au programme. Mais je ne sais plus si c'est le cas pour le heap. Question : est-ce que ça segfault si on tente de faire « déborder » une chaîne avec strdup ?

En tout cas, bravo, pour cette belle explication :)
0
Profil bloqué
24 août 2011 à 11:39
+1 le pere

et pour faire propre il faudrai que tu initialise ta memoire a zero
avec bzero par exemple
bzero(Personne perso, sizeof(Personne));
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 842
24 août 2011 à 23:00
Pour faire propre ? Je ne vois pas en quoi cela fait propre à part rendre le programme plus "secure" en cas de dump mémoire.
En tout cas, bzero n'est pas une fonction conseillée. Il vaut mieux utiliser memset.
0
Merci pour vos réponses :)
0