C 对链表实现的一些误解

C 对链表实现的一些误解,c,linked-list,C,Linked List,对于如何用C实现链表,我似乎有一个很大的误解。据我所知(这段代码尽可能简化!),这应该是可行的,但会返回一个分段错误。还值得注意的是,如果我注释掉MARKER下面的所有内容,它似乎是有效的,但可能有一个潜在的问题使它出现故障 #include <stdio.h> #include <stdlib.h> typedef struct node { int val; struct node *next; } node; int main (void)

对于如何用C实现链表,我似乎有一个很大的误解。据我所知(这段代码尽可能简化!),这应该是可行的,但会返回一个分段错误。还值得注意的是,如果我注释掉
MARKER
下面的所有内容,它似乎是有效的,但可能有一个潜在的问题使它出现故障

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


typedef struct node
{
    int val;
    struct node *next;
} node;


int main (void)
{
    node *pode;
    node *nx, *vx, *sx;

    pode = (node *) malloc(1 * sizeof(node));
    pode->val = 12;

    pode = (node *) realloc(pode, 2 * sizeof(node));
    pode->next = (pode + 1);
    (pode + 1)->val = 66;

    pode = (node *) realloc(pode, 3 * sizeof(node));
    pode->next = (pode + 2);
    (pode + 2)->val = 33;

    pode = (node *) realloc(pode, 4 * sizeof(node));
    pode->next = (pode + 3);
    (pode + 3)->val = 44;


    printf("%d\n", pode->val);

    nx = pode->next;
    printf("%d\n", nx->val);

    // MARKER
    vx = nx->next;
    printf("%d\n", vx->val);

    sx = nx->next;
    printf("%d\n", sx->val);

    return 0;
}
#包括
#包括
类型定义结构节点
{
int-val;
结构节点*下一步;
}节点;
内部主(空)
{
节点*pode;
节点*nx,*vx,*sx;
pode=(node*)malloc(1*sizeof(node));
pode->val=12;
pode=(节点*)realloc(pode,2*sizeof(节点));
pode->next=(pode+1);
(pode+1)->val=66;
pode=(节点*)realloc(pode,3*sizeof(节点));
pode->next=(pode+2);
(pode+2)->val=33;
pode=(节点*)realloc(pode,4*sizeof(节点));
pode->next=(pode+3);
(pode+3)->val=44;
printf(“%d\n”,pode->val);
nx=pode->next;
printf(“%d\n”,nx->val);
//标记
vx=nx->next;
printf(“%d\n”,vx->val);
sx=nx->next;
printf(“%d\n”,sx->val);
返回0;
}

boo-boo在哪里?

您的第一个错误是使用了
realloc()
。这很容易被烧坏,因为它可能会移动节点,也可能不会移动节点,导致指向它的任何指针成为危险的悬空指针

链表都是关于用指针将节点链接在一起的,因此没有理由将一个节点重新分配到一个更大的节点中,这样就无法通过更改指针来拼接列表。通常,您会分别分配每个节点。(在某些情况下,程序可能会通过将列表节点放入“空闲”列表中来解除分配列表节点,以便进行非常快速的解除分配和以后的重新分配。)

第二个错误是在分配节点时忘记设置每个节点的
next
指针。不要依赖分配器来清除它

即使代码正确地设置了所有
next
指针,
realloc()
也可能会移动节点,破坏所有这些指针


最后,如果您在调试器(例如CLion)中单步执行正在运行的程序,您将能够看到悬挂指针的位置。

好的,您的第一个错误是使用
realloc()
。这很容易被烧坏,因为它可能会移动节点,也可能不会移动节点,导致指向它的任何指针成为危险的悬空指针

链表都是关于用指针将节点链接在一起的,因此没有理由将一个节点重新分配到一个更大的节点中,这样就无法通过更改指针来拼接列表。通常,您会分别分配每个节点。(在某些情况下,程序可能会通过将列表节点放入“空闲”列表中来解除分配列表节点,以便进行非常快速的解除分配和以后的重新分配。)

第二个错误是在分配节点时忘记设置每个节点的
next
指针。不要依赖分配器来清除它

即使代码正确地设置了所有
next
指针,
realloc()
也可能会移动节点,破坏所有这些指针


最后,如果您在调试器(例如CLion)中单步执行正在运行的程序,您将能够看到哪里有悬空指针。

这里有两个问题。为什么我的代码会崩溃?这是做链表的“正确的”、“惯用的”方法吗

第二个第一个。不,不是。通常的方法是分别对每个节点进行malloc,并将节点链接在一起。您真正要构建的是一个可调整大小的数组。你需要

    node *pode; // the head
    node *n, *prev;

    pode = (node *) malloc(sizeof(node));
    pode->val = 12;

    n = (node *) malloc(sizeof(node));
    pode->next = n;
    n->val = 66;
    prev = n;
    n = (node *) malloc(sizeof(node));
    prev->next = n;
   n->val = 33;
    prev = n;
    n = (node *) malloc(sizeof(node));
    prev->next = n;
    n->val = 44;
etc.

要找出代码崩溃的原因,请在调试器下运行它,这里有两个问题。为什么我的代码会崩溃?这是做链表的“正确的”、“惯用的”方法吗

第二个第一个。不,不是。通常的方法是分别对每个节点进行malloc,并将节点链接在一起。您真正要构建的是一个可调整大小的数组。你需要

    node *pode; // the head
    node *n, *prev;

    pode = (node *) malloc(sizeof(node));
    pode->val = 12;

    n = (node *) malloc(sizeof(node));
    pode->next = n;
    n->val = 66;
    prev = n;
    n = (node *) malloc(sizeof(node));
    prev->next = n;
   n->val = 33;
    prev = n;
    n = (node *) malloc(sizeof(node));
    prev->next = n;
    n->val = 44;
etc.

要了解代码崩溃的原因,请在调试器下运行它

如果
realloc
决定移动内存块,则
pode->next
指向已释放的旧内存块。如果
realloc
决定移动内存块,则
pode->next
指向已释放的旧内存块。“realloc很慢”. 和什么相比?引用还是证据?@rici公平问题。与从空闲列表中删除节点相比,速度非常慢。与malloc()相比,它必须在适当的位置增长节点,否则malloc、memcpy和free,更不用说问题中的O(N^2)行为,它通过扩展/复制所有以前的节点来分配每个额外的节点,关于悬空指针,您肯定是对的,但如果您总是要分配连续存储,则不需要指针。因此,数据结构可以更小,缓存也更友好。重新分配一个更小的向量通常是不可行的,如果分配足够大,重新分配更大的向量通常也是很小的工作。一些realloc实现会过度分配,所以OPs策略不一定是二次的(尽管我只建议非生产代码使用它。)@rici Yes。关于如何实现向量可以教很多东西,但问题是如何修复链表实现。我同意。我只是反对realloc is slow评论,这对我来说似乎完全不合适。OP不应该使用它,因为它不起作用(悬空指针);它的速度无关紧要,“realloc很慢”。和什么相比?引用还是证据?@rici公平问题。与从空闲列表中删除节点相比,速度非常慢。与malloc()相比,它必须在适当的位置增长节点,否则malloc、memcpy和free,更不用说问题中的O(N^2)行为,它通过扩展/复制所有以前的节点来分配每个额外的节点,关于悬空指针,您肯定是对的,但如果您总是要分配连续存储,则不需要指针。所以t