我注意到在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,与我原先的想法相反)。
答案 0 :(得分:16)
接受一个指针(调用者负责malloc'ing或不负责)来填充内存,在提供返回指针的灵活性方面具有明显的优势(必然是malloc'ed)。特别是,如果调用者知道它需要使用仅在某个函数内返回的任何东西,它可以传入堆栈分配的结构或数组的地址;如果它知道它不需要重入,它可以传入static
结构或数组的地址 - 在任何一种情况下,都会保存malloc / free对,并且这样的节省会增加! - )< / p>
答案 1 :(得分:5)
这没有多大意义。 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);
...
}
答案 2 :(得分:2)
我通常更喜欢接收指针(属性初始化)作为函数参数,而不是返回指向函数内部已经malloc的内存区域的指针。通过这种方法,您明确表示内存管理的责任在于用户方。
返回指针通常会导致内存泄漏,因为如果没有malloc()编辑它们会更容易忘记free()你的指针。
答案 3 :(得分:1)
我通常不会做出固定的选择,我会干净地将它放入自己的库中并提供两全其美的效果。
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。
答案 4 :(得分:0)
我个人喜欢使用引用或指针参数返回数据,并使用函数return返回错误代码。
答案 5 :(得分:0)
1)由于Samir指出代码不正确,指针按值传递,你需要**
2)该函数本质上是一个构造函数,因此它分配内存和初始化数据结构是有意义的。 Clean C代码几乎总是面向对象,就像构造函数和析构函数一样。
3)你的函数是无效的,但它应该返回int以便它可以返回错误。将至少有2个,可能是3个可能的错误条件:malloc可能失败,类型参数可能无效,并且可能值超出范围。
答案 6 :(得分:0)
本文中未讨论的问题是如何在分配它的函数中引用malloc'ed缓冲区,并且可能在将控制权返回给调用者之前将其存储在其中。
在将我带到此页面的情况下,我有一个传入指针的函数,该指针接收HOTKEY_STATE结构数组的地址。原型声明参数如下。
HOTKEY_STATE ** plplpHotKeyStates
返回值ruintNKeys是数组中元素的数量,由分配缓冲区之前的例程确定。但是,我没有直接使用malloc(),而是使用了calloc,如下所示。
*plplpHotKeyStates = ( HOTKEY_STATE * ) calloc ( ruintNKeys ,
sizeof ( HOTKEY_STATE ) ) ;
在我验证plplpHotKeyStates不再为null之后,我定义了一个本地指针变量hkHotKeyStates,如下所示。
HOTKEY_STATE * hkHotKeyStates = *plplpHotKeyStates ;
使用此变量,对于下标使用无符号整数,代码使用简单成员运算符(。)填充结构,如下所示。
hkHotKeyStates [ uintCurrKey ].ScanCode = SCANCODE_KEY_ALT ;
当数组完全填充时,它返回ruintNKeys,并且调用者拥有处理数组所需的一切,无论是以传统方式,使用引用运算符( - &gt;),还是采用与我相同的技术在函数中使用以获得对数组的直接访问。