无法理解链表的定义

时间:2019-04-11 21:34:44

标签: c pointers struct linked-list

我对班级中定义的链表的定义有一些疑问。

这是使用的:

typedef struct node_t {
int x;
struct node_t *next;
} *Node;

现在,我了解到通过这种方式,我们创建了一种使用指向结点node_t的指针的较短方法。 Node将用作struct node_t*

现在,说我们要创建一个链表。例如:

Node node1 = malloc(sizeof(*node1));
Node node2 = malloc(sizeof(*node2));
Node node3 = malloc(sizeof(*node3));
node1->x = 1;
node1->next = node2;
node2->x = 4;
node2->next = node3;
node3->x = 9;
node3->next = NULL;

这大概就是我的想象(圆圈代表结构):

enter image description here 现在我知道这是错误的,但我不明白为什么。我们有一个指针node1,它指向我们的结构。然后,我们指向node2,它指向另一个结构,依此类推。

另一件事是,我不知道如何在图片中使用更长的箭头。我们不应该只能从圆圈的每个下部指向结构,而不能指向结构的指针吗?这怎么可能?

如果这里有人可以使事情变得更清楚一点,将不胜感激。非常感谢。

3 个答案:

答案 0 :(得分:5)

您有三个链接的节点,以及指向它们的其他本地指针。
尽管节点通常不方便使用名称来引用节点,但是节点对这些本地指针一无所知。
相反,他们知道序列中的下一个节点,最后一个节点都不知道。

换一种说法,您的图像完全错误。

          +---+------+
node1 --> | 1 | next |
          +---+-|----+
                |
                v
          +---+------+
node2 --> | 4 | next |
          +---+-|----+
                |
                v
          +---+------+
node3 --> | 9 | NULL |
          +---+------+

答案 1 :(得分:3)

分配是传递性操作。所以,

node1->next = node2;

将表示node1->next指向node2所指向的内容。并且特别是node1->next本身并不指向node2

node1node2node3中的每一个都命名为指针的变量。

node1       node2       node3
+---+       +---+       +---+
| * |       | * |       | * |
+ | +       + | +       + | +
  v           v           v
+---+---+   +---+---+   +---+---+
| 1 | * --> | 4 | * --> | 9 | * --> NULL
+---+---+   +---+---+   +---+---+

答案 2 :(得分:1)

typedef struct node_t {
    int x;
    struct node_t *next;
} *Node;                   /* <-- don't typedef pointers */

只需使用Node而不是Node *,然后分配:

Node *node1 = malloc(sizeof(*node1));

为什么?有人在typedef声明下方100行处查看您的代码,并不会固有地知道Node是类型还是 pointer-to-type 。这种混乱只会随着代码大小的增加而加剧。审核:Is it a good idea to typedef pointers?

注意:使用取消引用的指针在sizeof中设置类型大小,做得很好)

链接列表

链表只是一个聪明的数据结构,它使您可以迭代许多独立分配的节点。每个节点都包含一些data,然后是指向列表中下一个节点的指针,如果该节点是列表中的最后一个节点,则为NULL

(对于双向链接列表,您只需添加一个prev指针,该指针也指向列表中当前节点之前的节点。您还具有 circular 列表,其中最后一个节点指向第一个允许从列表中的任何节点迭代到任意其他节点的迭代,无论您开始与哪个节点进行迭代。对于双向链接的循环列表,您都可以从任何节点的两个方向对整个列表进行迭代)

在您的情况下,您的列表很简单:

     node1    +-> node2    +-> node3
    +------+  |  +------+  |  +------+
    | data |  |  | data |  |  | data |
    |------|  |  |------|  |  |------|
    | next |--+  | next |--+  | next |---> NULL
    +------+     +------+     +------+

其中data是单个整数值,而next指针仅保存列表中下一个节点的地址,或者NULL(如果它是列表中的最后一个节点) 。添加您的数据,您的列表将是:

     node1    +-> node2    +-> node3
    +------+  |  +------+  |  +------+
    |   1  |  |  |   4  |  |  |   9  |
    |------|  |  |------|  |  |------|
    | next |--+  | next |--+  | next |---> NULL
    +------+     +------+     +------+

创建列表时,第一个节点通常称为列表的 head ,最后一个节点称为列表的 tail 。您必须始终保留一个指向列表头的指针,因为该指针包含开始的列表地址。为了有效地插入列表,保持指向 tail 节点的指针也是一个好主意,这样您就可以简单地插入新节点,而无需遍历列表每次都找到最后一个节点,例如:

Node *newnode = malloc(sizeof(*newnode));   /* allocate */
newnode->next = NULL;                       /* initialize next NULL */
tail->next = newnode;                       /* assign to tail */
tail = newnode;                             /* set new tail at newnode */

列表是C的基础,Linux内核本身使用了许多列表。花一些时间来理解它们以及如何用不同的变体来编写它们。您会很高兴的。最后,不要忘了编写一个简单的函数以在完成后释放列表(并释放data,如果分配了它)。一个简单的free_list函数将是:

void free_list (Node *list) 
{
    while (list) {
        Node *victim = list;    /* separate pointer to node to free    */
        list = list->next;      /* can you see why you iterate next... */
        free (victim);          /* before you free the victim node?    */
    }
}

如果您还有其他问题,请告诉我。