C 对链表实现的一些误解
对于如何用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)
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