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

时间:2010-03-26 02:13:45

标签: c pointers

我注意到在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,与我原先的想法相反)。

7 个答案:

答案 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;),还是采用与我相同的技术在函数中使用以获得对数组的直接访问。