我正在编写一些多态 C 代码,我想知道下面介绍的两个内存释放函数是否都能正常工作。我很确定
void free_casted_parent(Parent* parent)
是正确的,但我开始想知道内部的专业化是否有必要?
typedef enum {
CHILD_TYPE_1,
CHILD_TYPE_2,
} Kind;
typedef struct {
Kind kind;
} Parent;
typedef struct {
Kind kind;
int value;
} ChildType1;
typedef struct {
Kind kind;
float other_value;
} ChildType2;
void free_parent(Parent* parent) {
free(parent);
}
void free_casted_parent(Parent* parent) {
switch (parent->kind) {
case CHILD_TYPE_1:
free((ChildType1*) parent);
break;
case CHILD_TYPE_2:
free((ChildType2*) parent);
break;
}
}
int main() {
ChildType1* ptr1 = malloc(sizeof(ChildType1));
ChildType2* ptr2 = malloc(sizeof(ChildType2));
// do some stuff with both variants of parent
free_parent((Parent*) ptr1);
free_casted_parent((Parent*) ptr2);
return 0;
}
如果
free_casted_parent(Parent* parent)
不正确,这与释放堆分配的值数组有什么不同?在这两种情况下,我们都传递有效地址而不指定确切的大小
是的,它可以正常工作,但过于复杂,相反:
free(ptr1);
free(ptr2);
任何对象指针都可以与
void *
进行转换,这就是 free()
的操作。
您缺少
#include <stdlib.h>
。
malloc()
返回 NULL(出错时),这将导致 parent->
出现段错误。您想检查一下。
所示代码中的所有指针转换均由 C 2018 6.3.2.3 7 定义:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。否则,当再次转换回来时,结果将与原始指针相等......
由于指针是用
malloc
分配的,它为任何基本类型提供了适合寻址的内存,因此上面的第二句话不适用,并且定义了行为。
唯一显示的引发未定义行为问题的代码是
switch (parent->kind)
。 (代码中可能存在// do some stuff with both variants of parent
中未显示的问题。)这里使用类型Parent
访问内存,但我们不知道内存的有效类型是什么,因为动态分配内存的有效类型取决于用于将值存储到内存的类型,并且未显示该代码。推测使用了 ChildType1
或 ChildType2
,在这种情况下,此访问可能违反 C 2018 6.5 7 中的别名规则,因此具有未定义的行为。