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 };
}
我做链表已经有一段时间了,我一直想知道。为什么有些人在任何函数中使用双指针声明参数,而有些人使用单指针?
它们对输出没有任何影响,只是增加了程序员的工作量,因为需要在各处添加额外的 '&'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 };
}