最近在搞手写二叉树,单单插入元素就搞了3天…
话不多说,上代码:

节点与树:

TT
struct Node
{T data;Node<T>* leftNode;Node<T>* rightNode;
};TT
class myTree
{public:myTree();
//    ~myTree();void PrintNode(Node<T>* inputNode);void PrintTree();Node<T>** FindNode(Node<T>* p,T x);void Insert(T x);int Size();T Delete(T x);
//private:Node<T>* firstNode;int TreeSize;
};TT
myTree<T>::myTree()
{firstNode=nullptr;TreeSize=0;
}

打印节点、打印树等简单成员函数:

TT
void myTree<T>::PrintNode(Node<T>* inputNode)
{Node<T>* p=inputNode;if (p->leftNode!=nullptr)PrintNode(p->leftNode);cout<<p->data<<' ';if (p->rightNode!=nullptr)PrintNode(p->rightNode);
}TT
void myTree<T>::PrintTree()
{PrintNode(firstNode);cout<<"<PrintTreeDone.\n";
}TT
int myTree<T>::Size()
{return TreeSize;
}TT
T myTree<T>::Delete(T x)
{}

寻找节点:

TT
Node<T>** myTree<T>::FindNode(Node<T>* p,T x)
{if (x<p->data){if (p->leftNode==nullptr)return &(p->leftNode);elsereturn FindNode(p->leftNode,x);}else if (x>p->data){if (p->rightNode==nullptr)return &(p->rightNode);elsereturn FindNode(p->rightNode,x);}else{Node<T>** res=&p;   //No staticreturn res;
//        return &p;        //Warming [-Wreturn-local-addr]}
}

插入元素:

TT
void myTree<T>::Insert(T x)
{if (TreeSize==0){firstNode=new Node<T>;firstNode->data=x;firstNode->leftNode=nullptr;firstNode->rightNode=nullptr;TreeSize++;}else{Node<T>** p=FindNode(firstNode,x);Node<T>* q=*(p);                    //先斩后奏if (*p==nullptr){(*p)=new Node<T>;(*p)->data=x;(*p)->leftNode=nullptr;(*p)->rightNode=nullptr;TreeSize++;}else{cout<<"q == "<<q<<endl;cout<<"The number "<<(q)->data<<" has existed."<<endl;}}
}

主函数:

int main()
{myTree<int> t;for (int i=1;i<=3;i++)t.Insert(i);t.Insert(3);t.PrintTree();cout<<"<Done.";return 0;
}

解释:

某一个元素插入树中,该元素的存在性只有2种情况:存在和不存在。
对于不存在的元素,插入法则十分简单,那就是:

  1. 该元素比当前节点的值小→往节点左侧看
  2. 节点左侧为空→就是该元素在树的位置
  3. 节点左侧已经有节点→移动到左子节点,递归执行第1、2、3步

对于存在的元素,处理的办法就更简单了,什么都不管就行。
然而,如何判断此元素的存在性呢?

问题一:

按照5,4的顺序插入元素,需要返回p->leftNode(或者相关的什么东西)。
但是你注意到没有:p->leftNode是空指针,返回p->leftNode(的值)那就相当于return 0(啊这)。
(如果不是这种函数传来传去的做法,那就可以无视这个问题)
冷静下来……容易发觉我们要的是p->leftNode这个指针,那么应该返回p->leftNode这个指针的地址,然后直接修改之。

 if (x<p->data)                        //待查元素比当前节点元素小,寻访左子节点{if (p->leftNode==nullptr)     //当前节点左子节点是空的(萝卜坑),说明是目的地)return &(p->leftNode);     //p->leftNode是个指针,返回该指针的地址(指向指针的指针)elsereturn FindNode(p->leftNode,x);       //左子节点非空,递归查找之}

对应的Insert()代码:

     Node<T>** p=FindNode(firstNode,x);//此时p存的值就是FindNode()中“p->leftNode”的地址if (*p==nullptr)         //说明是个萝卜坑,填之{(*p)=new Node<T>;        //相当于“p->leftNode”=new Node<T>;(*p)->data=x;(*p)->leftNode=nullptr;(*p)->rightNode=nullptr;TreeSize++;}

问题二:

若满足条件(x == p->data),说明待查元素在树中存在,需要向外部发送“存在”的信号。根据“不存在就有萝卜坑”,容易想到应该返回该节点。
返回p吗?p存的恰是该节点的地址,容易判断不是“萝卜坑”,进而得知元素重复。但是由于Node<T>** myTree<T>::FindNode(Node<T>* p,T x)限定了返回值类型为“指向指向Node<T>的指针的指针”,让你一度怀疑人生……

辣我们就return &p;!但是这样子的话呢,会遇到[-Wreturn-local-addr]的问题。

(下面开启神棍模式,请各位批判观看,积极提出质疑点,感谢)

什么意思呢?就是说这里的p是个函数创建的局部变量,一旦函数退出,p也会被销毁,程序不能返回一个被销毁的指针的地址,这就是[-Wreturn-local-addr]
因此,就不传地址了,我们麻烦点,传值吧,以后再转换。

 else if (x == p->data){//        return &p;        //问题二:本来就应该返回这个,但是会[-Wreturn-local-addr]Node<T>** res=&p;   //不可以返回地址,应以传值方式返回之。(注意:不能加static)return res;}

问题三:

难道传值了就大吉大利了吗?函数退出,p被销毁,怎么看都没办法访问到原p所指向的节点。
不是说元素重复了吗?试图输出该重复的值:

     Node<T>** p=FindNode(firstNode,x);if (*p==nullptr)(略)else{cout<<"p == "<<p<<endl;cout<<"*p == "<<*p<<endl;cout<<"The number "<<(*p)->data<<" has existed."<<endl;}

果不其然,*p指向的,data也乱码了,说明没访问到原p所指节点。

但诡异的是……如果这样写是可以正确输出的:

     Node<T>** p=FindNode(firstNode,x);if (*p==nullptr)(略)elsecout<<"The number "<<(*p)->data<<" has existed."<<endl;

两者区别就在于有没有事先访问p和*p。好似见光死一般,一旦有直接访问的行为(不包括与nullptr比较),就不会访问到原p所指节点,犹如“局部变量的幽灵在内存游荡”。
见光死是吧?辣我们就先斩后奏,加一条Node<T>* q=*(p);

     Node<T>** p=FindNode(firstNode,x);Node<T>* q=*(p);                    //先斩后奏if (*p==nullptr)(略)else{cout<<"q == "<<q<<endl;cout<<"The number "<<(q)->data<<" has existed."<<endl;}

运行结果正常;并且查看内存信息,发现q所指向的,就是各节点。

问题四:

为什么对于不存在的元素,处理办法就相对简单一点

冷静点,都是屎代码了,急也急不来

总结

我还是看答案吧,语言特性太不友好了

C++ [-Wreturn-local-addr] 局部变量的幽灵 在内存游荡相关推荐

  1. java成员变量和局部变量的初始化和内存中的运行机制

    成员变量: 当系统加载类或创建类的实例时,系统会自动为成员变量分配内存空间,并在分配内存空间后,自动为成员变量指定初始值. eyeNum是类属性.name是实例属性 所有person实例访问eyeNu ...

  2. 闭包:让外部函数能访问函数内的变量,让局部变量长期贮存在内存中

    闭包:让外部函数能访问函数内的变量,让局部变量长期贮存在内存中 转载于:https://www.cnblogs.com/xfdmb/p/6126408.html

  3. 112.局部变量和全局变量在内存中是怎样存储的?113.WLAN无线传输协议

    动态申请数据:堆 局部变量:栈 全局变量:静态区 网络传输介质:是指网络中发送方和接收方之间得到物理通路. 常见的有同轴电缆.光纤.双绞线. 无线网络协议 (Wireless Local Area N ...

  4. 静态、全局和局部变量在作用域和内存空间的详解

    1.从作用域看: C++变量根据定义的位置的不同的生命周期,具有不同的作用域,作用域可分为6种: 全局作用域,局部作用域,语句作用域,类作用域,命名空间作用域和文件作用域. 1>全局变量具有全局 ...

  5. 全局变量和局部变量(local variable)

    全局变量(global variable) 在Lua中定义全局变量非常简单,就是定义变量的时候,前面不要加上local. 这个神秘的全局变量,其实本质上也是一个table,它把我们创建的全局变量都保存 ...

  6. JVM学习笔记之-运行时数据区概述及线程概述,程序计数器(PC寄存器),虚拟机栈(栈,局部变量表,操作数栈,动态连接,方法调用,方法返回地址等),本地方法接口,本地方法栈

    运行时数据区概述及线程概述 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JV ...

  7. 【C++】局部变量、全局变量、静态变量与动态对象的性质

    [fishing-pan:https://blog.csdn.net/u013921430转载请注明出处] 概述 局部变量 在一个函数内部定义的变量(包括函数形参)是局部变量. 全局变量 在函数外定义 ...

  8. C语言基础1:初识C语言(转义、注释;数组、操作符、反码、补码、static、define、指针、结构体常变量;局部变量;作用域、生命周期)

    文章目录 C语言基础1:初识C语言 1.C语言简介 1.1什么是C语言 1.2C语言的发展 2.第一个C语言程序 2.1创建项目 2.2添加源文件 2.3写代码 3.数据类型 4.变量.常量 4.1定 ...

  9. matlab 局部变量和全局变量,C++局部变量和全局变量(详解版)

    局部变量定义在一个函数内部,在函数之外是不可访问的.全局变量定义在所有函数之外,并且在其作用域内的所有函数都可以访问.下面做详细讲解. 局部变量 函数中定义的变量是该函数的局部变量.它们在其他函数的语 ...

最新文章

  1. 天龙源码分析 - 选择角色流程
  2. jupyter notebook修改默认工作目录
  3. WPF实现环(圆)形菜单
  4. 语音识别揭秘:你的手机究竟有多理解你?
  5. 22.基于深度学习的车型识别方法及系统实现
  6. web前端开发常用的几种图片格式及其使用规范
  7. WPS vbe6ex.olb 不能加载
  8. 新冠死亡率居高不下,为什么偏偏是意大利?
  9. kindle for pc版本更新后无法打开解决办法
  10. 基于Dav快速搭建React开发框架
  11. 卡通的平板卧推男孩动画特效
  12. golang中获取字符串长度的办法
  13. DOSBOX使用经验
  14. [Linux Audio Driver] SM6350平台音频bring up ( 一 )
  15. 定义一个数组存储10个上面描述的小怪兽,每个小怪兽的名字为(小怪兽+数组下标)
  16. 部署卡巴斯基网络版6.0:安装控制中心和服务器端推送
  17. HTML如何引入md5算法,HTML_VBS MD5加密算法代码,复制代码 代码如下:Private Const - phpStudy...
  18. 陈经纶2021年高考成绩查询时间,最新丨2018人大附等28所北京学校中高考成绩一览...
  19. 读书笔记--项亮《推荐系统实践》第一章
  20. show master status 时没有数据显示

热门文章

  1. 数据合集、并集和差集的产生(seq、cat、sort和uniq的综合使用)
  2. 终于有一篇能让小白更容易理解GC算法的文章了
  3. 工业自动控制过程中最好用SCADA软件推荐
  4. java c2 compiler_C2中的CompilerThread
  5. jiegputo matlab转置,matlab实现用免疫克隆算法求二元函数的最优值(附源码)
  6. LVTTL转LVDS GM8285C,28位LVDS发送器,替代GM8283
  7. C# 生成图片清晰度问题(转载)
  8. 你以为SSL是安全的吗?
  9. 风险投资(VC)私募股权投资(PE)30问答
  10. 网络设计部分知识点总结