2010-07-22 29 views
12

Cách tiêu chuẩn để sao chép hai cấu trúc có chứa mảng char là gì?Sao chép hai cấu trúc trong C có chứa các con trỏ char

Dưới đây là một số mã:

#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; 
} 

Dòng printf("%s\n", p2.name); không in một cái gì đó, bởi vì tôi giải phóng bộ đệm.

Vấn đề với cấu trúc của tôi là chúng lớn hơn cấu trúc person. Chúng chứa hàng trăm con trỏ char, và tôi phải sao chép từng thành viên một.

Có cách nào khác để sao chép hai cấu trúc chứa mảng char mà không sử dụng mallocstrcpy cho mọi thành viên không?

+0

Memcpy hoạt động như thế nào, nếu bộ nhớ không được phân bổ cho p2, Ai có thể giải thích? Không nên ném một số ngoại lệ vào thời gian chạy? – JagsVG

+1

Cấu trúc của bạn chứa * con trỏ *, không * mảng *. Con trỏ của bạn có thể chứa địa chỉ của một mảng char, nhưng bạn muốn mảng đó được sao chép, bạn phải quản lý nó một cách rõ ràng. –

Trả lời

13

Bạn không có lựa chọn nào khác cung cấp một chức năng sao chép bản thân:

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); 
} 

có thể được nhiều hơn xây dựng hơn rằng: kiểm tra lỗi, bao thanh toán các strlen + strcpy trong một chức năng auxilliary vv

Đó là những gì sao chép các hàm tạo trong C++ là cho.

+4

Nếu bạn đang sử dụng hệ thống POSIX (ví dụ: linux), bạn có thể sử dụng 'strdup' thay vì' malloc + strcpy'. –

+1

@ Jens: đó là những gì tôi có trong đầu khi tôi viết "bao thanh toán .... trong một chức năng trợ giúp". –

7

Vâng, sao chép cấu trúc có chứa char mảng sẽ làm việc mà không cần bất kỳ vấn đề, nhưng struct với char con trỏ (hoặc bất kỳ loại con trỏ cho rằng vấn đề), bạn sẽ phải làm bằng tay.

Cũng lưu ý rằng loại bỏ kiểu trả về của malloc là không cần thiết trong C (nó là trong C++) và có thể ẩn một nguyên mẫu còn thiếu cho malloc.

0

Bạn phải cấp phát bộ nhớ cho bất kỳ con trỏ nào nếu bạn muốn sao chép. Tuy nhiên, bạn luôn có thể tạo một điểm con trỏ tới bộ nhớ đã được cấp phát. Ví dụ, bạn có thể làm như sau:

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

Đây là nguy hiểm như có nhiều hơn một tham chiếu đến vị trí nhớ cùng. Nếu bạn miễn phí p1.name hoặc p2.name, điều này dẫn đến tình huống nguy hiểm.

Để sao chép toàn bộ nội dung bạn phải cấp phát bộ nhớ cho con trỏ của cấu trúc p2.

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

Điều này là do bộ nhớ không được cấp phát tiếp theo. Ngoài ra sizeof(struct) sẽ cung cấp cho bạn kích thước của các thành viên của cấu trúc và không phải là bộ nhớ được phân bổ cho nó.

Ví dụ sizeof(p2) = 8 = sizeof(p1)= sizeof(person) ngay cả sau khi cấp phát bộ nhớ cho các thành viên của p1.

Nó sẽ là một trường hợp khác nhau có các thành viên được mảng char.

1

Một chút out-of-the-box suy nghĩ:

Kể từ khi cấu trúc của struct của bạn là tĩnh, bạn có thể viết một chương trình tiện ích nhỏ hoặc kịch bản để tạo mã bản sao cho bạn.

Lấy mã nguồn của định nghĩa cấu trúc của bạn làm đầu vào và sau đó đưa ra một bộ quy tắc để tạo mã sao chép.

Đây là ảnh chụp nhanh và tôi không biết liệu viết mã sao chép có nhanh hơn không - nhưng ít nhất đó là vấn đề thú vị hơn.

1

Để xây dựng câu trả lời của Alexandre C. bạn có thể muốn làm malloc() làm thao tác đơn lẻ để free() cũng đơn giản.

Cách tiếp cận này cung cấp mức độ bảo vệ trong đó malloc() sẽ thành công hoặc không thành công để bạn không gặp sự cố malloc() không thực hiện được việc xây dựng bản sao. Với cách tiếp cận này, bạn sẽ kết hợp person với các con trỏ đến person đã được gắn kết để bạn có thể muốn có hai loại dữ liệu khác nhau dọc theo các dòng sau để đánh dấu tốt hơn.

Tôi đã cung cấp hai lựa chọn thay thế cho việc sao chép bằng cách sử dụng chức năng thư viện chuẩn C strcpy()strlen() và cách khác bằng cách sử dụng hàm đơn giản sao chép thẳng và trả về con trỏ đến vị trí còn lại trong bộ đệm đích.

Tôi chưa cố biên dịch ví dụ này để có thể có vấn đề với nó.

Có một mối quan tâm có thể xảy ra với phương pháp này. Vì các chuỗi riêng lẻ không phải là malloced, bạn có thể gặp phải vấn đề nếu bạn đang di chuyển các chuỗi riêng lẻ xung quanh bằng cách sử dụng con trỏ của chúng với ý tưởng rằng mỗi chuỗi riêng lẻ là khu vực bộ nhớ riêng của nó. Cách tiếp cận này giả định toàn bộ đối tượng là mong muốn hoặc không ai trong số đó là muốn.

#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; 
    } 
Các vấn đề liên quan