微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

链表与结构

如何解决链表与结构

基本上,我创建了一个带有struct的链表和一个将新节点添加到开头的函数。 我在链接列表中添加了4个节点,并使用for循环进行读取。但是输出结果很奇怪

#include <iostream>
using namespace std;

struct Lin{
    int val;
    Lin* nex;
};

Lin ext (Lin orig,int nod) {
    Lin fresh = {nod,&orig};
    return fresh;
};

int main(){
    Lin x;
    x.val=15;
    x=ext(x,25);
    x=ext(x,35);
    x=ext(x,45);

    for (int i = 0; i < 4; i=i+1) {
        cout <<x.val<< endl;
        x = *x.nex;
    }    
}

输出为:

45
35
-72537468
892483373
Segmentation fault (core dumped)

前2个数字正确,但第3个错误。 有人可以解释一下实际发生了什么吗?

解决方法

  1. class存储在堆栈中,并且没有初始化,因此x.nex是垃圾(甚至不是NULL)。
  2. addOneToElement也存储在堆栈中。它已初始化,但不幸的是,x也存储在堆栈中,并且两者仅在调用期间有效。 fresh返回新鲜的副本。

代码看起来像您更熟悉以所有内容为参考的语言(例如JS,C#和许多其他语言)。在C / C ++中并非如此,您必须显式使用指针。更像是:

orig

更新。

  1. 我忘记提及列表结构了。这是一条链,其中最后一个节点的ext为NULL(在C / C ++中为空指针;在现代C ++中也可以拼写为Lin *ext (Lin *orig,int nod) { return new Lin{nod,orig}; // Lin(nod,orig) on pre-C++11 systems } int main() { Lin *x = nullptr; // NULL on pre-C++11 systems x = ext(x,15); ... for (Lin *y = x; y; y = y->nex) cout << y->val << endl )。
  2. 正如JHBonarius注意到的那样,我忘记描述如何释放该列表(否则它将保留在内存中直到程序结束)。它是通过类似的循环完成的。这里的技巧是在删除节点之前,先保存nex 的值
nullptr

请注意,nxt在删除第一个节点时成为悬空的指针(即指向任意内存位置),因此最好将其设置为NULL(因为可以检查指针是否为NULL,但不能为NULL)。悬空;同时取消引用NULL几乎肯定会导致崩溃,而取消悬空指针的引用会导致晦涩的问题(有时也会崩溃)出现在不相关的程序部分中。

,

您的问题在这里:

Lin fresh = {nod,&orig};

您要获取orig的地址,该地址声明为:

Lin ext (Lin orig,int nod) {

所以orig实际上是Lin的副本!函数完成后,它将超出范围,因此将留下无效的指针和未定义的行为。

您可以通过引用而不是副本来避免这种情况:

Lin ext (Lin &orig,int nod) {

但是您每次都传递相同的Lin对象(x),因此&orig每次都将是相同的地址。您将需要多个具有不同地址的不同Lin对象。如果要继续避免动态分配内存,则可能类似于:

int main(){
    Lin x;
    x.val = 15;
    x.nex = nullptr; // Don't forget to null your tail's next!

    Lin x2 = ext(x,25);
    Lin x3 = ext(x2,35);
    Lin x4 = ext(x3,45);

    for (Lin i = x4; i.nex; i = *i.nex) {
        cout << i.val << endl;
    } 
}
,
    Lin ext (Lin orig,int nod) {
        Lin fresh = {nod,&orig};
        return fresh;
    };

此-Lin orig是作为原始数据的COPY传递的,对此进行引用会在某些时候使内存混乱,因为您正在引用临时变量的内存地址。

我自己传递一个指针

这样:

#include <iostream>
#include <string>

struct Lin 
{
   int val;
   Lin* nex;

   Lin(int value = 0,Lin *next = NULL) 
    : val(value),nex(next) 
   {}
};

Lin* ext(Lin *orig,int nod) 
{
    return new Lin(nod,orig);
};

int main()
{
   Lin *pi_x = new Lin(15);
   pi_x = ext( pi_x,25 );
   pi_x = ext( pi_x,35 );
   pi_x = ext( pi_x,45 );

   std::cout << "List:" << std::endl;
   for(Lin *lin = pi_x; lin != NULL; lin = lin->nex )
   {
      std::cout << lin->val << std::endl;
   }
   std::cout << "------" << std::endl;

  return 0;
}

最后但并非最不重要的-您正在遍历指针-内存地址。 通过将空指针作为终止元素来建模链接列表(next为null)

您要创建的结构至少应以某种内存分配开头,这样您就不会在Lin的指针中设置随机地址,并且还应确保不要取消引用空指针(即* x.nex)

- 我重新执行我的实现-由于注释正确地指出了该变量是堆栈分配的,并且'node factory'方法使用了相同的地址。

现在,通过调用“ new”,将动态分配(堆)内存,并且使用指针将仅在使用赋值运算符时更新内存地址,而不是像以前那样使用堆栈分配的内存复制结构的内容。

使用NULL检查终止是否为for循环可能是一件危险的事情-正如评论所暗示的,如果数据不符合预期的规则,则存在无限循环的风险。

运行修改后的代码的结果

    List:
    45
    35
    25
    15
    ------

关于内存泄漏,可以将实现更改为使用可以在“ ”中找到的智能指针

然后与交换任何原始的Lin指针

#include <memory>
std::shared_ptr<Lin> ptr;
ptr = std::make_shared<Lin>(0,NULL);

// Getting raw pointer from smart_pointer
Lin *raw = ptr.get();

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。