我用C写了一段关于交换链表中相邻节点的代码,但由于分段错误,该代码无法在VSCode中运行。我不知道是什么原因造成的,代码在在线编译器上运行时输出正确。
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int val;
struct node *next;
} node;
void insert_end(node **head, int d)
{
node *new_node = (node *)malloc(sizeof(node)), *temp;
new_node->val = d;
if (*head == NULL)
*head = new_node;
else
{
temp = *head;
while (temp->next != NULL) //segmentation fault
temp = temp->next;
temp->next = new_node;
new_node->next = NULL;
}
}
node *swap(node **head)
{
node *temp = (node *)malloc(sizeof(node));
temp->next = *head;
node *start = (*head)->next;
while (temp->next != NULL && temp->next->next != NULL)
{
node *ptr1 = temp->next;
node *ptr2 = temp->next->next;
temp->next = ptr2;
ptr1->next = ptr2->next;
ptr2->next = ptr1;
temp = ptr1;
}
return start;
}
void disp(node *head)
{
while (head != NULL)
{
printf("%d\n", head->val);
head = head->next;
}
}
void main()
{
int arr[] = { 1, 2, 3 };
int size = sizeof(arr) / sizeof(arr[0]);
node *head = NULL;
for (int i = 0; i < size; i++)
{
insert_end(&head, arr[i]);
}
node *ptr = swap(&head);
disp(ptr);
}
4条答案
按热度按时间atmip9wb1#
代码中存在多个问题:
main
的原型应该是:insert_end()
中,当分配列表的第一个节点时,您不初始化temp->next
。malloc()
不会初始化内存块,因此无法预测temp->next
在分配后可能包含什么。这导致测试while (temp->next)
在while
循环的第二次迭代中插入下一项时具有未定义的行为。根据具体情况,temp
可能是空指针,问题不会发生(如在线编译器),或者temp
可能包含无效指针,导致您在系统上观察到的分段错误。始终将
temp->next
初始化为NULL
,并检查潜在的分配失败。node *swap(node **head)
函数应该实现什么是相当模糊的。其意图似乎是交换节点对。在任何情况下,都不需要分配新的node
,并且根据编码,该节点不会被释放,并且可能会丢失。也不清楚为什么swap
应该返回一个node *
,它应该更新head
指针。下面是一个修改后的版本,它返回更新后的头节点。
p8ekf7hl2#
对于符合C标准的启动器,应将不带参数的函数
main
声明为函数
insert_end
可以调用未定义的行为。首先,它不会检查新节点是否已成功分配。其次,如果
*head
等于NULL
,则新节点的数据成员next
保持未初始化这就是为什么这个while循环
产生分段故障。
请注意,在单链表的尾部添加新节点是低效的。在这种情况下,最好定义一个双边单链表。
函数
swap
的返回类型node *
令人困惑。由于函数通过指向头节点的指针的引用来接受指向头节点的指针,因此假设在调用函数之后,指向头节点的指针将在函数内正确地更新,这在逻辑上是正确的。但主要问题是在函数中分配额外的节点
这会导致内存泄漏。该函数不应分配任何节点。
这份声明
可以为空列表调用未定义的行为。你需要检查 *head是否等于
NULL
。你也应该把这个函数分成两个函数。这将使你的代码更加清晰和可读。main函数将遍历列表,其他函数将交换两个相邻的节点。
另外,如果使用全名
display
而不是缩写名称disp
,则代码将更具可读性。至少函数参数应该用限定符const
声明,因为在函数中传递的列表不会被改变。您还需要一个函数,当不再需要列表时,它将释放列表的所有已分配内存。
下面是一个演示程序,展示了如何声明和定义上述函数。
程序输出为
9rbhqvlz3#
考虑您的
insert_end
函数,您发现分段错误发生。malloc
为node
分配内存(你不应该在C中强制转换malloc
的结果,这与C++不同)。val
。next
。这不能假定为NULL
。如果它不是
NULL
,那么你的循环会给temp
分配一个随机地址,然后temp->next
解引用这个随机地址。未定义的行为可能会引发错误,但很可能是分段错误,这就是您遇到的问题。为了解决这个问题,根据评论,你有两个选择:
calloc
分配,将所有内存分配给0
。new_node->next
初始化为NULL
。qlckcl4x4#
第1步:使用警告和调试符号编译:
第二步和第三步:解决这个问题,然后运行valgrind:
第4步:第19行(你标记的那个)中的一个未初始化的变量似乎有问题。别这样初始化变量。
第五步:你也会泄漏内存。但可能你还没有到那个地步。
通过静态代码分析工具可以获得更多的见解,如clang-tidy:
只要一个接一个地解决任何问题,直到你修复了所有的警告。