继承
要解决的问题
一个人的血型由两个等位基因(即基因的不同形式)决定。三种可能的等位基因是 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_family 来 free 之前通过 malloc 分配的任何内存。
// Free memory
free_family(p);
create_family 和 free_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 创建了两个新的父母 parent0 和 parent1。你的 create_family 函数然后应该设置你创建的新人的父指针。最后,通过从每位父母那里随机选择一个等位基因,为该新人分配两个 alleles。
- 请记住,要通过指针访问变量,可以使用箭头表示法。例如,如果
p是一个指向 person 的指针,那么可以通过p->parents[0]访问此人第一个父母的指针。 - 你可能会发现
random()函数对于随机分配等位基因很有用。此函数返回一个介于0和RAND_MAX(即32767)之间的整数。特别是,要生成一个为0或1的伪随机数,可以使用表达式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