c语言 - 对内存分配良好做法的怀疑



假设我有一个结构体,其声明与此类似

typedef struct {
char *name;
int age;
double *numbers; // A pointer to a list of doubles
} Person;

假设我有一个"初始化"函数Person_new创建一个新Person,它将char *nameint agedouble *numbers作为参数,为新Person分配内存并将其参数的值分配给新Person的相应元素。

Person *Person_new(char *name, int age, double *numbers) {
Person *self;
self = malloc(sizeof(Person));
if (self != NULL) {
self->name = name;
self->age = age;
self->numbers = numbers;
} 
return self;
}

使用此功能的正确方法是什么?它还应该为结构的元素分配内存吗?我对此的理解是,由于age不是指针,因此我不应该为此分配任何其他内存(因为我在mallocselfmalloc它)。因此,我应该为其他两个参数分配一些内存,因为在我的函数中,我只为它们的指针分配了内存。那么,这个malloc应该由使用该功能的人还是功能本身来制作?

通常,设计依赖于"软拷贝"的数据结构是一个坏主意。也就是说,您实际上并没有对传递的数据进行"硬拷贝",您只是指向它,实际分配是在其他地方完成的。由于许多原因,这是有问题的 - 例如,数据可能会变得无效。

在某些情况下,"软拷贝"可能有意义,例如创建一个指向只读数据的容器,如字符串文本。但通常,如果要创建具有读/写访问权限的容器,则应复制传递的所有值,以便容器"拥有"它使用的所有内存。

在您的情况下,您似乎正在做一些简单的数据库,在这种情况下,您绝对应该分配传递的数据的副本。通常,执行分配的代码部分也负责释放它,因此您还需要提供清理功能。

严格来说你说的是正确的(不需要分配两次age,需要分配namenumbers)。

但是,如果您愿意对这些字段施加长度限制,我建议您执行以下操作:

typedef struct {
char name[MAX_NAME_LENGTH];
int age;
double numbers[MAX_NUMBERS_LENGTH];
} Person;

在这种情况下,当您malloc此结构时,还会分配用于namenumbers的内存。然后,在Person_new函数中,您可以将name值和numbers值从收到的指针复制到结构的字段,例如使用memcpy。 这样,结构使用的所有内存都归结构所有,这可以防止潜在的问题(如果在结构仍在使用时释放numbersname的内存怎么办?这也意味着当您释放Person时,其字段的内存也会被释放,这也是 IMO 的良好行为。

它还应该为结构的元素分配内存吗?

对此没有"一刀切"的答案。

这取决于您的程序正在做什么,例如数据来自哪里以及如何将数据读入系统。

例如,数据可能来自由其他函数读取的文件,每当从文件中读取"人"时,该函数都会调用您的Person_new。在这种情况下,文件读取器函数很可能在读取名称和数字时已经使用了动态分配。如果是这样,在Person_new内分配内存并再次复制所有数据将毫无意义。相反,只需存储指向"已分配"内存的指针即可。

当你编写一个函数时,你总是需要描述函数在做什么,并描述调用函数的规则,即签订合约。可以写namenumbers必须是指向使用malloc获得的内存的指针,并且所有权作为调用函数的一部分转移。(无标准但广泛使用的strdup是转移已分配内存所有权的一个例子)。

但请注意,这限制了函数的使用方式。例如,您的函数不能与指向具有自动存储持续时间的数组(即局部变量)的指针一起使用,更糟糕的是:如果有人破坏了您的合约并确实传递了指向局部变量的指针,那么编译器将无法检测到此错误。

如果您的函数分配内存并复制数据,则无需对函数的调用方式设置此类限制。

另一方面,如果"必须是指向动态分配内存的指针"限制适合您的用例,则可以避免不必要的分配和数据副本。

所以 - 只是重复一遍 - 没有适合所有用例的单一解决方案 - 这取决于程序的其余部分。

最新更新