【问题标题】:What is the mistake in my linked list code?我的链表代码有什么错误?
【发布时间】:2018-05-29 13:16:26
【问题描述】:

这是我用 ANSI C 编写的代码。 我不断收到运行时错误:分段错误(SIGSEGV)。 请帮帮我。 我是数据结构和 C 的新手。 我无法检测到问题。

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

typedef struct node
{
    int data;
    struct node *nxt;
}node;

node * create(int n);
void display(node *head);

int main()
{
    int n = 0;
    node *head = NULL;
    printf("Enter the number of nodes\n");
    scanf("%d", &n);
    head = create(n);
    display(head);
    return 0;
}

node * create(int n)
{
    int i;
    node *head = NULL;
    node *temp = NULL;
    node *p = NULL;
    for (i = 0; i < n; i++)
    {
        temp = (node *)malloc(sizeof(node));
        printf("\nEnter the value of %d node", i + 1);
        scanf("%d", &temp->data);
        temp->nxt = NULL;
        if (head == NULL)
        {
            head = temp;
        }
        else
        {
            p = head;
            while (p->nxt != NULL)
            {
                p = p->nxt;
                p->nxt = temp;
            }

        }

    }
    return head;
}

void display(node *head)
{
    node *p = NULL;
    if (head = NULL)
    {
        printf("\nEmpty List");
    }
    else
    {
        p = head;
        while (p != NULL);
        {
            printf("%d->", p->data);
            p = p->nxt;
        }
    }
}

【问题讨论】:

  • 欢迎来到 SO。您的代码很难阅读。请编辑您的问题以应用适当的缩进。除此之外,此时您应该开始考虑使用像 GDB 这样的调试器。它应该向您显示 SIGSEGV 发生的位置,这是搜索错误的良好起点。
  • 您的错误出现在循环while(p-&gt;nxt!=NULL) 中,您在其中重复分配temp。使用调试器并单步执行代码。
  • 在显示代码中,在 while 循环之后有一个不需要的分号:while (p != NULL); — 如果p 为空,则几乎可以保证在它之后的printf() 调用中崩溃,或者如果它不为空,则无限循环。两者都不可取。此外,在此之前,您可以使用if (head = NULL) 中的分配来删除您的head 指针。大多数现代编译器都会对此发出警告。如果您的编译器没有,请找到相应的选项,或者使用更好、更现代的编译器。

标签: c data-structures linked-list


【解决方案1】:

Thomas Jager 在他的answer 中为您提供了重要的修复。我在comment 中给了你两个重要的修复。当这些结合在一起时,代码对我有用。

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

typedef struct node
{
    int data;
    struct node *nxt;
} node;

node *create(int n);
void display(node *head);

static void error(const char *msg)
{
    fprintf(stderr, "%s\n", msg);
    exit(EXIT_FAILURE);
}

int main(void)
{
    int n = 0;
    node *head = NULL;
    printf("Enter the number of nodes: ");
    if (scanf("%d", &n) != 1)
        error("failed to read an integer");
    head = create(n);
    display(head);
    return 0;
}

node *create(int n)
{
    int i;
    node *head = NULL;
    node *temp = NULL;
    node *p = NULL;
    for (i = 0; i < n; i++)
    {
        temp = (node *)malloc(sizeof(node));
        if (temp == NULL)
            error("failed to allocate memory");
        printf("\nEnter the value of %d node: ", i + 1);
        if (scanf("%d", &temp->data) != 1)
            error("failed to read an integer");
        temp->nxt = NULL;
        if (head == NULL)
        {
            head = temp;
        }
        else
        {
            p = head;
            while (p->nxt != NULL)
            {
                p = p->nxt;
            }
            p->nxt = temp;
        }
        display(head);   // Debugging - check the list as it is built
    }
    return head;
}

void display(node *head)
{
    node *p = NULL;
    if (head == NULL)
    {
        printf("Empty List\n");
    }
    else
    {
        p = head;
        while (p != NULL)
        {
            printf("%d->", p->data);
            p = p->nxt;
            fflush(stdout);     // Band-aid - remove ASAP
        }
        putchar('\n');
    }
    fflush(stdout);     // Band-aid - remove ASAP
}

我在输入代码中使用了display 函数来确保列表始终正常运行。它占了下面显示的列表的额外副本。该代码还使用换行符终止输出行,这有助于确保它出现。有两个记录在案的对 fflush(stdout) 的“删除我”调用是不需要的,但在您的代码崩溃时调试时很有帮助。有一个部分论点是提示 printf() 调用后应该跟一个 fflush(stdout) 以确保出现提示。交互式输出通常不需要。

请注意,我添加了一个错误报告功能,以便于报告错误,从而鼓励检测可能的错误。您可以在 GitHub 上的 SOQ(堆栈溢出问题)存储库中看到我首选的错误处理代码,即 src/libsoq 子目录中的文件 stderr.cstderr.h

当我使用数据结构(例如列表)时,我通常会为自己创建一个 dump_list() 函数。通常有 2 或 3 个参数:

void dump_list(const char *tag, const node *list);
void dump_list(FILE *fp, const char *tag, const node *list);

'tag' 参数用于注释输出:

dump_list(__func__, head);  // In create()

dump_list("result", head);  // In main()

标签很重要;它允许您为使用该函数的每个地方创建一个不同的标记(我在很多场合都使用过dump_list("point 1", …)dump_list("point 2", …)、……在单个函数中)。 如果我认为我可能需要将它转到标准输出以外的位置(例如标准错误或日志文件),我会为自己提供带有 FILE * 参数的版本。使用它可以检查数据结构。请注意,该函数不允许修改数据结构。 display() 函数的格式可能与dump_list() 函数的格式不同——在这种情况下,您不会在main() 函数中调用dump_list()。但是拥有这样一个可用于验证数据结构的函数会很有帮助。

使用显示的代码,我可以运行程序(ll53 创建自 ll53.c,使用 GCC 8.1 进行干净编译),如下所示:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
>     -Wstrict-prototypes ll53.c -o ll53 
$ ./ll53
Enter the number of nodes: 1

Enter the value of 1 node: 234
234->
234->
$ ./ll53
Enter the number of nodes: 2 

Enter the value of 1 node: 234
234->

Enter the value of 2 node: 123
234->123->
234->123->
$ ./ll53
Enter the number of nodes: 7

Enter the value of 1 node: 987
987->

Enter the value of 2 node: 888
987->888->

Enter the value of 3 node: 789
987->888->789->

Enter the value of 4 node: 345
987->888->789->345->

Enter the value of 5 node: 444
987->888->789->345->444->

Enter the value of 6 node: 543
987->888->789->345->444->543->

Enter the value of 7 node: 0
987->888->789->345->444->543->0->
987->888->789->345->444->543->0->
$ ./ll53
Enter the number of nodes: 0
Empty List
$

【讨论】:

    【解决方案2】:

    在这部分:

        while(p->nxt!=NULL)
        {
            p=p->nxt;
            p->nxt=temp;
        }
    

    我认为p-&gt;nxt=temp; 应该在循环之外,像这样:

        while(p->nxt!=NULL)
        {
            p=p->nxt;
        }
        p->nxt=temp;
    

    【讨论】:

    • Thomas 的回答很好,但是由于 ';' 而 while 循环无法工作在原始示例中的 while 末尾。
    猜你喜欢
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-18
    • 1970-01-01
    • 2018-10-15
    相关资源
    最近更新 更多