指针作为第二个参数而不是返回指针?

我注意到在C中接受一个非malloc ed指针作为第二个参数而不是返回指针是一个常见的习惯用法。 例:

 /*function prototype*/ void create_node(node_t* new_node, void* _val, int _type); /* implementation */ node_t* n; create_node(n, &someint, INT) 

代替

 /* function prototype */ node_t* create_node(void* _val, int _type) /* implementation */ node_t* n = create_node(&someint, INT) 

两种方法的优点和/或缺点是什么?

谢谢!

编辑谢谢大家的回答。 选择1的动机现在对我来说非常清楚(我应该指出,选择1的指针参数应该与我原先的想法相反)。

接受一个指针(调用者负责malloc’ing或不负责)要填写的内存,在返回指针(必然是malloc)的灵活性方面提供了明显的优势。 特别是,如果调用者知道它需要使用仅在某个函数内返回的任何东西,它可以传入堆栈分配的结构或数组的地址; 如果它知道它不需要重入,它可以传入一个static结构或数组的地址 – 在任何一种情况下,一个malloc / free对都会被保存,并且这样的节省会增加! – )

这没有多大意义。 C中的指针按值传递,就像其他对象一样 – 区别在于值。 使用指针,值是内存地址,传递给函数。 但是,您仍然在复制值,因此当您使用malloc ,您将更改函数内部指针的值,而不是外部的指针值。

 void create_node(node_t* new_node, void* _val, int _type) { new_node = malloc(sizeof(node_t) * SIZE); // `new_node` points to the new location, but `n` doesn't. ... } int main() { ... node_t* n = NULL; create_node(n, &someint, INT); // `n` is still NULL ... } 

有三种方法可以避免这种情况。 首先,正如您所提到的,从函数返回新指针。 第二种是获取指针指针,从而通过引用传递它:

 void create_node(node_t** new_node, void* _val, int _type) { *new_node = malloc(sizeof(node_t) * SIZE); // `*new_node` points to the new location, as does `n`. ... } int main() { ... node_t* n = NULL; create_node(&n, &someint, INT); // `n` points to the new location ... } 

第三个是在函数调用之外简单地malloc n

 int main() { ... node_t* n = malloc(sizeof(node_t) * SIZE); create_node(n, &someint, INT); ... } 

我通常更喜欢接收指针(属性初始化)作为函数参数,而不是返回指向函数内部已经malloc的内存区域的指针。 通过这种方法,您明确表示内存管理的责任在于用户方。

返回指针通常会导致内存泄漏,因为如果没有malloc()编辑它们会更容易忘记free()你的指针。

我通常不做出固定的选择,我干净地将它放入自己的库中并提供两全其美的选择。

 void node_init (node_t *n); void node_term (node_t *n); node_t *node_create () { node_t *n = malloc(sizeof *n); /* boilerplate error handling for malloc returning NULL goes here */ node_init(n); return n; } void node_destroy (node_t *n) { node_term(n); free(n); } 

对于每个malloc应该有一个免费的,因此对于每个初始化应该有一个术语,并且对于每个创建都应该有一个毁灭。 随着对象变得越来越复杂,您会发现自己开始嵌套它们。 某些更高级别的对象可以使用node_t列表进行内部数据管理。 在释放此对象之前,必须首先释放列表。 _init和_term关心这一点,完全隐藏了这个实现细节。

可以有关于进一步细节的决定,例如,destroy可以取一个node_t ** n并在释放后将* n设置为NULL。

我个人喜欢使用refernce或pointer params返回数据,并使用函数return返回错误代码。

1)由于Samir指出代码不正确,指针按值传递,你需要**

2)该函数本质上是一个构造函数,因此它分配内存和初始化数据结构是有意义的。 Clean C代码几乎总是面向对象,就像构造函数和析构函数一样。

3)你的函数是无效的,但它应该返回int,以便它可以返回错误。 将至少有2个,可能是3个可能的错误条件:malloc可能失败,类型参数可能无效,并且可能值超出范围。

本文中未讨论的问题是如何在分配函数的函数中引用malloc’ed缓冲区,并且可能在将控制权返回给调用者之前将其存储在其中。

在将我带到此页面的情况下,我有一个传入指针的函数,该指针接收HOTKEY_STATE结构数组的地址。 原型声明参数如下。

 HOTKEY_STATE ** plplpHotKeyStates 

返回值ruintNKeys是数组中元素的数量,它由分配缓冲区之前的例程确定。 但是,我没有直接使用malloc(),而是使用了calloc,如下所示。

 *plplpHotKeyStates = ( HOTKEY_STATE * ) calloc ( ruintNKeys , sizeof ( HOTKEY_STATE ) ) ; 

在我validationplplpHotKeyStates不再为null之后,我定义了一个本地指针变量hkHotKeyStates,如下所示。

 HOTKEY_STATE * hkHotKeyStates = *plplpHotKeyStates ; 

使用此变量,对于下标使用无符号整数,代码使用简单成员运算符(。)填充结构,如下所示。

 hkHotKeyStates [ uintCurrKey ].ScanCode = SCANCODE_KEY_ALT ; 

当数组完全填充时,它返回ruintNKeys,并且调用者拥有处理数组所需的一切,可以使用传统方式,使用引用运算符( – >),或者使用我在函数中使用的相同技术获得对arrays的直接访问。