【问题标题】:C double pointer meaningC双指针含义
【发布时间】:2015-03-12 16:21:22
【问题描述】:

我无法理解关于使用双指针的链表的 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 而不是简单的 L 来调用函数 insertBegin

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

感谢您的帮助!

【问题讨论】:

标签: c linked-list double-pointer


【解决方案1】:

它的意思是“指向指针的指针”。在 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

【讨论】:

    【解决方案2】:

    **L 类型被读取为指向 L 的指针。所以如果你有一个指向 L 的指针并获取它的地址,那就是你得到的。 **L 在函数参数中的模式(在 C 中)通常用于实现“输出参数” - 代码可以更新的参数。要在开头插入,您需要更新指向列表头部的指针 - 这就是该函数将指向头部的指针作为参数的原因。当赋值给*L时,函数更新参数。

    【讨论】:

    • *L 指向什么?排在榜首?如果是的话,在我看来你真的不需要使用**L指针,你可以直接更改*L指针中包含的地址。
    • 一个可以保存指向 L 的指针的内存位置。基本上它具有指向 L 的类型指针的变量或成员的地址。
    【解决方案3】:

    L 存储列表中第一个链接的地址。因此: *L 是列表中第一个链接的内容,并且 &L 是变量的地址,它存储了列表中第一个链接的地址。

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

    【讨论】:

    • 所以要修改第一个节点的地址,也就是*L。要更改 *L,您需要使用指向 *L 的指针。这就是为什么我们应该使用 **L?我说的对吗?
    【解决方案4】:

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

    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的类型需要为struct list **

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-25
        • 2017-08-20
        相关资源
        最近更新 更多