删除带有指向 C 中指针的指针的列表

Deleting a list with pointer to pointer in C

C 语言的部分代码在这里:

typedef struct List {
  double v;
  struct List *next;
} List;

void deleteList (List **p) {
  *p = (*p)->next;
}

我对 deleteList 函数的工作原理感到困惑。所以争论是一个指向List结构的指针。所以我们有:

p : pointer_2 --> pointer_1 --> List

所以我有一些问题:

  1. 那么函数deleteList()中的*p是什么?是 pointer_1 还是别的?
  2. = 之前的 *p 和 = 之后的 *p 意思一样吗?
  3. *p 和 (*p) 有区别吗?

假设我们有:

... la --> lb --> lc --> ld ....

然后说我们要删除 lb。理论上我明白了。您更改 la->next 以指向 lc。但是我对指针业务感到困惑。 deleteList() 的参数是什么? 是 deleteList(la->next) 吗?或者是其他东西? 然后是真正令人困惑的部分。 *p = ... 应该是 la->next 因为这是我们要更改的指针。 但是然后 ...(*p)->next,这不就是 lb 吗?但是我们想要lc?所以好像 *p 在同一行有不同的含义?!

让我们首先正确编写函数。

void deleteList( List **head ) 
{
    while ( *head != NULL )
    {
        List *tmp = *head;
        *head = ( *head )->next;
        free( tmp ); 
    }
}

指向头节点的指针通过引用传递给函数。

如果你将函数定义为

void deleteList( List *head ) 
{
    while ( head != NULL )
    {
        List *tmp = head;
        head = head->next;
        free( tmp ); 
    }
}

也就是说,如果指针不通过引用传递,那么函数将处理指针的副本。更改副本不会影响原始指针。

考虑以下演示程序。

#include <stdio.h>
#include <stdlib.h>

void f( int *p )
{
    p = NULL;
}

int main(void) 
{
    int x = 10;
    int *px = &x;

    printf( "Before the function call px = %p\n", ( void * )px );   

    f( px );

    printf( "Adter  the function call px = %p\n", ( void * )px );   

    return 0;
}

它的输出可能看起来像

Before the function call px = 0x7ffe26689a2c
Adter  the function call px = 0x7ffe26689a2c

即原始指针 px 未更改,因为该函数处理指针的副本。

要更改指针,您需要通过引用将其传递给函数

#include <stdio.h>
#include <stdlib.h>

void f( int **p )
{
    *p = NULL;
}

int main(void) 
{
    int x = 10;
    int *px = &x;

    printf( "Before the function call px = %p\n", ( void * )px );   

    f( &px );

    printf( "Adter  the function call px = %p\n", ( void * )px );   

    return 0;
}

现在程序输出可能看起来像

Before the function call px = 0x7ffed60815fc
Adter  the function call px = (nil)

在该函数中,您需要取消对参数的引用才能访问通过引用传递的指针。

*p = NULL;

^^^^

在函数 deleteNode 中也发生同样的情况。要检查传递的指针是否等于 NULL,请使用以下语句

while ( *head != NULL )
       ^^^

要访问原始指针指向的节点的数据成员next,您必须再次取消引用参数才能访问原始指针

*head

所以这个表达式产生了原始指针。所以接下来要访问数据成员你必须写

( *head )->next

您使用括号是因为后缀运算符 -> 具有更高的优先级,但您首先需要获取原始指针。

也就是说,如果你没有引用指针,你会写

head->next

但是当你有一个引用指针时,也就是当你有一个指向原始指针的指针时,为了获得原始指针,你必须像

这样取消引用引用指针
( *head )->next

您可以在不通过引用接受指向头节点的指针的情况下编写函数。但在这种情况下,您应该在调用者中再添加一条语句,将指针头设置为 NULL。

例如

void deleteList( List *head ) 
{
    while ( head != NULL )
    {
        List *tmp = head;
        head = head->next;
        free( tmp ); 
    }
}

在调用者中你需要写

List *head - NULL;

// the code thatf fills the list

deleteList( head );
head = NULL;

或者函数可以 return 空指针,如

List * deleteList( List *head ) 
{
    while ( head != NULL )
    {
        List *tmp = head;
        head = head->next;
        free( tmp ); 
    }

    return head;
}

在调用者中你可以写

List *head - NULL;

// the code thatf fills the list

head = deleteList( head );

通过引用定义接受头节点指针的函数的好处是函数的使用者不需要记住自己将指针设置为NULL。

deleteList 函数中:在传递到下一个元素之前,您必须释放指向的元素。

void deleteList (List **p) {
  while(*p != NULL){
    List *nextNode = (*p)->next;
    free(*P);
    *p= nextNode;
  }
}