制作结构的深层副本...制作结构的浅表副本

时间:2011-08-02 11:59:29

标签: c struct

有一些问题就像这个问题一样,但它们与我提出的具体问题并不相似。

我的问题是如何使用指针作为成员制作结构的深层副本,以及如何使用指针作为成员制作结构的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;

}

我的问题的另一部分(没有指针作为成员的结构)可能只是口头解释。

阅读有用的评论后被删除:

浅拷贝:         的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);
}

6 个答案:

答案 0 :(得分: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 :(得分:2)

浅拷贝和深拷贝之间的区别可以用一句话来解释:浅拷贝复制指针;深拷贝复制他们指向的内容。

从问题的最后一部分开始:如果没有指针,浅拷贝或深拷贝之间没有区别。

您尝试制作浅色副本在技术上是正确的。但这在逻辑上是错误的。你的delete_student()函数(释放mallocs的函数)不能处理浅拷贝。它不知道还有多少其他学生副本,并且你需要推迟free()直到删除alst副本。

深层复制存在一个非常相关的问题。这在技术上是不正确的。奇怪的是,您的create_student函数显示您确实知道如何将char *复制到另一个 - 它具有first_namelast_name的深层副本。您的copy_Student也应该这样做。

答案 2 :(得分:1)

轻微免责声明:我假设64位gcc编译器的sizeof()以及8字节对齐。我也意识到这几乎是一个已有7年历史的问题,但是在我的Google搜索中它以1号的形式弹出,所以我想为其他可能为之迷惑的人澄清一些事情。确实,我只是想发表评论,但要获得50声誉就必须发表评论。所以这里有另一个答案...

我不确定原始海报对指针的理解是什么,但是我个人知道我必须在内部停止将它们视为“指向”任何东西,而将其视为“某物的内存地址”。 / p>

您列出的用于浅表复制的代码具有微妙的(但可能是灾难性的)监督。

在您的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?

(在堆栈上)声明了局部(指针/内存地址)变量s1和s2:

  • 学生* s1(64位gcc上的8字节内存地址)
  • 学生* s2(64位gcc上的8字节内存地址)
成为指针的

s1和s2是学生结构的内存地址,由于您的create_Student()函数正在使用malloc()来在堆上分配内存(表示即使在create_Student()退出后,它也会停留在周围。

在s1或s2前面加上一个&号就像是说:“给我我学生结构的地址”

&s1和&s2现在表示s1和s2指针(或内存地址)的内存位置(在堆栈中)。换句话说,您现在有2个深度的指针:指向(位于堆栈中的)指针(指向(堆中的)学生结构)指针。

通过指定memcpy(&s2,&s1,sizeof(Student)),您已要求memcpy用堆栈指针s1的内容(或地址)覆盖堆栈指针s2并破坏了main()的另外24个字节紧跟在以&s2开头的8个字节和紧随&s1的24个字节之后的堆栈存储器。因此,引述Anomie:

  

如果尺寸合适,则与s2 = s1相同;

因此,使用“需要对指针指向的副本进行复制”的相同逻辑,您的copy_Student()DEEP副本可能类似于:

// I swapped the s1 and s2 arguments with
// target and source for clarity as well as their order
// to more closely mimic memcpy()
void copy_Student(Student *target, Student *source)
{
   if (target!=NULL) free_Student(target); // if target is already allocated, free it...
   assert(source != NULL);

   target->grade = source->grade;
   target->id = source->id;

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

   strncpy(target->first_name, source->first_name, strlen(source->first_name) + 1);
   strncpy(target->last_name, source->last_name, strlen(source->last_name) + 1); 
}

答案 3 :(得分: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的克隆。

要制作浅色副本,请像对待s1s2memcpy)一样,或者干脆执行:

s2 = malloc(sizeof(Student));
*s2 = *s1

答案 4 :(得分:0)

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

在这里,你已经用s2中的相应指针值覆盖了指针s2s1内的指针,所以你已经泄露了内存。

要执行深层复制,必须首先释放目标结构指向的所有内存。然后分配足够的内存来保存源结构指向的字符串。现在,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); 
}

答案 5 :(得分: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目标指针,否则会出现内存泄漏。但即使free指针,如果深度复制到以前浅层复制到的结构,也会导致问题。