继承

要解决的问题

一个人的血型由两个等位基因(即基因的不同形式)决定。三种可能的等位基因是 A、B 和 O,每个人拥有其中两个(可能相同,也可能不同)。孩子的父母各自随机地将自己的两个血型等位基因之一传给孩子。可能的血型组合有:OO、OA、OB、AO、AA、AB、BO、BA 和 BB。

例如,如果一位父母的血型是 AO,另一位父母的血型是 BB,那么孩子的可能血型将是 AB 和 OB,具体取决于从每位父母那里接收到的等位基因。同样,如果一位父母的血型是 AO,另一位是 OB,那么孩子的可能血型将是 AO、OB、AB 和 OO。

在一个名为 inheritance 的文件夹中,有一个名为 inheritance.c 的文件,请模拟一个家庭每位成员血型的继承。

演示

分发代码

对于这个问题,你将扩展由 CS50 工作人员提供的代码的功能。

下载分发代码

登录 cs50.dev, 点击你的终端窗口,然后执行 cd 本身。你应该会发现你的终端窗口的提示符类似于下面所示:

$

接下来执行

wget https://cdn.cs50.net/2026/x/psets/5/inheritance.zip

从而将一个名为 inheritance.zip 的 ZIP 文件下载到你的 codespace 中。

然后执行

unzip inheritance.zip

来创建一个名为 inheritance 的文件夹。你不再需要这个 ZIP 文件了,所以可以执行

rm inheritance.zip

and 在提示符下回复 “y” 然后按 Enter 键来删除你下载的 ZIP 文件。

现在输入

cd inheritance

然后按 Enter 键进入(即打开)该目录。你的提示符现在应该类似于下面这样。

inheritance/ $

单独执行 ls,你应该会看到一个名为 inheritance.c 的文件。

如果你遇到任何问题,请再次按照这些相同的步骤操作,看看能否确定哪里出错了!

实现细节

完成 inheritance.c 的实现,使其创建一个指定世代大小的家庭,并为每位家庭成员分配血型等位基因。最老一代的等位基因将随机分配。

  • create_family 函数接受一个整数 (generations) 作为输入,并应为该世代数量的家庭的每位成员(通过 malloc)分配一个 person,返回指向最年轻一代的 person 的指针。
    • 例如,create_family(3) 应返回一个指向拥有两位父母的人的指针,其中每位父母也拥有两位父母。
    • 每个 person 应被分配 alleles。最老一代的等位基因应随机选择(通过调用 random_allele 函数),年轻世代应从每位父母那里继承一个等位基因(随机选择)。
    • 每个 person 应被分配 parents。最老一代的两个 parents 应设置为 NULL,年轻世代的 parents 应为一个包含两个指针的数组,每个指针指向一位不同的家长。

提示

理解 inheritance.c 中的代码

查看 inheritance.c 中的分发代码。

请注意名为 person 的类型定义。每个人都包含一个包含两个 parents 的数组,每个 parent 是指向另一个 person 结构体的指针。每个人还包含一个包含两个 alleles 的数组,每个 allele 是一个 char(可能是 'A''B''O')。

// Each person has two parents and two alleles
typedef struct person
{
    struct person *parents[2];
    char alleles[2];
}
person;

现在,看一下 main 函数。该函数首先为随机数生成器”播种”(即提供一些初始输入),我们稍后将用它来生成随机等位基因。

// Seed random number generator
srandom(time(0));

main 函数然后调用 create_family 函数来模拟为 3 代家庭(即一个人、他们的父母和他们的祖父母)创建 person 结构体。

// Create a new family with three generations
person *p = create_family(GENERATIONS);

然后我们调用 print_family 来打印出每位家庭成员及其血型。

// Print family tree of blood types
print_family(p, 0);

最后,该函数调用 free_familyfree 之前通过 malloc 分配的任何内存。

// Free memory
free_family(p);

create_familyfree_family 函数留给您来实现!

完成 create_family 函数

create_family 函数应返回一个指向 person 的指针,该人从给定输入中的 generations 代数量继承了其血型。

  • 首先请注意,这个问题为递归提供了很好的机会。
    • 要确定当前人的血型,你首先需要确定其父母的血型。
    • 要确定那些父母的血型,你必须首先确定他们父母的血型。以此类推,直到你到达你想要模拟的最后一代。

要解决这个问题,你会在分发代码中找到几个 TODO。

首先,你应该为一个新人分配内存。回想一下,你可以使用 malloc 来分配内存,并使用 sizeof(person) 来获取要分配的字节数。

// Allocate memory for new person
person *new_person = malloc(sizeof(person));

接下来,你应该检查是否还有需要创建的世代:即 generations > 1 是否成立。

如果 generations > 1,则还有更多世代需要被分配。我们已经通过递归调用 create_family 创建了两个新的父母 parent0parent1。你的 create_family 函数然后应该设置你创建的新人的父指针。最后,通过从每位父母那里随机选择一个等位基因,为该新人分配两个 alleles

  • 请记住,要通过指针访问变量,可以使用箭头表示法。例如,如果 p 是一个指向 person 的指针,那么可以通过 p->parents[0] 访问此人第一个父母的指针。
  • 你可能会发现 random() 函数对于随机分配等位基因很有用。此函数返回一个介于 0RAND_MAX(即 32767)之间的整数。特别是,要生成一个为 01 的伪随机数,可以使用表达式 random() % 2
// Create two new parents for current person by recursively calling create_family
person *parent0 = create_family(generations - 1);
person *parent1 = create_family(generations - 1);

// Set parent pointers for current person
new_person->parents[0] = parent0;
new_person->parents[1] = parent1;

// Randomly assign current person's alleles based on the alleles of their parents
new_person->alleles[0] = parent0->alleles[random() % 2];
new_person->alleles[1] = parent1->alleles[random() % 2];

假设没有更多世代需要模拟。即 generations == 1。如果是这样,此人将不会有父母数据。新人的两个父母都应设置为 NULL,并且每个 allele 都应随机生成。

// Set parent pointers to NULL
new_person->parents[0] = NULL;
new_person->parents[1] = NULL;

// Randomly assign alleles
new_person->alleles[0] = random_allele();
new_person->alleles[1] = random_allele();

最后,你的函数应返回一个指向已分配的 person 的指针。

// Return newly created person
return new_person;
完成 free_family 函数

free_family 函数应接受一个指向 person 的指针作为输入,释放该人的内存,然后递归释放其所有祖先的内存。

  • 由于这是一个递归函数,你应首先处理基本情况。如果函数的输入是 NULL,则没有需要释放的内容,因此你的函数可以立即返回。
  • 否则,你应在 free 孩子之前先递归 free 该人的两个父母。

下面是一个相当大的提示,但这就是实现的方法!

// Free `p` and all ancestors of `p`.
void free_family(person *p)
{
    // Handle base case
    if (p == NULL)
    {
        return;
    }

    // Free parents recursively
    free_family(p->parents[0]);
    free_family(p->parents[1]);

    // Free child
    free(p);
}

讲解视频

不确定如何解决?

如何测试

在运行 ./inheritance 后,你的程序应遵循背景部分描述的规则。孩子应有两个等位基因,分别来自每位父母。每位父母应各自有两个等位基因,分别来自各自的父母。

例如,在下面的例子中,第 0 代的孩子从第 1 代的两位父母那里各获得了一个 O 等位基因。第一位父母从第一位祖父母那里获得了一个 A,从第二位祖父母那里获得了一个 O。同样,第二位父母从他们的祖父母那里获得了一个 O 和一个 B。

$ ./inheritance
Child (Generation 0): blood type OO
    Parent (Generation 1): blood type AO
        Grandparent (Generation 2): blood type OA
        Grandparent (Generation 2): blood type BO
    Parent (Generation 1): blood type OB
        Grandparent (Generation 2): blood type AO
        Grandparent (Generation 2): blood type BO

正确性

check50 cs50/problems/2026/x/inheritance

代码风格

style50 inheritance.c

如何提交

在终端中执行以下命令提交你的作业,并按提示完成操作。

submit50 cs50/problems/2026/x/inheritance