Menu
CoddyTech

深拷贝

本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 15 课,共 61 课。

在 C 中将一个结构体赋值给另一个结构体时,会得到一个浅拷贝:所有字段都会按字节复制。对于整数这样的简单值,这样做没有问题,但指针会带来一个问题:两个结构体最终都会指向同一块内存。

typedef struct {
    char *name;
    int age;
} Person;

Person alice = {"Alice", 30};
Person copy = alice;  // 浅拷贝!
// alice.name 和 copy.name 都指向同一个字符串

如果你释放其中一个,另一个也会失效。如果你通过其中一个修改字符串,两者都会看到更改。这很少是你想要的。

深拷贝会为指针成员分配新的内存,并复制实际数据:

Person *clone_person(const Person *original) {
    Person *copy = malloc(sizeof(Person));
    copy->name = malloc(strlen(original->name) + 1);
    strcpy(copy->name, original->name);
    copy->age = original->age;
    return copy;
}

现在,每个对象都拥有自己的字符串。你可以修改或释放其中一个,而不会影响另一个。这个模式很直接:对于每个指针成员,都要分配新的内存并复制内容,而不只是复制地址。

challenge icon

挑战

简单

我们来构建一个 Student 模块,用来演示深拷贝模式。你将创建一个克隆函数,生成一个完全独立的学生对象副本:修改或释放原对象都不会影响克隆对象。

你将创建 3 个文件:

  • student.h:声明一个 Student 结构体,包含两个成员:char *name(动态分配的字符串)和 int grade。声明 3 个函数:
    • create_student:构造函数,接收姓名和成绩,并返回指向新 Student 的指针
    • clone_student:接收一个 const Student *,并返回指向全新独立副本的指针
    • free_student:析构函数,用于正确释放所有已分配的内存
    使用符号 STUDENT_H 添加头文件保护。
  • student.c:实现这 3 个函数。clone_student 函数是关键。它必须为结构体和姓名字符串分别分配新的内存,然后复制实际的字符串数据(而不只是指针地址)。这样可以确保克隆对象拥有自己的内存,且与原对象完全分离。
  • main.c:创建一个学生,克隆该学生,显示两个学生的信息,然后正确清理这两个对象。

你会收到两个输入:学生姓名(字符串)和成绩(整数)。

在主文件中,使用提供的值创建一个学生,然后克隆该学生。打印原对象和克隆对象的信息,以确认它们包含相同的数据。最后,释放这两个学生并打印确认信息。

按以下格式打印输出:

Original: {name}, Grade: {grade}
Clone: {name}, Grade: {grade}
Both freed

例如,输入 Alice 和 95 时,输出为:

Original: Alice, Grade: 95
Clone: Alice, Grade: 95
Both freed

深拷贝模式可确保每个对象都真正独立:克隆对象拥有自己分配的字符串,而不是与原对象共享指向同一块内存的指针。

亲手试试

#include <stdio.h>
#include <stdlib.h>
#include "student.h"

int main() {
    char name[100];
    int grade;
    
    // 读取输入
    scanf("%s", name);
    scanf("%d", &grade);
    
    // TODO: 用提供的姓名和成绩创建一个学生
    
    // TODO: 创建该学生的克隆
    
    // TODO: 输出原学生的信息
    // 格式:"Original: {name}, Grade: {grade}"
    
    // TODO: 输出克隆学生的信息
    // 格式:"Clone: {name}, Grade: {grade}"
    
    // TODO: 释放两个学生占用的内存
    
    // 输出确认信息
    printf("Both freed\n");
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C 在线编译器