应该什么时候C函数返回新分配的内存?

Wil*_*hin 14 c memory-management coding-style

在其他地方的回复中,我找到了以下代码段:

一般来说,让C调用者分配内存而不是被调用者更好 - 因此在我看来strcpy是一个"更好"的函数,而不是strdup.

我可以看到这是一个有效的模式,但为什么它可以被认为更好?遵循这种模式是否有优势?或不?

最近我写了大量的代码,看起来像:

struct foo *a = foo_create();
// do something with a
foo_destroy(a);
Run Code Online (Sandbox Code Playgroud)

如果foo它不是一个扁平结构,那么我想我可以将所有初始化放在一个步骤中.另外,假设结构应该在堆上.为什么这样做会更好:

struct foo *a = malloc(sizeof(foo));
foo_init(a);
// do something with a
foo_destroy(a)
Run Code Online (Sandbox Code Playgroud)

Ale*_*ski 12

每当你想要一个不透明的结构,并且不想在头文件中公开它的内部.你的foo_create()例子说明了这一点

另一个例子是Windows API.例如,CreateWindow给你一个HWND.您不知道实际WND结构是什么样的,并且无法触及其字段.

与内核对象句柄相同.例如CreateEvent给出一个HANDLE.您只能使用定义良好的API对其进行操作,然后将其关闭CloseHandle().

回覆:

struct foo *a = malloc(sizeof(foo));
Run Code Online (Sandbox Code Playgroud)

这要求您struct foo在标题中定义,从而暴露其内部.如果要在轨道上进行更改,则可能会破坏(错误地)直接依赖其成员的现有代码.


Nor*_*sey 8

让调用者分配内存的主要优点是它简化了界面,并且调用者拥有内存是完全明确的.正如您的create/destroy示例所示,简化并不是很好.

我更喜欢Dave Hanson在C接口和实现中建立的创建/销毁约定 :

struct foo *foo_new(...);   // returns result of malloc()
void foo_free(struct foo **foop); // free *foop's resources and set *foop = NULL
Run Code Online (Sandbox Code Playgroud)

你遵循这样的惯例:

struct foo *a = foo_new();
...
foo_free(&a);
// now `a` is guaranteed to be NULL
Run Code Online (Sandbox Code Playgroud)

这个约定使你不太可能留下一个悬垂的指针.


out*_*tis 5

您发布的任何一种方法都是很好的形式;前者更接近 C++ 处理事物的方式,后者更像 Objective-C。重要的是在代码块内平衡创建和销毁。这种做法属于减少耦合的范畴。不好的做法是让函数创建一些东西执行额外的任务,就像strdup这样做一样,这使得在不查阅文档的情况下更难知道调用者是否必须处理任何东西。

  • strdup() 只做一项工作——在分配的内存中创建一个字符串的副本。如果它还向标准错误写了一条消息,或者给作者打电话回家,那就太糟糕了。 (2认同)

nos*_*nos 5

这两种方法都很好。考虑所有 FILE* 操作函数,它们不允许您自己分配 FILE。

如果您使用 C 进行编程,您通常希望完全控制一切。这意味着让调用者控制分配结构的位置和方式是一件好事。如果我不需要不透明的结构,我个人通常会创建 2 个用于初始化的函数

int foo_init(struct foo *f); // allows the caller to allocate 'f' 
                             //however is suitable
struct foo * new_foo(void);  // mallocs and calls foo_init, for convenience.
Run Code Online (Sandbox Code Playgroud)

如果需要,相应的

 void foo_free(struct foo *f );   //frees and destroys 'f'
 void foo_destroy(struct foo *f); //cleans up whatever internal stuff 'f' has,
                                  // but does not free 'f' itself
Run Code Online (Sandbox Code Playgroud)

在您希望调用者将结构视为不透明的情况下,您将只提供一个 struct foo* new_foo(void); 不公开 struct foo 实现有一些好处:

  • 不允许呼叫者通过直接访问成员来四处闲逛或执行潜在的危险快捷方式。
  • 您可以在不破坏现有二进制文件的情况下更改 struct foo (您不会破坏 ABI),如果您正在实现一个库,这可能是一件大事。
  • 您的公共头文件不需要公开 struct foo 的实现和其他必需的头文件

缺点

  • 调用者无法控制 struct foo 的分配
  • 您将有总是必须通过函数调用来操作 struct foo 的开销