链接列表打印意外值

时间:2017-11-10 11:59:53

标签: c++ class object linked-list

无论我检查了多少个引用,我总觉得我的实现是健全的。 但是,这个程序不起作用,我不知道为什么。 请帮忙。谢谢。 我有这个班级

class intNode
{
    int x;
    intNode * next;
    public:
        intNode();
        intNode(int y, intNode *p);
        setNode(int y, intNode *p);
        int getX();
        void setX(int y);
        void setNext(intNode *p);
        intNode* getNext();
};

和这个班级

class intList
{
    private:
       intNode * head;
    public:
       intList(); //sets head=NULL
       void push( int x);
       void print();
 }

推送如下

      void intList::push(int x)
      {
        intNode *newNode;
        newNode->setX(x);
        newNode->setNext(head);
        head = newNode;
      }

并打印如下

void intList::print()
{
    intNode *current = head;
    cout << "Printing list" << endl;
    while(current != NULL)
    {
        cout << current->getX() << "\t";
        current = current->getNext();
    }
    cout << endl;
}

但不知何故,主要是

中的这段代码
intList l;
l.push(5);
l.print();

返回这个奇怪的值:6946556

3 个答案:

答案 0 :(得分:2)

让我们检查您的push方法,看看哪里出错了。

intNode *newNode;

您现在有一个名为newNode的变量,它是指向intNode的指针。 此指针当前未设置为任何内容(我们称之为&#34;未初始化&#34;)。

尝试访问未初始化的变量(例如取消引用它或在其上调用方法)会导致undefined behavior,这是另一种说法&#34;所有赌注都已关闭&#34; - 标准没有说明应该发生什么,所以你的编译器可以生成它想要的任何东西。

所以当你这样做的时候:

newNode->setX(x);

您现在正在调用未初始化指针newNode上的方法。除此之外的任何事情与我们对代码的检查几乎没有关系,因为编译器可能无意中做了许多看似疯狂的事情中的任何一个&#34;当它优化程序时的事情。

例如,g++ 6.4.0 -O0,您的程序可以正常使用。它打印5.这是因为编译器没有优化任何东西,我们显然得到了幸运&#34;以及newNode&#34;恰好是&#34;实际上是一个有效的地址。

然而,一旦我转到-O1,该程序实际上根本没有输出任何值。我怀疑编译器确定push在所有代码路径上都会产生UB,因此得出结论:push必须永远不会被调用,并且根本没有为该方法生成任何程序集。所有。 (注意:我现在在程序集中验证了这一点。)

这只是在调用未定义行为时发生的疯狂事情之一 - 请参阅上面链接的文章以了解其他一些内容。未修改的行为应该在代码中永远

现在,正确的做法是分配 new 节点并将指针设置为指向它。用

初始化它
intNode *newNode = new intNode();

,那么你的代码看起来很好。不要忘记new是一个堆分配 - 当你不再需要(当你移除节点时)确保它delete d为你的工作,否则你会在那些没有被使用的情况下留下流浪的记忆(memory leak)。

PS:如果您使用g++选项调用-Wall,它会警告您这个错误:

test.cpp: In member function ‘void intList::push(int)’:
test.cpp:30:17: warning: ‘newNode’ is used uninitialized in this function [-Wuninitialized]
 newNode->setX(x);

始终注意编译器警告 - 这通常是他们的充分理由!

答案 1 :(得分:1)

intList::push中,它不会创建新节点,但会为其使用不确定的指针值。

必须分配新节点:

void intList::push(int x) {
    head = new intNode(x, head);
}

您可能希望在启用警告的情况下编译代码,因为这将是编译器警告。对于g++使用-Wall -Wextra -Werror命令行选项。

答案 2 :(得分:1)

Treeston很好地解释了为什么在这里发生任意的诡计,但我不喜欢他使用new的建议。

您的intNode 拥有 next指针,并且它是唯一所有者,因此您应该使用std::unique_ptr。类似地,intList 拥有 head

class intNode {
    friend class intList;
    int x;
    std::unique_ptr<intNode> next;
public:
    intNode();
    intNode(int _x, std::unique_ptr<intNode> _next); 
    int getX();
    intNode * getNext();
};

intNode::intNode() {}

intNode::intNode(int _x, std::unique_ptr<intNode> _next)
    : x(_x), next(std::move(_next)) {}

class intList {
    std::unique_ptr<intNode> head;
public:
    void push(int x);
    void print();
}

void intList::push(int x) {
    head = std::make_unique(x, std::move(head));
}

void intList::print()
{
    cout << "Printing list" << endl;
    for(intNode * current = head.get(); current; current = current->getNext())
    {
        cout << current->getX() << "\t";
    }
    cout << endl;
}