C链接列表指针问题(无限循环)

时间:2013-11-20 13:34:07

标签: c pointers linked-list

编辑:新代码添加,当前问题是当我插入一个节点它工作正常,但如果我插入任何多个节点,insert_node方法永远不会完成,第一个语句打印,说“插入调用”然后它永远不会完成永远不会打印“插入完成”。

我唯一能想到的是while循环条件,但我不明白为什么这会是无限的。

此外,当我插入1个节点时,它打印正常,但它永远不会停止打印,再次是另一个无限循环。

关于错误原因的任何想法?

 /* 
 * File:   main.c
 * Author: che16
 *
 * Created on 20 November 2013, 08:59
 */

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

#include "structure.h"

/*
 * 
 */

node* head = NULL;
int set_head = 0;

int main(int argc, char** argv) {

    int no;

    printf("enter amount of books \n");

    scanf("%d", &no);

    create_books(no);
    print_list(head);


    return (EXIT_SUCCESS);

}

node* create_books(int no_of_books) {
    char title[50];
    char author[30];
    unsigned int number;
    int i;

    for (i = 0; i < no_of_books; i++) {
        node* new_node;
        new_node = (node *) malloc(sizeof (node));
        printf("enter book title \n");
        scanf("%s", title);
        printf("enter author name \n");
        scanf("%s", author);
        printf("enter ISDN number \n");
        scanf("%10u", &number);

        strncpy(new_node->btitle, title, 40);
        strncpy(new_node->name, author, 40);

        new_node->isbn = number;
        new_node->n = NULL;

        if (!set_head) {
            head = new_node;
            insert_node(head, new_node);
            set_head = 1;
            continue;
        }
        insert_node(head, new_node);
    }
}

void insert_node(node* head, node* insert) {
    printf("insert called \n");
    insert->n = NULL;

    if (head == NULL) {
        head = insert;
    } else {
        node* curr = head;

        while (curr->n != NULL) {
            curr = curr->n;
        }
        curr->n = insert;
    }
    printf("insert complete \n");

}

void delete_node(node* head, node * node) {

}

void print_list(node * head) {
    while (head) {
        printf("%s: \"%s\" (%u)\n", head->btitle, head->name, head->isbn);

        head = head->n;
    }
}

1 个答案:

答案 0 :(得分:0)

您的代码存在一些问题。

首先,你分配了太多的内存。临时局部变量只是指向现有内存的指针。您想要分配内存的唯一位置是create_book,您可以在其中创建数据。通过稍后删除节点,您必须free该内存。 (我认为您可能会对指针所扮演的不同角色感到困惑:有时它们只是识别新分配的内存,更常见的是它们用于访问和遍历已有的内存。)

接下来,head本身不是一个节点,它只是指向第一个节点的指针。如果没有节点,则此指针为NULL。这在程序开始时就是如此,这意味着你必须初始化它:

node* head = NULL;

但是如果从列表中删除所有书籍也是如此。显然,如果添加第一个节点或删除第一个节点,head会发生变化。您的insert_node应该注意这些变化。一种方法是将头部作为指针传递给节点指针。

(在C中,作为参数传递的所有内容都是本地副本。更改将使调用函数中的原始值保持不变。但是如果传递指针,则指向的东西在调用和调用中都是相同的叫做函数。你可以通过poiner改变它。)

此外,head是否需要更改应该是insert_node的关注点,而不是main中的调用(客户端)代码。如果你跳过所有错误的mallocs,插入就容易了:

void insert_node(node **head, node* insert)
{
    insert->n = NULL;

    if (*head == NULL) {
        *head = insert;
    } else {
        node* curr = *head;

        while (curr->n) curr = curr->n;
        curr->n = insert;
    }
}

您可以无条件地从main调用此内容:

insert_node(&head, create_book(title, author, number));

请注意所有这些也会导致head_set多余:如果head已设置,则它具有合法指针值,否则为NULL

出现抱怨不兼容指针的编译器错误,因为该结构没有名称。在C中,结构类型由单词struct定义,然后由其名称定义。有些人认为struct关键字是不必要的,typedef结构为单字类型。您可以在一个语句中执行此操作。你可以声明:

typedef struct node {
    char btitle[40];
    char name[40];
    unsigned int isbn;
    struct node* n;
} node;

或者你可以分开类型和结构:

typedef struct node node;

struct node {
    char btitle[40];
    char name[40];
    unsigned int isbn;
    struct node* n;
};

typedef可能会进入主源文件中的标题和结构。)

您会注意到我已将char *btitle更改为char btitle[40]。如果只存储指向标题的指针,则基本上存储指向title中本地缓冲区main的指针。此缓冲区用于循环中的输入,并且在打印书籍时,缓冲区包含第三本书的值。这意味着列表中的所有书籍都将具有相同的名称和作者。

解决此问题的一种方法是将指向缓冲区的内容复制到struct中,当然必须为其缓冲区提供内存,如上所示。您的create_book变为:

node* create_book(char* t, char* auth, unsigned int num)
{
    node* new_node;

    new_node = malloc(sizeof (node));

    strncpy(new_node->btitle, t, 40);
    strncpy(new_node->name, auth, 40);
    new_node->isbn = num;
    new_node->n = NULL;

    return new_node;
}

例程strncpy从第二个缓冲区到第一个缓冲区最多复制40个字符。您应该为原型包含<string.h>。另请注意,我将指向下一个节点的指针n显式初始化为NULL,正如我在main中使用head所做的那样。下一节点指针始终是NULL或指向有效节点的指针。

现在插入工作正常,打印列表很简单:

void print_list(node* head)
{
    while (head) {
        printf("%s: \"%s\" (%u)\n", head->btitle, head->name, head->isbn);

        head = head->n;
    }
}

查看代码如何仅使用节点本身中包含的数据,以及如何使用head == NULLhead短消息作为达到列表末尾的便捷标准。 (print_node函数不要求将head作为指向节点指针的指针,因为它永远不会更改列表,只检查其数据。)

还有一些问题需要解决,例如在使用后删除节点(或while列表)以及如何处理不成功的分配,但是通过这些更改,您的代码至少应该编译并运行。