2010-07-22 9 views
12

Quelle est la méthode standard pour copier deux structures contenant des matrices de char?Copie de deux structures en C contenant des pointeurs char

Voici un code:

#include stdio.h> 
#include string.h> 
#include stdlib.h> 

typedef struct { 
    char* name; 
    char* surname; 
} person; 

int main(void){ 
    person p1; 
    person p2; 

    p1.name  = (char*)malloc(5); 
    p1.surname = (char*)malloc(5); 

    strcpy(p1.name, "AAAA"); 
    strcpy(p1.surname, "BBBB"); 

    memcpy(&p2, &p1, sizeof(person)); 
    free(p1.name); 
    printf("%s\n", p2.name); 
    return 0; 
} 

La ligne printf("%s\n", p2.name); n'imprime pas quelque chose, parce que je libérais le tampon.

Le problème avec mes structures est qu'elles sont plus grandes que struct person. Ils contiennent des centaines de pointeurs char, et je dois copier chaque membre un par un.

Existe-t-il un autre moyen de copier deux structures contenant des tableaux char sans utiliser malloc et strcpy pour chaque membre?

+0

Comment fonctionne memcpy, si la mémoire n'est pas allouée pour p2, quelqu'un peut-il expliquer? Ne devrait-il pas jeter une exception au moment de l'exécution? – JagsVG

+1

Vos structures contiennent * pointeurs *, pas * tableaux *. Vos pointeurs peuvent contenir l'adresse d'un tableau de char, mais si vous voulez que ce tableau soit copié, vous devez le gérer explicitement. –

Répondre

13

Vous avez pas d'autre choix que de fournir une fonction de copie vous:

void copy_person(person *dst, const person *src) 
{ 
    dst->name = malloc(strlen(src->name) + 1); 
    dst->surname = malloc(strlen(src->surname) + 1); 
    strcpy(dst->name, src->name); 
    strcpy(dst->surname, src->surname); 
} 

qui peut être plus élaborée que: vérification des erreurs, l'affacturage la strlen + strcpy dans une fonction auxilliaire, etc.

C'est ce que les constructeurs de copie en C++ sont pour.

+4

Si vous utilisez un système POSIX (par exemple Linux), vous pouvez simplement utiliser 'strdup' au lieu de' malloc + strcpy'. –

+1

@Jens: c'est ce que j'avais dans la tête quand j'écrivais "l'affacturage .... dans une fonction auxiliaire". –

7

Oui, struct copie qui contiennent ombles tableaux fonctionneront sans aucun problème, mais avec struct type char pointeurs (ou tout autre type de pointeur pour cette question), vous devrez le faire manuellement.

Notez également que la distribution du type de retour de malloc n'est pas nécessaire en C (c'est en C++) et peut masquer un prototype manquant pour malloc.

0

Vous devez allouer de la mémoire à n'importe quel pointeur si vous voulez en faire une copie. Cependant, vous pouvez toujours faire un pointeur vers la mémoire déjà allouée. Par exemple, vous pouvez effectuer les opérations suivantes:

p2.name = p1.name (p1.name is already allocated memory) 

C'est dangereux car il y a plus d'une référence au même emplacement mémoire. Si vous libérez soit p1.name ou p2.name, il en résulte une situation dangereuse.

Pour copier tout le contenu, vous devez allouer de la mémoire aux pointeurs de la structure p2.

p2.name = <allocate memory> 
Copy individual struct members instead of a memcpy of the entire struct 

Cela est dû au fait que la mémoire n'est pas allouée de manière contiguë. Aussi sizeof(struct) vous donnera la taille des membres de la structure et non la mémoire qui lui est allouée.

Par exemple sizeof(p2) = 8 = sizeof(p1)= sizeof(person) même après l'allocation de mémoire aux membres de p1.

Ce serait un cas différent si les membres étaient des tableaux char.

1

Un peu hors-the-box pensée:

Depuis la structure de votre structure est statique, vous pouvez écrire un petit utilitaire ou d'un script pour générer le code de copie pour vous.Prenez le code source de votre définition de structure en entrée, puis concevez un ensemble de règles pour générer le code de copie.

Ceci est quickshot, et je ne sais pas si c'était plus rapide d'écrire le code de la copie manuellement - mais au moins c'est un problème plus intéressant.

1

Pour élaborer sur la réponse d'Alexandre C. vous pouvez faire le malloc() en une seule opération de sorte qu'un free() est aussi simple.

Cette approche fournit un degré de protection en ce que le malloc() réussira ou échouera de sorte que vous n'ayez pas de problème de malloc() à mi-chemin de la construction d'une copie. Avec cette approche vous mélangeriez person avec des pointeurs vers person qui ont été malloced ainsi vous voudrez peut-être avoir deux types de données différents quelque chose dans le sens de ce qui suit afin de mieux marquer qui est qui.

J'ai fourni deux alternatives pour la copie avec C en utilisant les fonctions de bibliothèque standard strcpy() et strlen() et l'autre en utilisant une simple fonction qui fait une copie droite et renvoie un pointeur là où il s'est arrêté dans le tampon de destination.

Je n'ai pas essayé de compiler cet exemple, donc il peut y avoir des problèmes avec cela.

Il y a un problème possible avec cette approche. Puisque les chaînes individuelles ne sont pas mallocées, vous pouvez rencontrer un problème si vous déplacez les chaînes individuelles en utilisant leurs pointeurs avec l'idée que chacune des chaînes individuelles est sa propre zone de mémoire malléable. Cette approche suppose que l'objet entier est recherché ou qu'aucun d'entre eux n'est voulu.

#include <stdio.h> 
    #include <string.h> 
    #include <stdlib.h> 

    typedef struct { 
     char* name; 
     char* surname; 
     char* address1; 
    } person, *personptr; 

    // copy a string to destination string return pointer after end of destination string 
    char * StrCpyRetEnd (char *pDest, char *pSrc) 
    { 
     while (*pDest++ = *pSrc++); 
     return pDest; 
    } 
    personptr DeepCopyPerson (person *pSrc) 
    { 
     personptr  pDest = 0; 
     unsigned int iTotalSize = sizeof(person); 

     iTotalSize += (strlen(pSrc->name) + 1) * sizeof(char); 
     iTotalSize += (strlen(pSrc->surname) + 1) * sizeof(char); 
     iTotalSize += (strlen(pSrc->address1) + 1) * sizeof(char); 
     pDest = malloc(iTotalSize); 
     if (pDest) { 
#if 1 
      // alternative one without a helper function 
      pDest->name = (char *)(pDest + 1); strcpy (pDest->name, pSrc->name); 
      pDest->surname = pDest->name + strlen(pDest->name) + 1; strcpy (pDest->surname, pSrc->surname); 
      pDest->address1 = pDest->surname + strlen(pDest->surname) + 1; strcpy (pDest->address1, pSrc->address1); 
#else 
      // alternative two using StrCpyRetEnd() function 
      pDest->name = (char *)(pDest + 1); 
      pDest->surname = StrCpyRetEnd (pDest->name, pSrc->name); 
      pDest->address1 = StrCpyRetEnd (pDest->surname, pSrc->surname); 
      strcpy (pDest->address1, pSrc->address1); 
#endif 
     } 
     return pDest; 
    } 

    int main(void){ 
     person p1; // programmer managed person with separate mallocs 
     personptr p2; // created using ClonePerson() 

     p1.name  = malloc(5); 
     p1.surname = malloc(5); 
     p1.address1 = malloc(10); 

     strcpy(p1.name,"AAAA"); 
     strcpy(p1.surname,"BBBB"); 
     strcpy(p1.address1,"address1"); 

     p2 = DeepCopyPerson (&p1); 

     free(p1.name); 
     printf("%s\n", p2->name); 

     free (p2); // frees p2 and all of the memory used by p2 
     return 0; 
    } 
Questions connexes