C++ [-Wreturn-local-addr] 局部变量的幽灵 在内存游荡
最近在搞手写二叉树,单单插入元素就搞了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步
对于存在的元素,处理的办法就更简单了,什么都不管就行。
然而,如何判断此元素的存在性呢?
问题一:
按照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] 局部变量的幽灵 在内存游荡相关推荐
- java成员变量和局部变量的初始化和内存中的运行机制
成员变量: 当系统加载类或创建类的实例时,系统会自动为成员变量分配内存空间,并在分配内存空间后,自动为成员变量指定初始值. eyeNum是类属性.name是实例属性 所有person实例访问eyeNu ...
- 闭包:让外部函数能访问函数内的变量,让局部变量长期贮存在内存中
闭包:让外部函数能访问函数内的变量,让局部变量长期贮存在内存中 转载于:https://www.cnblogs.com/xfdmb/p/6126408.html
- 112.局部变量和全局变量在内存中是怎样存储的?113.WLAN无线传输协议
动态申请数据:堆 局部变量:栈 全局变量:静态区 网络传输介质:是指网络中发送方和接收方之间得到物理通路. 常见的有同轴电缆.光纤.双绞线. 无线网络协议 (Wireless Local Area N ...
- 静态、全局和局部变量在作用域和内存空间的详解
1.从作用域看: C++变量根据定义的位置的不同的生命周期,具有不同的作用域,作用域可分为6种: 全局作用域,局部作用域,语句作用域,类作用域,命名空间作用域和文件作用域. 1>全局变量具有全局 ...
- 全局变量和局部变量(local variable)
全局变量(global variable) 在Lua中定义全局变量非常简单,就是定义变量的时候,前面不要加上local. 这个神秘的全局变量,其实本质上也是一个table,它把我们创建的全局变量都保存 ...
- JVM学习笔记之-运行时数据区概述及线程概述,程序计数器(PC寄存器),虚拟机栈(栈,局部变量表,操作数栈,动态连接,方法调用,方法返回地址等),本地方法接口,本地方法栈
运行时数据区概述及线程概述 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JV ...
- 【C++】局部变量、全局变量、静态变量与动态对象的性质
[fishing-pan:https://blog.csdn.net/u013921430转载请注明出处] 概述 局部变量 在一个函数内部定义的变量(包括函数形参)是局部变量. 全局变量 在函数外定义 ...
- 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定 ...
- matlab 局部变量和全局变量,C++局部变量和全局变量(详解版)
局部变量定义在一个函数内部,在函数之外是不可访问的.全局变量定义在所有函数之外,并且在其作用域内的所有函数都可以访问.下面做详细讲解. 局部变量 函数中定义的变量是该函数的局部变量.它们在其他函数的语 ...
最新文章
- 天龙源码分析 - 选择角色流程
- jupyter notebook修改默认工作目录
- WPF实现环(圆)形菜单
- 语音识别揭秘:你的手机究竟有多理解你?
- 22.基于深度学习的车型识别方法及系统实现
- web前端开发常用的几种图片格式及其使用规范
- WPS vbe6ex.olb 不能加载
- 新冠死亡率居高不下,为什么偏偏是意大利?
- kindle for pc版本更新后无法打开解决办法
- 基于Dav快速搭建React开发框架
- 卡通的平板卧推男孩动画特效
- golang中获取字符串长度的办法
- DOSBOX使用经验
- [Linux Audio Driver] SM6350平台音频bring up ( 一 )
- 定义一个数组存储10个上面描述的小怪兽,每个小怪兽的名字为(小怪兽+数组下标)
- 部署卡巴斯基网络版6.0:安装控制中心和服务器端推送
- HTML如何引入md5算法,HTML_VBS MD5加密算法代码,复制代码 代码如下:Private Const - phpStudy...
- 陈经纶2021年高考成绩查询时间,最新丨2018人大附等28所北京学校中高考成绩一览...
- 读书笔记--项亮《推荐系统实践》第一章
- show master status 时没有数据显示
热门文章
- 数据合集、并集和差集的产生(seq、cat、sort和uniq的综合使用)
- 终于有一篇能让小白更容易理解GC算法的文章了
- 工业自动控制过程中最好用SCADA软件推荐
- java c2 compiler_C2中的CompilerThread
- jiegputo matlab转置,matlab实现用免疫克隆算法求二元函数的最优值(附源码)
- LVTTL转LVDS GM8285C,28位LVDS发送器,替代GM8283
- C# 生成图片清晰度问题(转载)
- 你以为SSL是安全的吗?
- 风险投资(VC)私募股权投资(PE)30问答
- 网络设计部分知识点总结