CS50 Inheritance作业中为何使用自定义结构体指针?
create_family(int generations)返回person*而非person变量? 这背后主要有几个关键原因,你的猜测(内存管理)是其中之一,还有更核心的逻辑层面的因素:
递归结构的本质需求
你的person结构体里包含了struct person *parents[2],这意味着每个person节点需要指向另外两个person节点,形成一棵家族树。如果返回的是person变量,那它只能表示单个节点,根本没法承载整个多代家族的层级关系——你没法把整个树的所有节点都塞进一个单个的person变量里,必须通过指针串联起堆上的多个动态分配的节点。栈内存的局限性
如果在create_family里创建一个person局部变量,这个变量是存储在函数的栈帧里的。当函数执行完毕返回时,栈帧会被销毁,这个局部变量的内存会被回收。此时如果返回这个变量的拷贝(值返回),虽然能拿到单个节点的数据,但它内部的parents指针指向的栈上内存已经失效了,会导致悬空指针,后续操作必然引发未定义行为。而用堆内存(通过malloc分配)创建节点,返回指针的话,堆上的内存不会随函数结束而销毁,能安全地被调用者访问。动态内存的灵活管理
家族的规模是由generations参数决定的,编译时没法预知需要创建多少个person实例。用指针返回堆上分配的节点,能按需创建任意数量的节点,之后还能通过free_family函数遍历整个树,逐个释放内存,避免内存泄漏——这正是你猜测的点,完全正确。避免不必要的拷贝开销
person结构体本身虽然不算特别大,但如果是返回整个结构体(值返回),每次返回都会做一次结构体拷贝。当需要构建多代家族树时,这种拷贝的开销会被放大。而返回指针只需要拷贝一个地址(通常是4或8字节),效率高得多。
举个简单的反例,如果强行返回person变量:
person create_family(int generations) { person p; // 尝试初始化p的parents... // 但parents需要指向其他person,这里只能创建局部变量,函数结束后就失效了 return p; }
这样返回的p里的parents指针全是无效的,完全没法用。而返回指针的话,你可以在函数里为每个节点分配堆内存,串联成树后返回根节点的指针,整个结构能被安全使用和后续释放。
内容的提问来源于stack exchange,提问作者Scarlett

