使用指向指针的指针有什么意义?
What's the point of using pointer to pointer?
我在一个网站上找到这段代码,用于在链表的开头插入一个节点。
void push(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
/* 2. put in the data */
new_node->data = new_data;
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
}
这是函数调用-
push(&head, 7);
我的问题是为什么通过传递指针的地址然后在函数内部的地址处提取值来走很长的路。
为什么我们不能简单地在函数中传递指针 head
然后像 -
这样赋值
new_node->next=head_ref;
?
调用程序中的变量head是指向链表头部的指针。你需要一个指向这个变量的指针,以便在 push() 中更新它。因此,您将一个指针传递给一个包含指针的变量,以便您可以更新该变量。
如果您通过了 head
而不是 &head
,则行
(*head_ref) = new_node;
必须改为
head_ref = new_node;
但是,这将是功能的局部更改。它不会改变调用函数中 head
的值。这有两个问题:
- 调用函数永远不会得到工作列表。
- 函数中分配的内存将是内存泄漏。
你也可以这样写代码:
struct Node* push(struct Node* head_ref, int new_data)
{
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = head_ref;
return new_node;
}
并按如下方式调用它:
head = push(head, 7);
它实现了完全相同的事情,即在列表的头部插入一个新节点,然后更新 head
以指向该新节点。但在我看来,这不是一个很好的解决方案,因为它允许 push() 函数的调用者忘记将返回值分配给 head
,此时您将拥有孤立节点并最终成为内存泄漏。
这都与您选择的编程风格有关。使用一组函数来操作列表使程序结构合理。这样你就可以让它更具可读性。如果您需要从多个地方操作同一个列表或操作多个列表,它还将避免剪切-粘贴。
组织功能的方式由您决定。我个人喜欢你例子中的方式。在这种情况下,您只需要在参数中指定一次指向头部的指针。如果您 return 它来自函数,则您在程序中使用错误指针的风险更高。
'C' 拥有足够的工具集来编写任何类型的程序。但是有时您需要遵循特定的规则来应用它们,例如您的情况下的指针地址。不是很方便,但可靠且行之有效。作为一名 'C' 程序员,您会很快习惯的。
您还可以查看 c++,它有更好的工具来使这更方便。
我在一个网站上找到这段代码,用于在链表的开头插入一个节点。
void push(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
/* 2. put in the data */
new_node->data = new_data;
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
}
这是函数调用-
push(&head, 7);
我的问题是为什么通过传递指针的地址然后在函数内部的地址处提取值来走很长的路。
为什么我们不能简单地在函数中传递指针 head
然后像 -
new_node->next=head_ref;
?
调用程序中的变量head是指向链表头部的指针。你需要一个指向这个变量的指针,以便在 push() 中更新它。因此,您将一个指针传递给一个包含指针的变量,以便您可以更新该变量。
如果您通过了 head
而不是 &head
,则行
(*head_ref) = new_node;
必须改为
head_ref = new_node;
但是,这将是功能的局部更改。它不会改变调用函数中 head
的值。这有两个问题:
- 调用函数永远不会得到工作列表。
- 函数中分配的内存将是内存泄漏。
你也可以这样写代码:
struct Node* push(struct Node* head_ref, int new_data)
{
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = head_ref;
return new_node;
}
并按如下方式调用它:
head = push(head, 7);
它实现了完全相同的事情,即在列表的头部插入一个新节点,然后更新 head
以指向该新节点。但在我看来,这不是一个很好的解决方案,因为它允许 push() 函数的调用者忘记将返回值分配给 head
,此时您将拥有孤立节点并最终成为内存泄漏。
这都与您选择的编程风格有关。使用一组函数来操作列表使程序结构合理。这样你就可以让它更具可读性。如果您需要从多个地方操作同一个列表或操作多个列表,它还将避免剪切-粘贴。
组织功能的方式由您决定。我个人喜欢你例子中的方式。在这种情况下,您只需要在参数中指定一次指向头部的指针。如果您 return 它来自函数,则您在程序中使用错误指针的风险更高。
'C' 拥有足够的工具集来编写任何类型的程序。但是有时您需要遵循特定的规则来应用它们,例如您的情况下的指针地址。不是很方便,但可靠且行之有效。作为一名 'C' 程序员,您会很快习惯的。
您还可以查看 c++,它有更好的工具来使这更方便。