C语言结构体嵌套时应使用值类型还是指针类型?
问题解答
1. 你的测试代码确实存在多处未定义行为
你观察到两种写法运行表现一致完全是巧合,测试代码的未定义行为包括:
- 栈地址逃逸导致野指针访问:
store_via_val函数中将局部变量j的地址赋值给swp->n,函数返回后j的生命周期结束,对应栈内存被回收,后续在main中访问swp->n->i属于访问已释放的非法内存。你当前测试能得到正确值只是因为该内存还没被其他操作覆盖,换编译参数或运行环境大概率会出现乱码、段错误等异常。 - malloc分配内存大小错误:你在
main中分配结构体内存时用了sizeof(swp),swp本身是指针类型,你分配的只是一个指针的大小(64位环境下为8字节),远小于结构体实际需要的内存大小,越界访问也属于未定义行为,正确写法应该是sizeof(*swp)或者sizeof(struct s_w_ptr)。
2. 结构体内嵌值类型成员和指针成员的核心区别
你觉得值类型写法有问题是误解,内嵌值类型是C语言非常常用的合法写法,和指针成员的本质差异如下:
- 内存布局差异:
- 内嵌值类型成员(比如示例中的
barney)的内存直接包含在父结构体的内存空间中,父结构体创建时就会一并分配子结构体的内存,不需要单独申请,生命周期和父结构体完全绑定,父结构体销毁时子结构体也同步销毁。 - 指针类型成员(比如示例中的
wilma)本身只占用一个指针的存储空间,指向的子结构体内存是独立的,需要你单独申请、手动管理生命周期,父结构体销毁时如果没有主动释放指向的内存,就会产生内存泄漏。
- 内嵌值类型成员(比如示例中的
- 访问开销差异:
- 内嵌值成员可以直接访问,不需要额外的解引用操作,访问效率更高。
- 指针成员需要多一次访存解引用,效率略低,还存在访问野指针的风险。
- 拷贝逻辑差异:
- 父结构体拷贝时,内嵌值成员会被完整深拷贝,两个父结构体的子成员完全独立,修改其中一个不会影响另一个。
- 父结构体拷贝时,指针成员只会拷贝地址,两个父结构体的指针指向同一块内存,修改其中一个指向的内容,另一个也会同步变化。
3. 两种写法的选型参考
优先选用内嵌值类型的场景:
- 子结构体的生命周期完全和父结构体绑定,不需要独立存在
- 子结构体体积小,拷贝开销可以忽略
- 不需要多个父结构体共享同一个子结构体实例,每个父结构体需要独立的子成员副本
- 希望减少内存管理成本,避免手动申请释放、野指针、内存泄漏等问题
优先选用指针类型的场景:
- 需要实现结构体自引用,比如链表、树节点等结构,必须用指针,否则会出现无限嵌套,无法计算结构体大小
- 子结构体体积大,内嵌会导致父结构体占用内存过高,拷贝开销太大,用指针只需要传递地址即可
- 同一个子结构体实例需要被多个父结构体共享,修改一处所有引用的地方都能同步生效
- 实现不透明类型,头文件中仅暴露结构体声明不暴露定义,对外仅提供指针访问,隐藏实现细节
内容的提问来源于stack exchange,提问作者Patrice Levesque
相关产品推荐
相关产品推荐

