c - 链表段错误

标签 c debugging segmentation-fault

下面我用 C 语言制作了一个简单的链表。该代码目前正在产生一个段错误,我觉得这很奇怪,因为我正在从我们当前的书中复制一个示例。我对代码所做的唯一一件事就是将代码放入方法“addToList”中。我知道段错误来自 addToList 方法,但我不知道我在哪里犯了错误。

#include <stdio.h>
#include <stdlib.h>
typedef struct node {
  int val;
  struct node *next;
} Node;

void addToList(Node *, int);
void printList(Node *);

void main() {
  int x;
  Node *head = malloc(sizeof(Node));
  for (x = 1; x < 4); x++) {
    printf("Enter an integer: ");
    x = scanf("%d");
    addToList(head, x);
  }
  printList(head);
}

void addToList(Node *head, int val) {
  Node *current = head;

  while (current->next != NULL) {
    current = current->next;
  }

  current->next = malloc(sizeof(Node));
  current->next->val = val;
  current->next->next = NULL;
}

void printList(Node *head) {
  Node *current = head;

  while (current != NULL) {
    printf("%d->", current->val);
    current = current->next;
  }
  printf("\n");
}

如能告诉我哪里出了问题或哪里出错,我们将不胜感激。

最佳答案

仔细查看您的代码:

int main(void) {
  int x;
  Node *head = malloc(sizeof(Node));
  for (x = 1; x < 4); x++) {
      ...
    addToList(head, x);
  }
  ...
}

你没有初始化内存,所以 head->valhead->next 不是 初始化。正因为如此

while (current->next != NULL) {
    current = current->next;
}

将循环未定义的次数。第一个 current->next 是最 可能不是 NULL,所以 current = current->next 被执行。那时 current 指向任何地方,因此在您的情况下未定义的行为会导致段错误。

你必须像这样初始化内存:

Node *head = malloc(sizeof *head);
if(head == NULL)
    // error handling

head->next = NULL;

但您也可以使用 calloc,它也将内存设置为 0,因此您不必初始化值(在这种情况下):

Node *head = calloc(1, sizeof *head);
if(head == NULL)
    // error handling

您应该始终检查 malloc/calloc/realloc 的返回值。

另请注意,main 函数的签名可以是以下之一:

  • int main(void);
  • int main(int argc, char **argv);
  • int main(int argc, char *argv[]);

编辑

我现在注意到的另一个错误:

x = scanf("%d");

不是这样的scanf作品。你必须传递一个指针,scanf 保存 通过传递的指针扫描值。 scanf 返回匹配的个数 成功的值(value),在这种情况下,成功将是 1:

int num;
int ret = scanf("%d", &num);
if(ret != 1)
{
    fprintf(stderr, "Could not read value from the user\n");
    continue; // to contiune looping

    // you could also do a break; and stop the looping, or
    // exit(1), etc.
}
    // error with scanf

也不要将相同的变量 x 用于循环迭代和用户输入, 否则你就是在搞乱循环。

编辑

User user3629249 wrote in the comment

good information, however the result will be the first entry in the linked list will contain garbage. Better to declare head via: Node *head = NULL; and the function addToList() check for NULL and proceed accordingly.

没错,head 元素并没有以这种方式保存任何数字。

选项一:双指针

这里 addToList 接收一个双指针。 head 的初始化发生 当 *head 指向 NULL 时。该函数为其分配内存,初始化 内存,保存值并返回。在 addToList 的并发调用中 *head 不会是 NULL,因此 addToList 查找列表的末尾。

我对您执行 mallocrealloc 的方式做了一些小改动。我还添加了 freeList 的实现,应该用于释放内存:

void addToList(Node **head, int val) {
    if(head == NULL)
    {
        fprintf(stderr, "head cannot be NULL\n");
        return;
    }


    if(*head == NULL)
    {
        *head = calloc(1, sizeof **head);
        head[0]->val = val;
        head[0]->next = NULL;
        return;
    }
    Node *current = *head;

    while (current->next != NULL) {
        current = current->next;
    }

    current->next = malloc(sizeof *current->next);
    if(current->next == NULL)
        return;
    current->next->val = val;
    current->next->next = NULL;
}

int main(void)
{
    int x;
    Node *head = NULL;
    for (x = 1; x < 4; x++)
    {
        int val;
        printf("Enter an integer: ");
        if(scanf("%d", &val) != 1)
        {
            fprintf(stderr, "Could not read from user. Skipping entry\n");
            continue;
        }

        addToList(&head, val);
    }

    printList(head);

    freeList(head);
    return 0;
}

void freeList(Node *head)
{
    if(head == NULL)
        return;

    Node *current = head;
    Node *next;

    while(next = current->next)
    {
        free(current);
        current = next;
    }

    free(current); // the last one

    free(head);
}

选项 2:addToList 返回指向头部的指针

这里 addToList 接受一个指向头部的指针。如果它是 NULL,它分配 内存和初始化如上所示。如果 head 不是 NULL,则 函数查找最后一个元素并返回 head。出错时 函数返回 NULL

Node *addToList(Node *head, int val) {

    if(head == NULL)
    {
        head = calloc(1, sizeof **head);
        head->val = val;
        head->next = NULL;
        return head;
    }
    Node *current = *head;

    while (current->next != NULL) {
        current = current->next;
    }

    current->next = malloc(sizeof *current->next);
    if(current->next == NULL)
        return NULL;
    current->next->val = val;
    current->next->next = NULL;

    return head;
}

int main(void)
{
    int x;
    Node *head = NULL, *tmp;
    for (x = 1; x < 4; x++)
    {
        int val;
        printf("Enter an integer: ");
        if(scanf("%d", &val) != 1)
        {
            fprintf(stderr, "Could not read from user. Skipping entry\n");
            continue;
        }

        tmp = addToList(head, val);
        if(tmp == NULL)
        {
            fprintf(stderr, "Not enough memory\n");
            freeList(head);
            return 1;
        }

        head = tmp;

    }

    printList(head);

    freeList(head);
    return 0;
}

关于c - 链表段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48332774/

相关文章:

c - fgets 和 sscanf 的结构导致意外结果

c - 用二进制值切换大小写值?

在 foreach 循环中导出函数时,C 符号名称不在加载表中

c - 如果我有这样的c语言消息 ".exe has stopped working"?

javascript - 处理和跟踪 JavaScript 错误

python - 使用 int 列表的稀疏矩阵切片

php - 第 1 行第 'budget' 列的数据被截断”

java - 找不到java.lang.ArrayIndexOutOfBoundsException的问题

C++动态数组在分配时导致段错误

c - 调整动态数组大小后出现段错误