首页 > 解决方案 > C中的链表函数参数

问题描述

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

它们对输出没有任何影响,但它们只是增加了程序员要做的艰苦工作,因为需要在这里和那里添加额外的 '&' 和额外的 '*'。

标签: cpointerslinked-listpass-by-referencepass-by-value

解决方案


假设您要将新值推送到单链表,并且您声明了函数参数,例如

push_front( struct Node *head, int value )

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

因此,您需要将更改后的指向函数头节点的指针返回给调用者。这种情况下的函数声明看起来像

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

函数的调用者应记住通过从函数返回的指针重新分配指向头节点的指针,例如

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

但是,如果新节点的内存分配在函数内失败,会发生什么?

在这种情况下,函数将返回一个空指针。在这种情况下,这个声明

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." );
}

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

这是函数的定义方式

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;
} 

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

在这种情况下,可以像这样声明和定义函数

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 };
}

推荐阅读