添加到链表的节点在函数后恢复为NULL

时间:2015-04-11 20:43:31

标签: c++ pointers

我正在从文件中读取行并将其存储到链接列表中。

void add(llist *list, somevalue) {
    Node *newnode = (Node *) malloc(sizeof(Node));
    newnode->value = somevalue;
    newnode->next = list->head;
    list->head = newnode;
}

我从初始化函数调用此函数,该函数打开文件并从文件中读取行。

void init() {
    llist *list = (llist *) malloc(sizeof(llist));
    //
    //bunch of file i/o codes
    //
    while (read file until it returns NULL) {
        add(list, line);
        //if I try to print the values of the list here it works.
    }
    //Outside the loop, the head is back to NULL

}

我意识到的另一个问题是,每当我尝试打印值时,值都会被连接起来。也就是说,输出将是:

First Loop: Tony
Second Loop: Peter
             TonyPeter
Third Loop: Mir
            PeterMir
            TonyPeterMir
  1. 如何解决此问题,以便添加功能将节点永久添加到链接列表中?

  2. 为什么这些价值会像那样混乱?

  3. ---- ---- EDITED

    列表是一个全局变量,这里有一些来自init函数的片段。这是带有问题的while循环:

    //open file
    //initialize & declare pointers
    while (1) {
                for (i = 0; i < max; i++) {
                    value[i] = '\0';
                }
                if (!(fgets(value,max,f))) {
                    //segfaults if I try to print out the list inside this block.
                    break;
                }
    
                add(list, value);
    
                //the values are well separated in this statement
                printf("id is %s\n", list->head->value);
    
                //This print list works, but works weird as shown above.
                print_list(list);
        }
        fclose(f);
    
        //This print list doesn't work, the list is NULL
        print_list(list);
    

    这是打印列表功能:

    void print_users(llist *list) {
        ListNode *e;
        if (list->head == NULL) {
                printf("NO USERS\r\n");
                return;
        }
        e = list->head;
        while (e != NULL) {
                puts(e->id);
                e = e->next;
        }
    

    }

2 个答案:

答案 0 :(得分:0)

所以我对你正在尝试做的事情一点都不了解,所以我们只能这么做。您可以考虑发布MCVE。但是,我可以给你一些关于构建链表的指示。我直接将你的add函数复制到我刚刚建立的链表类中,并且它工作正常,因此llist类中可能还有其他东西导致了问题,或者它可能是代码中的其他内容。下面列出了班级和班级的简要说明。

基本节点类 注意:我使用了模板,但您可以轻松删除模板语句并将T替换为任何类型。

template<typename T>
class node {
    private:
        T       data;
        node*   next;

    public:
        // The C++11 rule of 5
        // Default Constructor
        node(T value = T(), node* nxt = nullptr) : data(value), next(nxt) { }

        // Copy Constructor
        node(const node<T>& src) : data(src.data), next(src.next) { }

        // Move Constructor
        node(node<T>&& src) : data(src.data), next(src.next) {
            src.data = T();
            src.next = nullptr;
        }

        // Copy Assignment Operator
        node<T>& operator=(const node<T>& src) {
            this->data = src.data;
            this->next = src.next;
            return(*this);
        }

        // Move Assignment Operator
        node<T>& operator=(node<T>&& src) {
            this->data = src.data;
            this->next = src.next;
            src.data = T();
            src.next = nullptr;
        }

        // Destructor
        ~node() {};

        // Some functions to help with encapsulation

        void set_next(node* nxt) {
            this->next = nxt;
        }

        void set_data(T value) {
            this->data = value;
        }

        node* get_next() {
            return(this->next);
        }

        T& get_data() {
            return(data);
        }
};

链接列表类正文 由于您使用的是动态内存,因此您需要确保遵守3 or 5规则,具体取决于您是否使用C ++ 11。

template<typename T>
class llist {
    private:
        node<T>* head;

    public:
        llist();

        llist(const llist& src);

        llist(llist&& src);

        llist(~llist);

        llist& operator=(const llist& src);

        llist& operator=(llist&& src);

        void push();

        void insert();
};

默认构造函数 没什么好看的。

template<typename T>
llist<T>::llist() : head(nullptr) { }

复制构造函数 由于您使用的是动态内存,因此这是至关重要的

template<typename T>
llist<T>::llist(const llist& src) {
        node<T>* tmp = src.head;
        while(tmp) {
            this->push(tmp->get_data());
        }
}

移动构造函数

template<typename T>
llist<T>::llist(llist&& src) {
    // delete current nodes
    node<T>* tmp = this->head;
    while(tmp) {
        tmp = head->get_next();
        delete head;
        head = tmp;
    }
    // steal the sources list
    this->head = src.head;
    src.head = nullptr;
}

<强>析

template<typename T>
llist<T>::~llist() {
    node<T>* tmp = this->head;
    while(tmp) {
        tmp = head->get_next();
        delete head;
        head = tmp;
    }
}

复制分配运算符

template<typename T>
llist& llist<T>::operator=(const llist<T>& src) {
    node<T>* tmp = src.head;
    while(tmp) {
        this->push(tmp->get_data());
    }
    return(*this);
}

移动作业运算符

template<typename T>
llist& llist<T>::operator=(llist<T>&& src) {
    node<T>* tmp = this->head;
    while(tmp) {
        tmp = head->get_next();
        delete head;
        head = tmp;
    }
    this->head = src.head;
    src.head = nullptr;
    return(*this);
}

推送会员 这与您的add成员基本相反。

template<typename T>
void llist<T>push(T data) {
    node<T>* new_node = new node<T>(data);
    if(this->head) {
        node<T>* tmp = this->head;
        while(tmp->get_next()) {
            tmp = tmp->get_next();
        }
        tmp->set_next(new_node);
    } else {
        this->head = new_node;
    }
}

插入成员 这基本上是您的add成员。

template<typename T>
void llist<T>insert(T data) {
    node<T>* new_node = new node<T>(data, this->head);
    this->head = new_node;
}

我不知道这是否会有所帮助,你可能已经知道了大部分内容,但我希望它有所帮助。

答案 1 :(得分:0)

在此代码中,您似乎试图为“llist”用户定义对象'malloc'空间。

void init() {

    llist *list = (llist *) malloc(sizeof(llist));
    //
    //bunch of file i/o codes
    //
    while (read file until it returns NULL) {
        add(list, line);
        //if I try to print the values of the list here it works.
    }
    //Outside the loop, the head is back to NULL
}

首先,您将其标记为C ++。在C ++中,您只需使用new和delete。 C ++编译器不会将“malloc”与用户创建的名为“llist”的对象的ctor / dtor相关联。我向你保证,你确实想要创建这两种方法,即使每种方法都很简单。真。

另一方面,C ++编译器确实提供了New和Delete,并且在适用于动态变量(在堆中)和自动变量(在堆栈上)时将自动调用ctor和dtor。编译器不支持malloc。


其次,您的函数init()不会返回或以其他方式将您命名为“list”的自动变量的值传递给任何其他范围。 (通常,列表生命周期超过使用或创建它的任何函数的生命周期。)

因此,您的对象“list”仅存在于函数init()的范围内,在init()的生命周期内。不太有用。

因此,'malloc'事物列表的句柄丢失了,其他任何东西都无法访问。在init()之后,你在哪里计划listHead驻留?

即使您使用了新的(和删除),代码仍然无法在任何地方传递listHead。

为了推进您的计划,您可能需要以下两项内容中的一项:

1)“list”句柄的返回(来自函数)(我一直按你的意思称它为“listHead”,对吗?)

llist* init() {
    llist *listHead = ...

    return(listHead);
}

OR

2)init函数更改的参数引用。这会将列表头放在init()之外。

void init( llist** listHead) {
    llist *list = ...

    *listHead = list;
}

您可以查看并从std :: list中获取提示,其中有40多种方法,但您可能只需要10.对于您计划实现的方法,您应该努力遵守并使用类似的名称和参数

也许您打算将类数据属性与标签列表一起使用(很难从您提供的内容中想象这一点)。在这种情况下,您应该区分数据属性名称以帮助您记住它是什么,并且它具有不同的范围。例如,我会使用m_listHead。前缀m_​​(或者通常只是一个字符前缀'_')简单地向读者指示该符号是类的数据属性。这个想法是一个常见的c ++习惯用法,并不是由编译器强制执行的,但通常是编码标准的一部分。

祝你好运。