制作结构的深层副本…制作结构的浅表副本
有这样的问题,但它们与我提出的具体问题不太相似。
我的问题是如何使用指针作为成员来创建结构的深层副本,以及如何使用指针作为成员来创建结构的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); }
您列出的作为浅层副本的代码不是; 它实际上会粉碎堆栈并可能导致程序崩溃。
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 = 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
复制名称(或者包括“应该释放”标志,这样你就不必复制文字字符串了)。
现在,对于没有指针(或其他引用类型)的结构,深拷贝和浅拷贝之间没有区别,因为数据结构本身很浅。
浅拷贝和深拷贝之间的区别可以用一句话来解释:浅拷贝复制指针; 深拷贝复制他们指向的内容。
从问题的最后部分开始:如果没有指针,浅拷贝或深拷贝之间没有区别。
您尝试制作浅色副本在技术上是正确的。 但这在逻辑上是错误的。 你的delete_student()
函数(释放mallocs的函数)不能处理浅拷贝。 它不知道还有多少其他学生副本,并且你需要延迟free()
直到删除alst副本。
深拷贝有一个非常相关的问题。 这在技术上是不正确的。 奇怪的是,你的create_student
函数显示你知道如何将char *复制到另一个 – 它具有first_name
和last_name
的深层副本。 你的copy_Student
应该这样做。
轻微免责声明:我假设一个64位gcc编译器,就sizeof()和8字节对齐而言。 我也意识到这几乎是一个7岁的问题,但它在我的谷歌搜索中出现了1号,所以我想为其他可能偶然发现的事情澄清一些事情。 我真的只想发表评论,但SO需要50个声望。 所以这里有另一个答案……
我不确定原始海报对指针的理解是什么,但我个人知道我必须在内部停止将它们视为“指向”任何东西,并将它们视为“记忆地址”的东西。
您列出的作为浅层副本的代码具有微妙(但可能是灾难性的)疏忽。
在你的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(在堆栈上):
- Student * s1(64位gcc上的8字节内存地址)
- Student * s2(64位gcc上的8字节内存地址)
s1和s2是指针是学生结构的内存地址,它们碰巧在堆内存中分配,因为你的create_Student()函数正在使用malloc()在堆上分配内存( 堆意味着即使在create_Student之后它仍会存在) ()退出)。
在s1或s2前放一个&符号就像是说:“给我学生结构地址的地址”
&s1和&s2现在表示s1和s2指针(或内存地址)的内存位置(在堆栈中)。 换句话说,你现在是2级深度指针:指向(堆栈定位)Student结构的(堆栈定位)指针。
通过指定memcpy(&s2,&s1,sizeof(Student)),您已经要求memcpy用堆栈指针s1的内容(或地址)覆盖堆栈指针s2,以及损坏24个更多字节的main()的堆栈内存紧跟在&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); }
而不是将其视为副本,为什么不创建一个新的结构但使用与要复制的结构相同的参数? 这是一个微妙的区别,但你已经有了代码:
Student *s2 = create_Student("Leeroy","Jenkins",50,1337); Student *wiper = create_Student(s2->first_name, s2->last_name, s2->grade, s2->id);
wiper
结构有一个s2
的克隆。
要制作浅表副本,请执行与s1
和s2
( memcpy
)相同的操作,或者只是:
s2 = malloc(sizeof(Student)); *s2 = *s1
memcpy(&s2,&s1,sizeof(Student)); //shallow copy of s1 INTO s2?
在这里你已经用s1
相应的指针值覆盖了指针s2
和s2
的指针,所以你已经泄露了内存。
要执行深层复制,必须首先释放目标结构指向的任何内存。 然后分配足够的内存来保存源结构指向的字符串。 现在, 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); }
而不是这个:
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
指针也会导致问题。