浅析C++的引用与const指针与各种传递方式
转自:https://www.jb51.net/article/120561.htm
首先我们知道 const int *p 与 int const *p 是一样的,即 *p 是常量;
而 int * const p 跟上面是不一样的,即 p 是常量;
我们知道引用只是一个别名,与变量共享存储空间,并且必须在定义的时候初始化,而且不能再成为别的变量的别名,这让我们想到什么呢,貌似跟 int * const p 的性质很像。
其实引用的底层就是用const指针来实现的。下面举个小例子:
#include <iostream> using namespace std; void swap(int &x, int &y) { int temp = x; x = y; y = temp; } void swap(int *const x, int *const y) { int temp = *x; *x = *y; *y = temp; } int main(void) { int a = 5; int b = 6; swap(a, b); cout << "a=" << a << " b=" << b << endl; int c = 7; int d = 8; swap(&c, &d); cout << "c=" << c << " d=" << d << endl; return 0; }
其实两个swap函数达到的效果是一样的(name mangling),而const 引用如 const int & 呢我们也可以类比为 const int * const p 即既不能成为别的变量的引用,也不能通过引用更改变量的值。
引用经常作为函数的参数传递,可以与值传递,以及指针传递做个比较:
值传递: 实参初始化形参时要分配空间, 将实参内容拷贝到形参
引用传递: 实参初始化形参时不分配空间
指针传递:本质是值传递,但如果我们要修改指针本身,那只能使用指针的指针了,即 **, 或者指针引用 *&
而且使用指针比较不保险的是很多人会忘记加上const的限制,即很可能接下来的程序中你又把这个指针指向了其他的变量,这样就混乱了。
把引用作为函数返回值时,千万记得不要返回局部变量的引用
举个小例子:
#include <iostream> using namespace std; int &add(int a, int b) { int sum; sum = a + b; return sum; } int main(void) { int n = add(3, 4); // cout<<"just test"<<endl; int &n2 = add(5, 6); cout << "n2=" << n2 << endl; cout << "n=" << n << endl; return 0; }
在上面的例子中我们返回了局部变量的引用,那么输出结果是什么呢?
n2=11 n=7
好像没错是吧,再试试,我们在最后加一条语句再打印一下 n2
cout<<"n2="<<n2<<endl; n2=11 n=7 n2=1474313670
奇怪了,为什么这次打印变成这么大的数而我们完全没更改n2的值啊? 见到的不一定是真的啊,不要被它欺骗了,这就是返回局部变量的引用的后果。
其实函数返回的是局部变量sum的引用,而 n2 本身又是引用,即引用着原来sum 拥有的那块区域,第一次打印没有出错是因为本来写在sum 区域上的值11 尚未被覆盖,而再运行两条打印语句后再次打印,很可能原来属于sum 的区域变 dirty了,被覆盖了其他不确定的值,每次打印都不会是一个定值。
那 n 呢,对 n 来说即使你最后再打印一下, n 还是等于 7,因为 n 本身是个变量,函数返回时立马保存了sum 所属区域的值, 除非你对 n 更改,不然 n 在main 函数堆栈中是不会变化的,直到函数退出, 变量释放。
大家要比较清晰的是,局部变量在函数栈上释放,但本来区域的值第一时间还是原来的值,但经过程序运行,堆栈内存区域重用, 一般就被覆盖了。
以上就是C++的引用与const指针与各种传递方式,如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
递归学习
说到递归,我心里真是有亿万只cnm...自己了解递归这个概念已经有一段时间了,但是一直灭有用过,今天说试试吧,结果写了个public void test(){}没然后了... 所以这一篇文章是自己总结一下递归的知识,总结完之后回过头来自己的感觉是:编写递归不要深入到每次递归,因为容易陷进递归中,而是做一个指导者,将你的思路告诉递归,而循环就不一样,你必须告诉循环,每一步需要做什么怎么做,总结完本篇文章耗费不少时间,但是还是没有那种豁然开朗的问题,应该是还需要多加思考练习吧 小故事 从前有座山,山上有座庙,庙里住着个老和尚,老和尚在跟小和尚讲故事,讲的是什么故事呢?:从前有座山,山上有座庙,庙里住着个老和尚,老和尚在跟小和尚讲故事,讲的是什么故事呢?...讲的是什么故事呢?:现在有个人在看我的文章(哈哈小时候最后一句可不是这个) 上面讲了一个大家都知道的小故事,山套山,庙套庙,那么递归其实这个故事是很像的,递归也是一层一层的结构,自己调用自己,递归的条件是必须有一个出口,那么就是上面的:现在有个人在看我的文章,如果没有这个出口,那么递归会引发程序错误,递归就好像这样的 看到上面的动图我们...
- 下一篇
贝叶斯分类算法实例 --根据姓名推测男女
一.从贝叶斯公式开始 贝叶斯分类其实是利用用贝叶斯公式,算出每种情况下发生的概率,再取概率较大的一个分类作为结果。我们先来看看贝叶斯公式: P(A|B) = P(B|A) P(A) / P(B) 其中P(A|B)是指在事件B发生的情况下事件A发生的概率。 在贝叶斯定理中,每个名词都有约定俗成的名称: P(A|B)是已知B发生后A的条件概率,也由于得自B的取值而被称作A的后验概率。 P(A)是A的先验概率(或边缘概率)。之所以称为"先验"是因为它不考虑任何B方面的因素。 P(B|A)是已知A发生后B的条件概率,也由于得自A的取值而被称作B的后验概率。 P(B)是B的先验概率或边缘概率。 这里可以用一个例子来说明这个公式。 看一个简单的小例子来展示贝叶斯定理 病人的例子:某个医院早上收了八个门诊病人,如下表。 症状 职业 疾病 打喷嚏 护士 感冒 打喷嚏 农夫 过敏 头痛 建筑工人 脑震荡 头痛 建筑工人 感冒 打喷嚏 建筑工人 过敏 打喷嚏 教师 感冒 头痛 教师 脑震荡 打喷嚏 教师 过敏 现在又来了第九个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大? 根据贝叶斯定...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Hadoop3单机部署,实现最简伪集群
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库