所以我一直在努力理解链接列表的概念(在看一些示例代码时,我在互联网上发现了这个。现在,如果我可以请某人确认我是否正确掌握了一些概念。我将绘制我认为每个代码链接的图表。
#include <stdio.h>
#include <stdlib.h>
struct ListItem {
int data;
struct ListItem *next;
};
int main (int argc, char *argv[]){
struct ListItem a;
a.data = 0;
a.next = NULL;
struct ListItem b;
b.data = 1;
b.next = NULL;
struct ListItem c;
c.data = 2;
c.next = NULL;
a.next = &b;
b.next = &c;
int counter = 0;
struct ListItem *i;
for (i = &a; i != NULL; i = i->next){
printf("Item %d value is %d\n\r", counter, i->data);
counter++;
}
return 0;
}
代码段1:
struct ListItem {
int data;
struct ListItem *next;
};
这将创建一个名为ListItems的结构。该结构有两个组件,一个用于存储数据的组件和一个指向struct ListItem类型的另一个结构的指针。我想象链接列表如下:
这是一种可视化的正确方法吗?
代码段2:
struct ListItem a;
a.data = 0;
a.next = NULL;
struct ListItem b;
b.data = 1;
b.next = NULL;
struct ListItem c;
c.data = 2;
c.next = NULL;
是的,我知道它可以缩短但我只是这样做,看看我是否能理解这个概念。现在,这个片段创建了一个struct ListItem类型的变量“a”,“b”和“c”。然后它将每个结构的第一个成员(数据)分别设置为0,1,2,将第二个成员(下一个)设置为NULL。所以我的可视化现在是这样的:
现在更多问题:
问题1:当我们最初指向NULL时,它指向什么都没有?我们为什么要做这个?最初没有指向什么?
摘录3:
a.next = &b;
b.next = &c;
这个让我们在每个变量a,b(它是一个结构)中的下一个分别指向b和c的地址存储位置。
我的可视化:
问题:它怎么能这样做?结构本身不是存储在多个内存地址上(4表示int等)
摘录4:
int counter = 0;
struct ListItem *i;
for (i = &a; i != NULL; i = i->next){
printf("Item %d value is %d\n\r", counter, i->data);
counter++;
}
这是我曾经有点困惑的片段。现在,我们预留了一个名为counter的整数,并将其初始化为零。此外,我们创建一个名为i的变量,它指向struct ListItem类型。现在有人可以向我解释for循环吗?我对它正在做的事情感到有点困惑。特别是,i = i->接下来,我对此并不熟悉。我知道它相当于i =(* i).next但不确定它到底是做什么的。有人可以创建一个快速图表吗?
另外:如果有人对某些有用的网站有任何好的资源/链接(没有双关语),以帮助我理解链接列表,可以随意发布它们。
答案 0 :(得分:2)
Q1:未初始化的指针不指向“无”。如果你没有设置它的值,它可以(并且将)指向任何,包括(但不限于)nul值,越界内存或最后一个密码你进入了一个网站。
当你做
时,问counter
的价值是什么并没有什么不同
int counter;
...
printf ("counter is %d\n", counter);
因此,将next
指针设置为NULL有两个不同的目的。首先,您确定它是一个“已知”值,而不是任何随机值。其次,按照惯例,NULL值表示它不指向任何有效位置。
(这是'约定',因为NULL有效地存储数值'0'。这是一个内存地址 - 因为你在谈论指针 - - 完全有效。它的日常用途并不多。)
答案 1 :(得分:0)
对问题1的回答:不,最初结构是未初始化的,并且其next
字段很可能指向没有正确的,但可能是它不包含NULL
。
问题2:每个结构变量的地址都在内存中。当然它的字段(组件)也有它们的地址。整个结构的地址是其第一个字段的地址。
对于代码段4中的i = i->next
:i
是指向当前列表项的指针(最初设置为a
的地址)。赋值获取当前列表项的字段next
的值,并将此值移动到i
。所以i
现在指向下一个列表项。这只会将i
一个项目向右移动(它在列表上进行迭代)。当i
指向列表的最后一项时,i->next
为NULL
,因此处理后最后一项i
变为NULL
并且循环终止。
答案 2 :(得分:0)
问题1:当我们最初是指向
NULL
的指针时,它指向的是什么?我们为什么要做这个?最初没有指向什么?
是。 NULL不能是任何节点的地址,因此它不指向任何东西。我们初始化为NULL以避免程序中的错误。它还假设使用某个默认值初始化变量的良好做法。
假设你没有用NULL初始化next,那么next将指向一些垃圾节点,如果你的代码有错误,那么访问未分配的内存是未定义的行为。而未初始化的下一个值是垃圾,不同的运行会表现出不同的方式。但是如果初始化为NULL,它将产生常量结果,因此很容易跟踪错误。阅读:Initialize a variable。
问题2 :特别是
i = i->next
,我对此并不熟悉。我知道它相当于i = (*i)
.next,但不确定它的真正作用。有人可以创建一个快速图表吗?
在表达式i = i->next;
中,您将下一个节点的地址分配给i
指针。内存中的链表可以假设为:
a b c
+---+ +---+ +---+
| a |--->| 1 |--->| 2 |---+
+---+ +---+ +---+ |
null
最初在for循环中将表达式a
中的节点i
的地址分配给i = &a
,它就像这样:
a b c
+---+ +---+ +---+
| a |--->| 1 |--->| 2 |---+
+---+ +---+ +---+ |
▲ ▲ null
i i->next
i = i->next;
^ ^
| next to `i` in linked list
current node
运营商->
呼叫Member selection via pointer
,请注意i
是struct ListItem*
类型的指针。
您也可以使用名为.
的{{1}}运算符执行相同操作,如下所示:
Member selection via object name
运算符 i = (*i).next;
和->
都可用于访问对象的成员。运算符.
对地址有用,->
对值变量很有用。
问题3 :我们预留了一个名为counter的整数并将其初始化为零。此外,我们创建一个名为i的变量,指向struct
.
类型。现在有人可以向我解释for循环吗?
在您的代码中,ListItem
用于我上面解释的,以便在通过链表时保持当前节点的地址,而i
仅用于计算链表中的节点。
counter
另外:如果有人对某些有用的网站有任何良好的资源/链接(没有双关语),以帮助我更好地了解链接列表,请随时发布。
从源代码中读取,其中概念用图表和代码解释。您可以参考:How to create Linked list using C?