C语言 制作结构的深拷贝...制作结构的浅拷贝

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6911688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 09:17:55  来源:igfitidea点击:

Making a deep copy of a struct...making a shallow copy of a struct

cstruct

提问by Phil

There are questions LIKE this one, but they are not similar enough to my specific question FOR ME to pick up on.

有像这样的问题,但它们与我的具体问题不够相似,我无法回答。

My question is about how to make a deep copy of a struct with pointers as members and how to make a SHALLOW copy of a struct with pointers as members. And then, just for reference, how to make a a deep copy of a struct WITHOUT pointer members and how to make a shallow copy of a struct WITHOUT pointer members (not sure if that last one makes sense).

我的问题是关于如何制作具有指针作为成员的结构的深层副本以及如何制作具有指针作为成员的结构的浅拷贝。然后,仅供参考,如何制作没有指针成员的结构的深拷贝以及如何制作没有指针成员的结构的浅拷贝(不确定最后一个是否有意义)。

Let's say we have this:

假设我们有这个:

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

Here is a generic function I made to create a student (the header is being difficult to format):

这是我用来创建学生的通用函数(标题很难格式化):

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

My attempt to make a deep and a shallow copy;

我试图制作一个深浅的副本;

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

For deep copies of structs with pointer members I know we must make OUR OWN copy function that does something sensible with pointers. What that sensible thing is...I'm not sure...so here is my attempt at this DEEP copy.

对于具有指针成员的结构的深层副本,我知道我们必须制作我们自己的复制函数,该函数可以对指针执行一些合理的操作。什么是明智的事情......我不确定......所以这是我对这个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;

}

The other part of my question (structs WITHOUT pointers as members) can probably just be explained verbally.

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

EDITED AFTER READING HELPFUL COMMENTS:

阅读有用的评论后编辑:

Shallow copy: memcpy(s2,s1,sizeof(Student));

浅拷贝:memcpy(s2,s1,sizeof(Student));

Deep copy:

深拷贝:

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

采纳答案by Anomie

The code you have listed as making a shallow copy isn't; it will actually smash the stack and probably crash the program.

您列为制作浅拷贝的代码不是;它实际上会粉碎堆栈并可能使程序崩溃。

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?

If you had the size right, that would be the same as s2 = s1;. But since you have the size wrong, it is copying too much and will overwrite whatever is in memory after s2. To do a real shallow copy, leave off the &:

如果您的尺寸合适,那将与s2 = s1;. 但是由于您的大小错误,它复制了太多并且会在 s2 之后覆盖内存中的任何内容。要进行真正的浅拷贝,请省略&

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

The code you have for a deep copy is similarly wrong, but you're on the right track. The basic idea behind a deep copy is that you have to copy each field; for non-pointer types this is the same as a shallow copy, but for pointers you have to do something smarter. The code you posted, however, isn't doing that. Try this instead.

您拥有的深层复制代码同样是错误的,但您走在正确的轨道上。深层复制背后的基本思想是您必须复制每个字段;对于非指针类型,这与浅拷贝相同,但对于指针,您必须做一些更聪明的事情。但是,您发布的代码并没有这样做。试试这个。

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

Note that to avoid memory leaks, you would also need to free the old names from s2before assigning the new copies, make a free_Studentfunction that would free these names, and also make sure that create_Studentcopies the names in the first place (or else include "should free" flags so you don't have to copy literal strings).

请注意,为了避免内存泄漏,您还需要s2在分配新副本之前释放旧名称,创建一个free_Student可以释放这些名称的函数,并确保create_Student首先复制名称(或者包括“应该free”标志,因此您不必复制文字字符串)。

Now, for a struct without pointers (or other reference types), there is no difference between a deep and a shallow copy because the data structure it itself shallow.

现在,对于没有指针(或其他引用类型)的结构,深拷贝和浅拷贝之间没有区别,因为数据结构本身是浅的。

回答by lwg-ga

SLIGHT DISCLAIMER: I'm assuming a 64-bit gcc compiler as far as sizeof() as well as an 8-byte alignment. I also realize this is almost a 7 year old question, but it popped up in my google search as number 1 so I wanted to clarify a few things for others that might stumble upon it. Really I just wanted to comment, but SO requires 50 reputation to do so. So here goes another answer...

轻微免责声明:就 sizeof() 以及 8 字节对齐而言,我假设是 64 位 gcc 编译器。我也意识到这几乎是一个 7 年前的问题,但它在我的谷歌搜索中作为第一名出现,所以我想为可能偶然发现它的其他人澄清一些事情。真的,我只是想发表评论,但 SO 需要 50 声望才能这样做。所以这里有另一个答案......

I'm not sure what the original poster's understanding of a pointer is, but I know personally I had to internally stop thinking of them as "pointing" to anything and think of them as a "memory address of" something.

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

The code you have listed as making a shallow copy has a subtle (but potentially catastrophic) oversight.

您列出的浅拷贝代码有一个微妙的(但可能是灾难性的)疏忽。

In your main() function:

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

The local (pointer/memory address) variables s1 and s2 are declared (on the stack):

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

  • Student *s1 (an 8-byte memory address on 64-bit gcc)
  • Student *s2 (an 8-byte memory address on 64-bit gcc)
  • Student *s1(64 位 gcc 上的 8 字节内存地址)
  • Student *s2(64 位 gcc 上的 8 字节内存地址)

s1 and s2 being pointers are memory addresses of student structs which happen to be allocated in heap memory due to the fact that your create_Student() function is using malloc() which allocates memory on the heap (heapmeaning it will stick around even after create_Student() exits).

s1 和 s2 是指针是学生结构的内存地址,由于您的 create_Student() 函数使用 malloc() 在堆上分配内存(意味着它会在 create_Student 之后仍然存在) () 退出)。

Putting an ampersand in front of s1 or s2 is like saying: "Give me the address of the address of my Student struct"

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

&s1 and &s2 now represent the memory locations (in the stack) of your s1 and s2 pointers (or memory addresses). In other words you are now 2 levels of pointer deep: a pointer to a (stack located) pointer to a (heap located) Student struct.

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

By specifying memcpy(&s2,&s1,sizeof(Student)) you have asked memcpy to overwrite the stack pointer s2 with the contents (or address) of stack pointer s1 as well as corrupt 24 more bytes of main()'s stack memory that immediately follows the 8 bytes starting at &s2 with the 24 bytes that immediately follows &s1. So to quote Anomie:

通过指定 memcpy(&s2,&s1,sizeof(Student)) 你已经要求 memcpy 用堆栈指针 s1 的内容(或地址)覆盖堆栈指针 s2 以及破坏 main() 堆栈内存的另外 24 个字节紧跟在从 &s2 开始的 8 个字节之后,紧跟在 &s1 之后的 24 个字节。所以引用Anomie:

If you had the size right, that would be the same as s2 = s1;

如果你的大小合适,那将与 s2 = s1; 相同。

So using the same logic of "needing to make a copy of what pointers point to" your copy_Student() DEEP copy might look like:

因此,使用“需要复制指针指向的内容”的相同逻辑,您的 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); 
}

回答by MSalters

The difference between a shallow copy and a deep copy can be explained in one sentence: A shallow copy copies pointers; a deep copy copies what they point to.

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

To start with the last part of your question: if there are no pointers, there's no difference between a shallow or a deep copy.

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

Your attempt to make a shallow copy is technically correct. It's logically wrong, though. Your delete_student()function (the one that frees the mallocs) can't deal with shallow copies. It wouldn't know how many other student copies are still around, and ou'd need to delay the free()until the deletion of the alst copy.

您尝试制作浅表副本在技术上是正确的。不过,这在逻辑上是错误的。您的delete_student()函数(释放 malloc 的函数)无法处理浅拷贝。它不知道还有多少其他学生副本存在,您需要延迟free()直到删除最后一个副本。

The deep copy has a very related problem. it's technically incorrect. Strangely enough, your create_studentfunction shows that you do know how to copy a char* to another one - that has a deep copy of both first_nameand last_name. Your copy_Studentshould do the same.

深拷贝有一个非常相关的问题。这在技术上是不正确的。奇怪的是,您的create_student函数表明您确实知道如何将 char* 复制到另一个具有深拷贝first_namelast_name. 你也copy_Student应该这样做。

回答by Praetorian

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

Here you've overwritten the pointer s2and the pointers within s2by the corresponding pointer values in s1, so you've leaked memory.

在这里,您已经用 中的相应指针值覆盖了指针s2和其中的指针,因此您泄漏了内存。s2s1

To perform a deep copy you must first free any memory that was being pointed to by the destination structure. Then allocate enough memory to hold the strings pointed to by the source structure. Now, strncpythe strings over.

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

回答by Skizz

Instead of this:

取而代之的是:

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

do:

做:

newStudentp -> last_name = strdup (last_name);

Your deep copy wants to do something similar (not exactly what cnicutar suggested):

您的深层副本想要做类似的事情(不完全是 cnicutar 建议的):

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

The problem with cnicutar's suggestion is that it needs to manually allocate the buffer before the strcpy.

cnicutar建议的问题是需要在strcpy之前手动分配buffer。

And if I remember correctly:

如果我没记错的话:

*s2 = *s1;

*s2 = *s1;

will do a shallow copy.

会做一个浅拷贝。

Of course, in both the deep and shallow copies you must make sure that you freethe destination pointers, otherwise you'll get a memory leak. But even freeing the pointers can lead to problems if you deep copy to a structure that was previously shallow copied to.

当然,无论是深拷贝还是浅拷贝,你都必须确保你free的目标指针,否则你会得到内存泄漏。但是free,如果您深复制到以前浅复制到的结构,即使是指针也会导致问题。

回答by Vinicius Kamakura

Instead of thinking about it as a copy, why don't you create a new struct but with the same parameters as the one you want to duplicate? It is a subtle difference but, you have the code already:

与其将其视为副本,不如创建一个新结构,但其参数与要复制的结构相同?这是一个细微的区别,但是,您已经有了代码:

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

the wiperstruct has a clone of s2.

wiper结构具有s2.

To make a shallow copy, do as you are doing with s1and s2(the memcpy), or simply:

要制作浅表副本,请按照您的方式使用s1and s2(the memcpy),或者简单地执行以下操作:

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