2011-08-02 44 views
11

在我得到高度的低估之前,我會提到像我已經看到的這個問題,但它們都不夠相似對我而言,我至少有一個問題需要考慮(原諒我在這種情況下的無知),或者他們不一定是針對C語言的。C:製作一個結構體的深層副本...製作一個結構體的淺拷貝

我的問題是關於如何使用指針作爲成員來創建結構的深層副本,以及如何使用指針成員來創建結構的SHALLOW副本。然後,僅供參考,如何在沒有指針成員的情況下創建結構的深層副本,以及如何在沒有指針成員的情況下創建結構的淺拷貝(不確定最後一個結果是否有意義)。

比方說,我們有這樣的:

typedef struct Student 
{ 
    char* first_name; 
    char* last_name; 
    int grade; 
    long id; 
} Student; 

這裏是我做了創建一個學生(頭被暫時難以格式,請多多包涵)的通用功能:

Student* create_student(const char* first_name, const char* last_name, int grade,long id) 

{ 

    Student *newStudentp = (malloc(sizeof(Student))); 

    newStudentp -> last_name = (malloc((strlen(last_name) + 1) * sizeof(char))); 
    newStudentp -> first_name = (malloc((strlen(first_name) + 1) * sizeof(char))); 

    strncpy(newStudentp -> first_name, first_name, strlen(first_name) + 1); 
    strncpy(newStudentp -> last_name, last_name, strlen(last_name) + 1); 

    newStudentp -> grade = grade; 
    newStudentp -> id = id; 


    return newStudentp; 
} 

現在我會嘗試做一個深淺的副本;告訴我,如果我做了什麼愚蠢的

int main() 
{ 
    Student *s1 = create_Student("Bo","Diddly", 100, 221); 
    Student *s2 = create_Student("Leeroy","Jenkins",50,1337); 
    memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2? 
    return 0; 
} 

現在,與指針的成員,我知道我們必須使我們自己的複製功能,做一些明智的指針結構的深層副本。這是什麼明智的東西......我不確定......所以這是我在這份DEEP副本中的嘗試(可能是可憐的)。

void copy_Student(Student *s1, Student *s2) 
{ 
    s2 -> grade = s1 -> grade; 
    s2 -> id = s1 -> id; 
    s2 -> first_name = s1 -> *first_name; 
    s2 -> last_name = s1 -> *last_name; 

} 

我的問題的其他部分(結構沒有指針作爲成員)可能只是口頭解釋。

好的,EDITED之後讀有益的意見:

淺副本: 的memcpy(S2,S1,的sizeof(學生));

深副本:

void free_student(Student* stu) 
{ 
    free(stu -> first_name); 
    free(stu -> last_name); 
} 

void copy_Student(Student *s1, Student *s2) 
{ 
    s2 -> grade = s1 -> grade; 
    s2 -> id = s1 -> id; 
    s2 -> first_name = strdup(s1 -> first_name); 
    s2 -> last_name = strdup(s1 -> last_name); 
} 

謝謝你們(一定要指出,如果仍然有錯誤,雖然)!

非常感謝, 菲爾

+0

free_student應該在學生完成之後調用,而**不在copy_Student **之內。您首先釋放s2,然後將內容複製到它。不是一個好主意。 –

+0

正式注意。謝謝! – Phil

回答

5

您列出的製作淺拷貝的代碼不是;它實際上會粉碎堆棧並可能導致程序崩潰。

Student *s1 = create_Student("Bo","Diddly", 100, 221); 
Student *s2 = create_Student("Leeroy","Jenkins",50,1337); 
memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2? 

如果您的尺寸正確,那將與s2 = s1;相同。但是由於你的大小不對,它會複製太多,並會在s2之後覆蓋內存中的任何內容。要做到真正的淺拷貝,離開關&

memcpy(s2,s1,sizeof(Student)); //shallow copy of s1 INTO s2 

你有一個深拷貝代碼同樣是錯的,但你在正確的軌道上。深拷貝背後的基本思想是你必須複製每個字段;對於非指針類型,這與淺拷貝相同,但對於指針,你必須做更聰明的事情。但是,您發佈的代碼並未這麼做。試試這個。

void copy_Student(Student *s1, Student *s2) 
{ 
    s2 -> grade = s1 -> grade; 
    s2 -> id = s2 -> id; 
    s2 -> first_name = strdup(s1 -> first_name); 
    s2 -> last_name = strdup(s1 -> last_name); 
} 

請注意,以避免內存泄漏,您還需要指定新副本之前擺脫s2舊名稱,使free_Student功能,將釋放這些名字,也確保create_Student副本名稱(或者包括「應該釋放」標誌,這樣你就不必複製文字字符串)。

現在,對於沒有指針(或其他引用類型)的結構,深和淺拷貝之間沒有區別,因爲它本身淺的數據結構。

+1

strdup不是C標準 – user411313

+0

@ user411313:那麼,它在POSIX,SVr4和4.3BSD中。使用strlen,malloc和memcpy編寫替代品是很簡單的,如果由於某種原因您處於不提供它的環境中。 – Anomie

2

淺拷貝和深拷貝可以用一句話來解釋的區別:一個淺拷貝副本的指針;一個深層拷貝拷貝他們指向的內容。

從問題的最後一部分開始:如果沒有指針,淺表或深度副本之間沒有區別。

您嘗試製作淺拷貝在技術上是正確的。不過,這在邏輯上是錯誤的。你的delete_student()函數(釋放malloc的函數)不能處理淺拷貝。它不知道還有多少其他學生副本仍在,並且需要延遲free()直到刪除阿爾斯特副本。

深拷貝有一個非常相關的問題。這在技術上是不正確的。奇怪的是,您的create_student函數顯示您知道如何將char *複製到另一個字符,該字符具有first_namelast_name的深層副本。你的copy_Student也應該這樣做。

0

而不是將其視爲副本,爲什麼不創建一個新的結構,但與您想要複製的參數相同?這是一個微妙的差異,但是,你已經代碼:

Student *s2 = create_Student("Leeroy","Jenkins",50,1337); 
Student *wiper = create_Student(s2->first_name, s2->last_name, 
               s2->grade, s2->id); 

wiper結構有s2克隆。

要淺拷貝,做你與s1s2(該memcpy)做,或者乾脆:

s2 = malloc(sizeof(Student)); 
*s2 = *s1 
0
memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2? 

在這裏,你已經覆蓋指針s2和內s2指針通過s1中的相應指針值,所以你泄漏了內存。

要執行深度複製,您必須首先釋放目標結構指向的任何內存。然後分配足夠的內存來保存源結構指向的字符串。現在,strncpy的字符串結束。

void copy_Student(Student *s1, Student *s2) 
{ 
    assert((s1 != NULL) && (s2 != NULL)); 

    if(s2->first_name != NULL) free(s2->first_name); 
    if(s2->last_name != NULL) free(s2->last_name); 

    s2->grade = s1->grade; 
    s2->id = s1->id; 

    s2->last_name = (malloc((strlen(s1->last_name) + 1) * sizeof(char))); 
    s2->first_name = (malloc((strlen(s1->first_name) + 1) * sizeof(char))); 

    strncpy(s2-> first_name, s1->first_name, strlen(s1->first_name) + 1); 
    strncpy(s2-> last_name, s1->last_name, strlen(s1->last_name) + 1); 
} 
0

取而代之的是:

newStudentp -> last_name = (malloc((strlen(last_name) + 1) * sizeof(char))); 

做:

newStudentp -> last_name = strdup (last_name); 

你的深拷貝想要做類似的東西(不正是cnicutar建議):

s2->first_name = strdup (s1->first_name); 

cnicutar的問題建議它需要在strcpy之前手動分配緩衝區。

如果我沒記錯的話:

* s2 = * s1;

將做一個淺拷貝。

當然,在深度和淺度副本中,您都必須確保您的目標指針爲free,否則會導致內存泄漏。但是,即使您深度複製到先前淺拷貝到的結構,指針也可能導致問題。

+0

strdup不是C標準,malloc是 – user411313

相關問題