Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/arduino/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
理解在C中使用双指针创建单链表的代码_C_Linked List - Fatal编程技术网

理解在C中使用双指针创建单链表的代码

理解在C中使用双指针创建单链表的代码,c,linked-list,C,Linked List,我试图理解下面创建单链接列表的代码是如何使用双指针工作的 #include <stdio.h> #include <stdlib.h> struct Node { int data; struct Node* next; }; void push(struct Node** headRef, int data) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node))

我试图理解下面创建单链接列表的代码是如何使用双指针工作的

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

struct Node {
    int data;
    struct Node* next;
};

void push(struct Node** headRef, int data) {

    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;   
    newNode->next = *headRef;
    *headRef = newNode;
}

//Function to implement linked list from a given set of keys using local references
struct Node* constructList(int keys[], int n) {
    struct Node *head = NULL; 
    struct Node **lastPtrRef = &head; 
    int i, j;
    for(i = 0; i < n; i++) {
        push(lastPtrRef, keys[i]);
        lastPtrRef = &((*lastPtrRef)->next); //this line
        if((*lastPtrRef) == NULL) {
            printf("YES\n");
        }
    }

    return head;
}

int main() {
    int keys[] = {1, 2, 3, 4};
    int n = sizeof(keys)/sizeof(keys[0]);

    //points to the head node of the linked list
    struct Node* head = NULL;

    head = constructList(keys, n); //construct the linked list

    struct Node *temp = head;

    while(temp != NULL) { //print the linked list
        printf(" %d -> ", temp->data);
        temp = temp->next;
    }
}
最初,lastprref将指向指向NULL的head。在对push()的第一次调用中,在constructList()中的for循环中,head指向的值被更改(它指向包含值1的新节点)。因此,在第一次调用push()之后,lastprref将指向指向值为1的节点的头。但是,之后将执行以下行:

lastprref=&(*lastprref)->next

lastPtrRef被赋予新添加节点的下一个成员指向的任何对象的地址。在这种情况下,head->next为空

我不确定调用push()后更改lastprref的目的是什么。如果要构建链接列表,难道不希望lastprref具有指向包含1的节点的指针的地址,因为您希望将下一个节点(将包含2)推到列表的头部(即1)上吗

在constructList中for循环中对push()的第二次调用中,我们将传入lastprref,它指向head->next(NULL)和值2。在push()中创建新节点,包含值2,newNode->next指向head->next,该值为空。push中的headRef将被更改,以使其指向newNode(包含2)

也许我对代码的理解是错误的,但通过更改lastprref指向的内容,似乎忽略了包含1的节点。如果我们更改lastPtrRef保留的地址,我看不出链表是如何创建的


我真的很感激任何关于这段代码如何工作的见解。谢谢。

这使用了一种称为前向链接的技术,我相信您已经理解了这一点(使用指向前向链接的指针链接链表结构)

这个实现之所以让人困惑,是因为
push
函数的设计似乎是为了将项目填充到列表的开头,但在本例中,它将项目填充到了列表的尾部。那么它是如何做到的呢

重要的是要理解
push
中这条看似微不足道的语句:

newNode->next = *headRef
这似乎不重要,但我向你保证这是重要的。在这种情况下,
push
函数对该函数的实际功能造成了严重的不公正。实际上,它更像是一种通用的
insert
。关于这个函数的一些事实

  • 它接受指向指针
    headRef
    的指针作为参数,以及一些要放入正在管理的链表中的数据
  • 分配一个新节点并在其中保存数据后,它将新节点的
    next
    指针设置为当前存储在取消引用的
    headRef
    指针中的任何值(so..a指针),这就是我上面提到的行所实现的
  • 然后,它将新节点的地址存储在刚刚从中提取先前地址的同一位置;i、 e.
    *头EF
  • 有趣的是,它没有返回值(它是
    void
    ),这进一步让人有些困惑。结果它不需要一个
在返回给来电者时,起初似乎什么都没有改变
lastprref
仍然指向某个指针(实际上与以前的指针相同;它必须是,因为它是通过值传递给函数的)。但现在指针指向刚刚分配的新节点。此外,新节点的
next
指针指向函数调用之前
*lastprref
中的任何值(即,函数调用之前
lastprref
指针中的任何值)

这很重要。这就是该行代码所强制执行的,这意味着如果您使用
lastprref
对指向NULL的指针进行寻址(例如初始循环条目上的
head
),则该指针将接收新节点,新节点的
下一个
指针将为
NULL
。然后,如果您将
lastprref
中的地址更改为指向最后插入节点的
next
指针(该指针指向NULL;我们刚刚讨论过这一点),并重复该过程,它将在那里挂起另一个节点,在每次迭代中将该节点的
next
指针设置为
NULL
,等等,
lastprref
处理最后一个节点的
next
指针,该指针始终为
NULL

这就是
push
被用来构建前向链表的方式。最后一个想法。如果你有这样的链接列表,你会得到什么:

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

struct Node
{
    int data;
    struct Node* next;
};

void push(struct Node** headRef, int data)
{
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->next = *headRef;
    *headRef = newNode;
}

int main()
{
    //points to the head node of the linked list
    struct Node* head = NULL;
    push(&head, 1);
    push(&head->next, 2);
    push(&head->next, 3);

    for (struct Node const *p = head; p; p = p->next)
        printf("%p ==> %d\n", p, p->data);
}
然后将新节点的
next
指针的地址作为第一个参数附加到该节点,类似于
constructList
所做的操作,但没有
lastprref
变量(这里不需要它):

但这是:

push(&head->next, 3);
嗯。与前一个调用相同的指针地址,那么它会做什么呢?提示:记住
newNode->next=*headRef
行的作用(我一直在唠叨这个问题;我希望有东西卡住了)

上面程序的输出是这样的(显然,实际地址值会有所不同,取决于您的实例和实现):


希望有帮助

lastprref=&(*lastprref)->next相当于
lastprref=&(*lastprref)->next
只需将
lastprref
设置为
lastprref->next
的地址。回想一下,
lastprref
保存
head
的地址,您不能只将
head
发送到
push()
push()
将接收指针的副本,因此您可以传递指针的地址,以便
push()
在与您在
constructList
中的地址相同的地址上运行。代码相当奇怪:push()函数在开头插入,然后
push(&head, 1);
push(&head->next, 2);
push(&head->next, 3);
0x100705950 ==> 1
0x10073da90 ==> 3
0x100740b90 ==> 2