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 :

memcpy sur tableau de structure


Sujet :

C

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2005
    Messages : 72
    Points : 54
    Points
    54
    Par défaut memcpy sur tableau de structure
    Bonsoir tout le monde,
    je suis en train d'écrire un prog qui .... je vois pas trop comment expliquer
    En gros je bosse dans un société qui manipule à longueur de journée des fichiers sur des machines Linux. Nous développons tous nos softs en interne (tri, dédup...)
    Quelqu'un de chez nous à developper qq chose qui nous permet de nommer les champs d'un fichier texte comme on le fait sur un fichier dbase ou une base de donnée.
    cela nous permet de dire : tri le fichier sur le champ nomDeFamille au lieu de dire tri le fichier sur le champ 34.
    Ca c'était pour la petite histoire.

    Je suis en train d'écrire un truc qui va permettre "d'utiliser des alias de nom".
    Cela permettra au programme de dire que les champs nom et nomDeFamille représente la même chose.

    L'idée est de stocker dans un tableau de structure les couples source->destination.
    Malheureusement chez moi, pointeur rime avec "mais qu'elle horeur". J'ai un mal de chien avec ce truc. Si bien que les copies de valeurs que je fais dans le programme ci dessous ne donnent pas le résultat attendu.

    voici le source avec un max de commentaire

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
    int flagtrace=0;
    #include "config.c"
    #include "signatur.c"
    #include "structure.cpp"
    
    #define MAXALIAS 512
     
    struct salias {
    	char *de;
    	char *vers;
    	} listeAlias;
    
    int main(int argc, char *argv[]){	
    
    	// utilisés pour la table d'alias 
    	FILE	*falias;
    	char	ligne[512]; //stocke la ligne en cours de lecture
    	char 	*nomFicAlias;  // Nom du fichier contenant les alias
    	char	*p, *separChampAlias, *separVal, *destAlias;
    	char	*cleanSrc, *cleanDst; // utilisé pour nettoyer les noms
    
    	int i,j,l,lgclean;
    	int nbligneAlias;
    	
    	separChampAlias="="; // Séparateur de=>vers dans le fichier alias
    	separVal=",";  // Séparateur de valeurs dans fichier alias.
    	cleanSrc="ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöùúûüýÿ"; // utile pour supprimer les accents dans les noms de destinations.
    	cleanDst="AAAAAACEEEEIIIINOOOOOUUUUYaaaaaaceeeeiiiinooooouuuuyy"; // les caractères du dessus seront remplacés par ceci.
    	lgclean=strlen(cleanSrc); 
    
    	for (i=1;i<argc;i++) {
    		p=argv[i];
    		if (strncmp(p,"alias=",6)==0)
    			nomFicAlias=p+6;
    	}
    
    	salias listeAlias[MAXALIAS];
    	nbligneAlias=0;
    	lgclean=strlen(cleanSrc); 
    	
    	falias=fopen(nomFicAlias,"r");
    	while (fgets(ligne,sizeof(ligne),falias)) {
    		if (nbligneAlias>=MAXALIAS){
    			tracemsg(ERREUR,"Nombre d'alias trop important. Valeur max=%i.\n",MAXALIAS);
    			return(1);
    		}
    		i=strlen(ligne)-1; ligne[i]=0; //suppression du caracatére de fin de ligne 
    		
    		/* On sépare nom de destination et la liste de sources
    		   A ce moment, on se retrouve avec d'une part 
    		  le nom de destination,
    		  et d'autre part, la liste des noms(séparés par une virgule) qui prendront cette valeurs 
    		*/
    		p=strtok(ligne, separChampAlias);
    		destAlias=p; //On stocke temporairement le nom de destination car il peut etre utilisé plusieur fois . A mon avis, c'est pas la bonne facon de faire.
    		// Petit nettoyage du nom de destination. prénom devient prenom
    		i=strlen(destAlias);
    		for (j=0;j<i;j++){
    			for (l=0;l<lgclean; l++){
    				if (destAlias[j]==cleanSrc[l])
    					destAlias[j]=cleanDst[l];
    			}
    		}
    			
    		/* et on prend toutes les valeurs source possibles qui seront stocké dans le membre listeAlias[].de
    		on prodruit un couple de et vers pour chaque valeur source. Soit   prenom=pren,prenomType donneront 2 éléments
    		*/
    		while (p!=NULL){	
    			p=strtok(NULL, separVal);
    			if (p!=NULL){
    				listeAlias[nbligneAlias].vers= destAlias; // ceci doit etre faux
    				listeAlias[nbligneAlias].de=p; //ceci aussi 
    				tracemsg(TRACE,"le champ '%s' devient '%s'.\n",listeAlias[nbligneAlias].de,listeAlias[nbligneAlias].vers );
    				nbligneAlias++;
    			}
    		}
    	}
    	
    	for (i=0; i<nbligneAlias;i++){
    		tracemsg(TRACE,"%s = %s\n ",listeAlias[i].de,listeAlias[i].vers); // ici c'est pas beau comme resultat :(
    	}
    }

    le programme prend en l'argument alias=fichieralias

    le fichier alias contient ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    adr1=adresse1,adresse
    rs1=raisonsociale1,etp1
    nomFamille=nom

    J'ai mis en gras les parties que je pense fause.

    L'execution me donne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    htristra@leo:~/test$ /exec/Linux.i686/alfa/testrv2 alias=ficalias
    le champ 'raisonsociale' devient 'rkkkks1'.
    le champ 'rs' devient 'rkkkks1'.
    le champ 'raisonsociale2' devient 'rs2i'.
    le champ 'etp2' devient 'rs2i'.
    le champ 'nom' devient 'nomFamille'.
    le = nomFamille
     p2 = nomFamille
     mille = nomFamille
     etp2 = nomFamille
     nom = nomFamille
    A mon avis, ca plante car j'attribue un pointeur (ou son adresse plutot) à un tableau de char. Je pense que c'est pour cela que les premiers echo sont bon (pas encore de modif de p) et que les 5 dernieres lignes affiche toutes nomFamille (elles pointent tous vers la meme adresse)

    J'ai essayé le memcpy pour copier la valeur mais cela me donne une superbe "erreur de segmentation" .

    Si quelqu'un pouvait m'expliquer la bonne facon de copier la valeur de ce fichu p dans listeAlias[].de et listeAlias[].vers, ce serait merveilleux.


    Merci d'avance et bon week-end

  2. #2
    Expert éminent sénior

    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 : 43
    Localisation : Etats-Unis

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

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Bien le bonsoir,

    Ce serait bien que ton code soit compilable or il ne l'est pas... Du coup, on ne peut que donner des avis sur le programme... Voici quelques remarques :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    	falias=fopen(nomFicAlias,"r");
    Il faudrait un test pour vérifier que l'ouverture s'est bien passé...

    Ensuite, lorsque j'ai viré les lignes en trop, compris comment faire tourner le programme je remarque que j'ai comme sortie :
    le champ 'adresse1' devient 'adr1'.
    le champ 'adresse' devient 'adr1'.
    le champ 'raisonsociale1' devient 'rs1'.
    le champ 'etp1' devient 'rs1'.
    le champ 'nom' devient 'nomFamille'.
    mille = nomFamille
    = nomFamille
    amille = nomFamille
    etp1 = nomFamille
    nom = nomFamille
    par rapport à ce que tu disais :

    htristra@leo:~/test$ /exec/Linux.i686/alfa/testrv2 alias=ficalias
    le champ 'raisonsociale' devient 'rkkkks1'.
    le champ 'rs' devient 'rkkkks1'.
    le champ 'raisonsociale2' devient 'rs2i'.
    le champ 'etp2' devient 'rs2i'.
    le champ 'nom' devient 'nomFamille'.
    le = nomFamille
    p2 = nomFamille
    mille = nomFamille
    etp2 = nomFamille
    nom = nomFamille
    Bref, continuons...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    i=strlen(ligne)-1; ligne[i]=0; //suppression du caracatére de fin de ligne
    Plus lisible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    i=strlen(ligne)-1;
     ligne[i]=0; //suppression du caracatére de fin de ligne
    Plus correct :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    i=strlen(ligne)-1;
    if((i>=0)&&(ligne[i]=='\n'))
       {
       ligne[i]='\0'; //suppression du caracatére de fin de ligne 
       }
    A savoir que le test i>=0 est inutile ici parce que sinon cela voudrait dire que strlen a rendu 0 et donc que fgets a remplit une table vide ce qui est impossible vu qu'il n'a pas rendu NULL (il y a donc au moins un caractère dans ligne...). Mais bon, c'est bon usage et on discute de faire marcher ce programme...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    p=strtok(ligne, separChampAlias);
    Attention à cette fonction, elle est à éviter en principe... Mais dans ce cas, cela a l'air de bien se passer...

    On continue et on découvre ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    listeAlias[nbligneAlias].vers= destAlias; // ceci doit etre faux
    listeAlias[nbligneAlias].de=p; //ceci aussi
    Effectivement c'est faux...
    Ici, tu récupère seulement l'adresse, il faudrait copier la chaîne pointée... Donc faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    strcpy(listeAlias[nbligneAlias].vers,destAlias);
    strcpy(listeAlias[nbligneAlias].de,p);

    Bien sûr, cela veut dire qu'on a alloué de l'espace pour stocker ces chaînes... Donc allocation dynamique ou statique... Ici on va dire statique donc ta structure va devenir :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct salias {
    	char de[128];
    	char vers[128];
    	};
    Et si je relance le programme, j'obtiens :

    le champ 'adresse1' devient 'adr1'.
    le champ 'adresse' devient 'adr1'.
    le champ 'raisonsociale1' devient 'rs1'.
    le champ 'etp1' devient 'rs1'.
    le champ 'nom' devient 'nomFamille'.
    adresse1 = adr1
    adresse = adr1
    raisonsociale1 = rs1
    etp1 = rs1
    nom = nomFamille
    Jc

    PS: A savoir qu'il manque un return à la fin du main...

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Juillet 2005
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2005
    Messages : 72
    Points : 54
    Points
    54
    Par défaut
    Bonjour,

    merci pour tes infos fearyourself.

    J'ai fait toutes les modifs que tu m'a conseillé (protection de open, strcpy, caractère de fin de ligne, ...) et ca marche très bien.

    je vais maintenant essayé de remplacer le strtok par un boucle while. Si le strtok peut ête douteux, je préfére le remplacer.

    Merci encore et bonne fin de week-end.

  4. #4
    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
    Points : 6 498
    Points
    6 498
    Par défaut
    Pour ce qui est du strtok, il faut savoir
    1. qu'il n'est pas réentrant, c'est à dire que si deux thread distincts d'un même programme utilisent en même temps strtok, il y aura un problème de mélange des données,
    2. qu'il modifie la chaîne passée en argument, ce qui peut au choix provoquer des plantes si tu lui passes une chaîne non modifiable ou te retrouver avec des données incohérentes si tu utilises cette chaîne après l'usage de strtok, à par ça il fonctionne très bien
    "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

  5. #5
    Membre éprouvé

    Profil pro
    Inscrit en
    Août 2003
    Messages
    878
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 878
    Points : 1 067
    Points
    1 067
    Par défaut
    Citation Envoyé par fearyourself
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    strcpy(listeAlias[nbligneAlias].vers,destAlias);
    strcpy(listeAlias[nbligneAlias].de,p);

    Bien sûr, cela veut dire qu'on a alloué de l'espace pour stocker ces chaînes... Donc allocation dynamique ou statique... Ici on va dire statique donc ta structure va devenir :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct salias {
    	char de[128];
    	char vers[128];
    	};
    Un p'tit avis qui ne vaut que ce qu'il vaut : ce qui serait pas mal aussi, c'est de prendre l'habitude d'utiliser strncpy() au lieu de strcpy() (d'autant plus quand c'est pour copier vers un buffer dont la taille est fixe).
    Un problème bien exposé
    est, pour moitié, solutionné. / La connaissance s'accroît quand on la partage, pas quand on l'impose. / La violence est le langage des faibles.

  6. #6
    Expert éminent sénior

    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 : 43
    Localisation : Etats-Unis

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

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par David.Schris
    Un p'tit avis qui ne vaut que ce qu'il vaut : ce qui serait pas mal aussi, c'est de prendre l'habitude d'utiliser strncpy() au lieu de strcpy() (d'autant plus quand c'est pour copier vers un buffer dont la taille est fixe).
    En effet, mais il ne met pas forcément un '\0' à la fin s'il y a saturation... Il faudra donc penser à gérer ce cas...

    Jc

  7. #7
    Membre éprouvé

    Profil pro
    Inscrit en
    Août 2003
    Messages
    878
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 878
    Points : 1 067
    Points
    1 067
    Par défaut
    Citation Envoyé par fearyourself
    En effet, mais il ne met pas forcément un '\0' à la fin s'il y a saturation... Il faudra donc penser à gérer ce cas...

    Jc
    C'est quand même moins gênant qu'un strcpy() qui ne gère même pas le dépassement du buffer de destination...

    Mais c'est vrai qu'il y a encore mieux (mais peut-être pas encore présent sur tous les systèmes) : strlcpy() et strlcat().

    Une autre idée pour le même prix : que les modérateurs/experts du forum se mettent d'accord pour pousser les débutants à ne plus utiliser strcpy() et strcat().
    Un genre de propagande pro-sécurité...
    Un problème bien exposé
    est, pour moitié, solutionné. / La connaissance s'accroît quand on la partage, pas quand on l'impose. / La violence est le langage des faibles.

  8. #8
    Membre averti
    Avatar de Foobar1329
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    283
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Juin 2006
    Messages : 283
    Points : 387
    Points
    387
    Par défaut
    Hello,

    Citation Envoyé par David.Schris
    C'est quand même moins gênant qu'un strcpy() qui ne gère même pas le dépassement du buffer de destination...

    Mais c'est vrai qu'il y a encore mieux (mais peut-être pas encore présent sur tous les systèmes) : strlcpy() et strlcat().

    Une autre idée pour le même prix : que les modérateurs/experts du forum se mettent d'accord pour pousser les débutants à ne plus utiliser strcpy() et strcat().
    Un genre de propagande pro-sécurité...
    Bon il est tard et j'ai bu pas mal de bon rosé de Provence mais...
    Etant tombé sur ce post intéressant, je vais donner mon avis, subjectif bien sûr.

    1) il est vraiment dommage que strlcpy() et strlcat() ne soient pas partout.

    2) Je ne vais pas conseiller d'utiliser strlcpy() et strlcat() à un débutant car il risque de ne pas l'avoir dans son implémentation, d'une, et ce n'est pas de la bibliothèque standard (il est bon de se contenter de la Bib. C standard pour débuter).

    3) Vais-je conseiller à un débutant d'utiliser strncpy() et strncat() en plutôt que leurs pendants "non sécurisés" strcpy() et strcat() : NON. Je prend l'exemple de strcnpy() versus strcpy().

    strncpy()
    - 1 argument en plus, pas simple à maitriser :

    char * strncpy(char * dst, const char * src, size_t len);

    Copie AU PLUS len caractères de src dans dst. Si il y a moins de caractères que len dans dst, alors des ' \0' sont ajoutés dans dst jusqu'à len caractères, mais autrement, pas de '\0' ajouté. Pour un peu que dst ne soit pas initialisé comme il faut, pas bon...
    Cela nécessite donc d'ajouter par sécurité un '\0' à dst avec un dst[len] = '\0'.

    - Toujours à cause de ce troisième argument, si le tampon gérant dst est très grand (qui vaut len+1 souvent pour prévenir le fameux débordement de tampon) et qu'il n'y a que peu de caractères dans dst, ça fait beaucoup de '\0' à ajouter, inutilement. Pour bouffer en perf c'est bien.

    - Enfin, les guerres de "religion" limite pipi-caca entre les partisans de strcpy() vs ceux de strncpy() pour raisons de "sécurité", mon avis c'est que c'est de la connerie : la différence est FONCTIONNELLE essentiellement, la sécurité, un bon programmeur se démerdera que ce soit strcpy() ou strncpy(). strcpy() permet de copier une chaine source d'une position de début choisie jusqu'à la fin quand strncpy() permet de copier N caractères d'une chaine source à partir d'une position de début choisie (soit, de pas aller au bout comme avec strcpy() mais de faire une copie d'un vrai sous-ensemble de chaine). J'ai le même avis pour strcat() et strncat().

    Au final, je conseille au débutant d'utiliser strcpy() et strncpy() selon son besoin fonctionnel. Si il utilise strcpy() en ayant au préalable vérifié la taille de la chaine source (avec strlen() par exemple) par rapport à la taille du tampon de destination (ou plutôt au nombre de caractères effectifs que peut contenir ce tampon, soit taille -1), c'est bien, et ça aide à comprendre.

    Enfin, 2 autres raisons
    - Pour un débutant, faire crasher ses programmes et déboguer, c'est instructif, donc vouloir à tout prix bannir l'utilisation de certaines fonctions, surtout des courantes comme strcpy() et strcat(), je ne suis pas d'accord.
    - il y a d'autres fonctions de la bibliothèque standard sur lesquelles là on devrait mettre en rouge, ne pas utiliser : gets(), puis des fonctions nécessitant d'être avancé dans la langage avant de pouvoir les utiliser (scanf()), voir expert (strtok()), et y'en a d'autres surement...

    Voilà.

    A+

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

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par fearyourself
    En effet, mais il ne met pas forcément un '\0' à la fin s'il y a saturation... Il faudra donc penser à gérer ce cas...
    Il ne met jamais de 0 si il y a saturation. Il faut donc toujours prévoir ce cas.

    Je conseille strncat(). Bien plus performante et sûre quand elle est bien utilisée...
    Pas de Wi-Fi à la maison : CPL

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

Discussions similaires

  1. Exercice sur tableau de structures
    Par manou756011 dans le forum C
    Réponses: 2
    Dernier message: 06/05/2014, 00h45
  2. JNA pointeur sur tableau de structure
    Par NyTR0 x dans le forum Entrée/Sortie
    Réponses: 1
    Dernier message: 30/12/2009, 17h43
  3. Pointeur sur tableau de structure
    Par Mercenary Developer dans le forum Débuter
    Réponses: 3
    Dernier message: 22/09/2008, 08h35
  4. Réponses: 8
    Dernier message: 17/09/2008, 12h11
  5. [VB6]Tri multi-colonnes sur tableau de structure
    Par ELGUEVEL dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 17/02/2006, 08h02

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