C双指针含义

C double pointer meaning

我无法理解有关使用双指针的链表的 C 代码的含义。这是我正在阅读的代码

    struct list
{
    int value;
    struct list *next;
};
//Insert an element at the begining of the linked list
void insertBegin(struct list **L, int val)
{
    //What does **L mean?
    //Memory allocation for the new element temp
    struct list *temp;
    temp = (struct list *)malloc(sizeof(temp));
    //The new element temp points towards the begining of the linked list L
    temp->next = *L;
    //Set the beginning of the linked list
    *L = temp;
    (*L)->value = val;
}
void loop(struct list *L)
{
    printf("Loop\n");
    //Run through all elements of the list and print them
    while( L != NULL )
    {
        printf("%d\n", L->value);
        L = L->next;
    }
}
struct list* searchElement(struct list *L,int elem)
{
    while(L != NULL)
    {
        if(L->value == elem)
        {
            printf("Yes\n");
            return L->next;
        }
        L = L->next;
    }
    printf("No\n");
    return NULL;
}

int main()
{
    struct list *L = NULL;
    insertBegin(&L,10); // Why do I need 
    return 0;
}

insertElement中的**L是什么意思,**Lloop函数中的*L有什么区别?为什么主要是在声明 struct list *L = NULL 时我应该使用参数 &L 调用函数 insertBegin 而不是简单的 L?

我猜*L是指向链表第一个节点的指针,而**L可能指向链表的任何元素。但是,我不确定这是正确的。

感谢您的帮助!

类型 **L 被读取为指向 L 的指针的指针。因此,如果您有一个指向 L 的指针并获取其地址,这就是您得到的。函数参数(在 C 中)中 **L 的模式通常用于实现 "out parameter" - 代码可以更新的参数。要在开头插入,您需要更新指向列表头部的指针——这就是该函数将指向头部的指针作为参数的原因。分配给 *L 时,函数更新参数。

insertBegin 中的双指针用于将 L 的位置从 L 所在的位置更改为要插入的节点。调用函数时,您需要 &L 因为您需要通过引用传递它,因为您正在更改 L

表示"pointer to a pointer"。在 C 中,指针是按值传递的,因此如果您希望能够修改传递给该函数之外的函数的指针,则必须传递一个指向它的指针。传递指针只会为其传递另一个值,该值将在函数中修改但不会反映对其外部值的更改。作为指向指针的指针传递本质上允许您修改传递的地址处的值,而不仅仅是修改本地。

考虑这两个函数:

void foo(int * p, int * t) {
  p = t;
}

void foo2(int ** p, int * t) {
  *p = t;
}

代码:

int a = 1, b = 2;
int * p = &a;

foo(p, &b);
// in foo *p == 2, but here *p is still == 1, because p didn't change, only the copy that was passed to foo changed
foo2(&p, &b); // now *p == 2, p was changed in foo2

L 存储列表中第一个 link 的地址。因此: *L 是列表中第一个 link 的 contents,并且 &L 是存储列表中第一个 link 地址的变量 的地址。

换句话说,通过将参数传递给函数来为列表分配内存和初始化列表的唯一方法是提供 &L 作为参数。如果将 L 作为参数传递,该函数将接收第一个 link 的地址,而它需要一个地方来 存储 第一个 [=20] 的地址=].

如果您希望函数写入参数并在调用者中反映新值,则必须为该参数传递一个指针:

void foo( T *p ) // for any type T
{
  *p = new_value(); // write a new value to the thing p points to
}

void bar( void )
{
  T var;
  foo( &var ); // foo writes a new value to var
}

如果我们用指针类型Q *代替T,那么代码就是

void foo( Q **p ) // for any type Q
{
  *p = new_value(); // write a new value to what p points to
}

void bar( void )
{
  Q *var;
  foo( &var ); // foo writes a new value to var
}

两种情况下的语义完全相同;我们希望 foo 通过指针 p 更新存储在 var 中的值。唯一的区别是在第二种情况下 var 已经有一个指针类型,所以 p 必须是指向该指针类型的指针。

在您发布的代码中,insertBegin 函数更新存储在 L 中的值,它是指向列表头部的指针。由于main中的变量L的类型为struct list *insertBegin中的参数L的类型需要为[=25] =].