C 为什么我们要用双指针来增加前面/后面的节点,而不是中间的?

C 为什么我们要用双指针来增加前面/后面的节点,而不是中间的?,c,pointers,pass-by-reference,singly-linked-list,function-definition,C,Pointers,Pass By Reference,Singly Linked List,Function Definition,我有一段代码,它在最前面创建了新节点: void 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); (*head_ref) = new

我有一段代码,它在最前面创建了新节点:

void 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);
   
    (*head_ref)    = new_node;
}
我有一段代码,在另一个节点之后创建一个新节点:

void insertAfter(struct Node* prev_node, int new_data) 
{ 
    if (prev_node == NULL) 
    { 
       printf("the given previous node cannot be NULL");     
       return; 
    } 
          
    struct Node* new_node =(struct Node*) malloc(sizeof(struct Node)); 
  
    new_node->data = new_data; 
  
    new_node->next = prev_node->next; 
  
    prev_node->next = new_node; 
}
我理解,如果我们在第一种情况下传递一个指针a.k.
head
,更改将仅在
push
功能中生效,而不是在整个程序中生效。这就是为什么我们使用对
head
的引用来更改它指向的内容

不过,在第二种情况下,我们只传递一个指针。我只是不明白为什么当我们不使用对前一个节点的引用时,即使我们将
next
更改为指向新节点,代码仍然可以工作


这些更改不应该只保留在
insertAfter
函数中吗?

对这两种情况的解释是指针和C中的任何其他变量一样,都是按值传递的

当您更改作为参数传递的指针的值时,在函数中,您需要传递指向该指针的指针,因为您将更改指针本身

如果要更改变量所指向的值,只需传递指针的副本即可。它仍然可以工作,因为您将更改存储在该指针中的地址中的值,地址的副本就可以了

例如:

void func(int *ptr)
{
    static int a = 20;
    *ptr = 10; // changing the value stored in the address pointed by ptr
    ptr = &a;  // changing ptr itself, (spoiler, won't reflect in the original)
}
您将看到
ptr
指向的变量仍然是
var
,而不是
a
,并且输出将是
10
,因为您可以再次更改指针指向的变量的值


而如果将指针传递给该指针:

void func(int **ptr)
{
    static int a;
    *ptr = &a;  // changing the value of the pointer
    **ptr = 20; // changing the value of the variable stored int the address stored in ptr
}

现在
ptr
将指向
a
,而不是
var
,并且输出将是
20
,我们能够更改指针值和存储在指针中的地址中的值。

在第一种情况下,您正在修改指针
head\u ref
指向的内容

在第二种情况下,您正在修改指针
prev\u节点所指向的内容。(
A->B
表示
(*A.B

这两个函数都使用指向应该修改的内容和修改指向的内容的指针。

在第一个函数中

void 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);
   
    (*head_ref)    = new_node;
}
指向头部节点的指针本身已更改。因此,函数需要处理原始指针,而不是其值的副本

因此,您需要通过引用将指针传递到th head节点

在C中,通过引用传递意味着通过指向对象的指针间接传递对象。在本例中,取消对指针的引用,如函数的此语句中所示

    (*head_ref)    = new_node;
该函数可以直接访问原始传递的对象

在第二个函数中

void insertAfter(struct Node* prev_node, int new_data) 
{ 
    if (prev_node == NULL) 
    { 
       printf("the given previous node cannot be NULL");     
       return; 
    } 
          
    struct Node* new_node =(struct Node*) malloc(sizeof(struct Node)); 
  
    new_node->data = new_data; 
  
    new_node->next = prev_node->next; 
  
    prev_node->next = new_node; 
}

指针prev_节点本身未被更改。正在更改的是指针指向的节点。因此,不需要通过引用将原始指针传递给函数。另一方面,指针
prev_node
指向的节点由于指针而通过引用传递。使用指针可以更改指向节点的数据成员。

这是第一种情况,是的。但是第二个呢?@zaro,这就是我想说的,第二个例子,当你传递一个指针时,你传递的是一个副本,当你改变它的值时,它不会反映在原始参数中,我会用一个例子来编辑。所以在第二个例子中,我可以修改prev_节点中的元素,但不能修改节点本身,对吗?修改节点中的元素不是在修改节点吗?是的。我的意思是,我可以修改节点中的元素,但不能修改指向它的指针?你说得对。已传递指向节点指针的副本,因此无法从被调用方函数更改调用方传递的指针。非常好的解释,谢谢!还有一件事-“(*head\u ref)”指的是指针“head”,因此它可以指向新节点?@zaro想象一下,您声明了指向head节点的指针,如struct node*head=NULL;然后通过引用传递指针,如push(&head,new_data)l。该函数通过取消引用现在指向原始指针头的参数(pointer)*head_ref,可以直接访问指针头。您可以使用
(结构节点**prev_节点,…
,没有问题。但是,当调用
InsertAfter
时,实际上不需要修改调用方一侧的
prev_节点
指针。
    (*head_ref)    = new_node;
void insertAfter(struct Node* prev_node, int new_data) 
{ 
    if (prev_node == NULL) 
    { 
       printf("the given previous node cannot be NULL");     
       return; 
    } 
          
    struct Node* new_node =(struct Node*) malloc(sizeof(struct Node)); 
  
    new_node->data = new_data; 
  
    new_node->next = prev_node->next; 
  
    prev_node->next = new_node; 
}