C 中的链表函数参数

Linked List Function Arguments in C

我做链表已经有一段时间了,我一直想知道。为什么有些人在任何函数中使用双指针声明参数,而有些人使用单指针?

它们对输出没有任何影响,只是增加了程序员的工作量,因为需要在各处添加额外的 '&'s and extra '*'。

假设您要将一个新值推到一个单向链表中,并且您将函数参数声明为

push_front( struct Node *head, int value )

在这种情况下,该函数处理指向头节点的指针值的副本。更改副本不会影响用作参数的原始指针。

因此您需要 return 向调用者更改函数中指向头节点的指针。这种情况下的函数声明看起来像

struct Node * push_front( struct Node *head, int value );

函数的调用者应记住通过函数的 returned 指针将指针重新分配给头节点,例如

struct Node *head = NULL;
//...
head = push_front( head, value );  

但是如果新节点的内存分配在函数内失败会怎样?

在这种情况下,该函数将 return 一个空指针。在这种情况下,这条语句

head = push_front( head, value );  

会导致内存泄漏,因为指向头节点的指针会被空指针覆盖。

所以你需要写这样的东西

struct Node *new_node = push_front( head, value );
if ( new_node != NULL ) 
{
    head = new_node;
}
else
{
    puts( "Error: no memory available." );
}

通常使用此类函数的用户会忘记检查 returned 指针是否为空指针。

函数的定义方式如下

struct Node * push_front( struct Node *head, int value )
{
    struct Node *new_node = malloc( sizeof( struct Node ) );
    
    if ( new_node != NULL )
    {
        new_node->value = value;
        new_node->next  = head;
    }

    return new_node;
} 

另一种方法是通过引用将指针传递给头节点。在这种情况下,该函数可以 return 一个值,该值将报告新节点是否已成功添加。

在这种情况下,可以像

那样声明和定义函数
int push_front( struct Node **head, int value )
{
    struct Node *new_node = malloc( sizeof( struct Node ) );
    int success = new_node != NULL;

    if ( success )
    {
        new_node->value = value;
        new_node->next  = *head;

        *head = new_node;
    }

    return success;
}

而且函数可以这样调用

if ( !push_front( &head, value ) ) 
{
    puts( "Error: no memory available." );
}

如您所见,在不引入中间指针的情况下,函数的调用看起来不那么混乱,就像调用前一个函数的情况一样。

也就是引用传递头节点指针时的函数接口更加直接明了

如果在通过引用传递指向头节点的指针时对函数声明使用这种方法,那么例如在 C++ 中,函数看起来会更简单。例如

void push_front( Node * &head, int value )
{
    head = new Node { value, head };
}