奶奶都能看懂的 C++ —— 手把手指针
引用
在正式介绍指针之前,先来看看什么是引用。
int a = 10;
int &ref1 = a;
你可能注意到了,上面的代码里有个 &。这就是我们的主角,引用。在变量名之前加上该符号,就可以指出它是个引用。
我们常说的引用,就是把别人的东西拿过来自己用。C++ 的引用也是如此,就是把另外一个对象拿过来用,然后起个名字。也就是说:
// a = 10
ref1 = 11;
// 现在,a = 11
对象就像瓶子,引用就是瓶子上面的标签。访问引用时,就是找到标签所对应的瓶子。
引用必须满足以下条件:
- 引用指向的是一个对象,而不是值
- 引用类型和它指向的对象匹配
- 引用必须在声明时初始化
- 引用初始化后不能更改绑定的对象
要注意的是,引用必须在声明时初始化。下面代码会产生编译错误:
int &ref2; // Error!
另外要注意的一点是,可以一次声明多个引用,但都要加上 &。
int &ref1=a, ref2=a;// ref1 是引用,ref2 则是 a 值的拷贝
int &ref1=a, &ref2=a;// 都是引用
实际上,把 & 和类型名称放一起也是可行的,但是考虑到上面这个一次声明多个的问题,我还是建议和变量名放一起,否则有歧义。
指针
好好好,现在我们来到了正题。
先把上面的引用忘了,我们到最后再来讲指针和引用的差别。
创建指针
int a = 10;
int *p;
p = &a;
这里又有 * 又有 &,看晕了都。所以我把它拆成了三行,我们一行一行来。
首先,第二行,有个星号。这就是我们的主角,指针。* 表示创建的是指针。这一行声明了一个 int 类型的指针,但是并没有初始化。
第三行,把指针 p 指向 a 的地址。你肯定注意到这里有个老熟人 &。当然啦,我让你先把引用忘了是有原因的,因为这里的 & 和上面引用那里的完全不是一个东西。
这里的 & 叫做 取地址符。它和一个变量一起用可以返回那个变量的地址。各位都知道你的内存很大,位置很多,取地址符就是用于查找变量的位置的。
Warning! 这里不初始化指针拆成两行的方法是不推荐的,因为未初始化的指针行为未知。实际请务必初始化!
既然得到了位置,我们自然就知道指针的用法了——“一个指针对应一个对象的位置”。
注意:
- 引用不是对象,没有地址
- 指针自己是对象,所以可以用指针指向指针。这个后面再说。
ohhhhhhhh 恭喜你,你已经明白了怎么创建指针,接下来就用一下吧。
用指针
cout << *p;
// a = 10, output: 10
*p = 20;
cout << *p;
// a = 20, output: 20
嗯,现在熟悉的东西又来了。我们在创建指针的时候已经用了星号了,现在访问时又出现了。
或许你已经猜到了。很遗憾,这里的星号和前面的含义也截然不同。* 叫解引用符(别看名字,它和引用没半毛钱关系),用于从某个地址获取其对应的对象。
啥意思?我们的变量对象在内存里,& 找到了对象的位置用指针存起来,然后想要用的时候,再用 * 根据位置找到对象。
哎,回到上面的三行代码。1、4 行输出了对象,3 行则改变了对象的值。我们可以看到,由于根据位置找到的对象还是 a,所以 a 的值也发生了变化。
int b =30;
p = &b;
我们先前提到指针是对象,所以它本身也可以改变。
你可以用其它对象的地址重新赋值给指针,就像上面一样。这样指针就指向其它对象了。
再次恭喜你,你现在已经明白了怎么用指针了。接下来再介绍点特殊的指针。
在继续之前……
再强调一下,* & 两个符号存在多重含义。
*:
- 在声明变量时,在变量前,声明它是个指针
- 在使用变量时,在变量前,是通过地址找对象(解引用符)
&:
- 在声明变量时,在变量前,声明它是个引用
- 在使用变量时,在变量前,是根据对象找地址
也就是说:
声明前面是类型,其它时候在寻找。指针配上找对象,引用配上找地址。
空指针
int *p = nullptr;
int *p1 = 0;
我的天哪,这两个指针并没有指向某个对象的地址!会不会报错啊!
其实并不会,它们叫做空指针。顾名思义,就是空的指针。空指针什么都不指向。就是个指针而已,空的,用不了。通常你没理由这么干,除非你真的暂时不知道该指向什么,以后再指。这样你用的时候就可以检查指针是否有指向东西(是否为空),而不是未初始化指针的未知行为。
if(p){
...
}
if(p1){
...
}
如果指针是空的,那么它在 if 里相当于 false。所以可以像上面那样检查指针是否为空。
Warning! 未初始化和空指针不是一个东西。未初始化的指针的行为是未知的,不能这样检验。所以确保初始化。
指针的嵌套
前面提到了指针是对象,也就是说指针也有地址,也就是自己的位置。那么我们就可以套娃了,cpp 允许你嵌套,比如指向指针的指针。
int a = 10;
int *p1 = &a;
int **p2 = &p1;
cout<<*p2<<endl;
cout<<**p2<<endl;
cout<<*p1<<endl;
先想想取地址符和解引用符的作用,想想上面代码的输出是什么。
示例输出:
0x7ffe065143d4
10
10
嗯,你的输出第一行肯定和我不一样,且每次运行的输出肯定不一样。
如果你学过点底层知识,一定能看出来第一行是个十六进制数。没错它就是个地址。
为什么会有这样的结果呢?
通过图片解析下你就明白了(第一行变量名,第二行变量的值,第三行变量的地址。注意 p2 p1 地址未知,是假设的)

可以看到,*p2 实际上指的是 p1,而它的值则是 a 的地址。而 **p2 才指的是 a 本身。也就是说,解引用一次,就找一次地址对应的对象。要想获得 a,则必须解引用两次。
再再再恭喜你一下,你已经完全明白了指针的简单使用。
指针和引用
通过上面的讲解,我们不难得出结论:
- 指针是对象
- 引用不是对象
- 指针、引用可以指向的是对象
- (推论)指针可以指向指针
所以显然可以推出:
- 引用可以指向指针
- 指针不能指向引用
引用只是给对象贴了标签(起别名)而已。而指针则是创建了另一个对象来存储对象的位置。在这个过程中,最重要的是分清 & 和 * 到底是在声明类型,还是作为取地址和解引用运算符。
嗯,够清晰,够明白。
下一篇,我们将进一步探索 const 限定,了解什么是指针常量、指向常量的指针。依旧是奶奶级,拆碎了给你看。
奶奶都能看懂的 C++ —— 手把手指针的更多相关文章
- 小学生都能看懂的FFT!!!
小学生都能看懂的FFT!!! 前言 在创新实践重心偷偷看了一天FFT资料后,我终于看懂了一点.为了给大家提供一份简单易懂的学习资料,同时也方便自己以后复习,我决定动手写这份学习笔记. 食用指南: 本篇 ...
- 机器学习敲门砖:任何人都能看懂的TensorFlow介绍
机器学习敲门砖:任何人都能看懂的TensorFlow介绍 http://www.jiqizhixin.com/article/1440
- 只要听说过电脑的人都能看懂的网上pdf全书获取项目
作者:周奇 最近我要获取<概统>的教材自学防挂科(线代已死),于是我看到 htt链ps:/链/max链.book接118接.com接/html/2018/0407/160495927.sh ...
- 55张图吃透Nacos,妹子都能看懂!
大家好,我是不才陈某~ 这是<Spring Cloud 进阶>第1篇文章,往期文章如下: 五十五张图告诉你微服务的灵魂摆渡者Nacos究竟有多强? openFeign夺命连环9问,这谁受得 ...
- 搭建分布式事务组件 seata 的Server 端和Client 端详解(小白都能看懂)
一,server 端的存储模式为:Server 端 存 储 模 式 (store-mode) 支 持 三 种 : file: ( 默 认 ) 单 机 模 式 , 全 局 事 务 会 话 信 息 内 存 ...
- 小学生都能看懂的数位dp
前言 数位dp其实很久前就知道了,也做过几道和其他算法混在一起的题目,其实通过手玩是能做的 但毕竟是种算法,还是系统学下比较好(节省手玩时间) 模板题 P2602 [ZJOI2010]数字计数 化简题 ...
- 小白都能看懂的tcp三次握手
众所周知,TCP在建立连接时需要经过三次握手.许多初学者经常对这个过程感到混乱:SYN是干什么的,怎么一会儿是1一会儿是0?怎么既有大写的ACK又有小写的ack?为什么ACK在第二次握手才开始出现?初 ...
- 人人都能看懂的卡西欧fx991cnx玩机指南,手把手教你如何利用计算器的漏洞爆机
专业术语说明 你是VerB还是VerC 别人问你这个问题的时候不要慌,帮你看你的计算器是Ver几: 同时按住shift.7.开机键 9 5次shift 第一行后半句即是 紧接着可以顺便看看计算器的序列 ...
- 外行人都能看懂的WebFlux,错过了血亏!
前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 本文知识点架构: 如果有关注我公众号文章的同学就会发 ...
- 小白都能看懂的Spring源码揭秘之IOC容器源码分析
目录 前言 IOC 只是一个 Map 集合 IOC 三大核心接口 IOC 初始化三大步骤 定位 加载 注册 总结 前言 在 Spring 框架中,大家耳熟能详的无非就是 IOC,DI,Spring M ...
随机推荐
- ETL中Python组件的运用
Python是一种高级.通用.解释型编程语言,以简洁.易读.易学的语法而闻名,被广泛应用于Web开发.数据科学.人工智能.自动化脚本等领域. python的特点包含 易读易学:Python的语法设计简 ...
- 解锁ETLCloud中Kettle的用法
随着大数据时代的到来,数据的处理和管理成为各行各业不可或缺的一环.ETL(Extract-Transform-Load)工具作为数据处理的重要环节,扮演着将数据从源端抽取出来.经过转换处理,最终加载至 ...
- Infinity: Set Theory is the true study of Infinity
AN INTRODUCTION TO SET THEORY - Professor William A. R. Weiss, October 2, 2008 Infinity -> Set Th ...
- 「GIS数据」下载全国的GeoJSON、shp格式数据(精确到乡镇街道级)-2024年12月更新
发现个可以免费下载全国 geojson 数据的网站,推荐一下.支持全国.省级.市级.区/县级.街道/乡镇级以及各级的联动数据,支持导入矢量地图渲染框架中使用,例如:D3.Echarts等 geojso ...
- C语言经典例程100例
[程序1] 题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? 1.程序分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去掉不满足条件的排列. ...
- vmvare&vsphere制作虚拟机模板
VMware/vSphere中新安装好的虚拟机把以下两个文件添加注释后,即可转换为虚拟机模板添加注释[root]# cat /etc/udev/rules.d/70-persistent-net.ru ...
- OAuth2.0系列之使用JWT令牌实践教程(八)
@ 目录 1.文章前言介绍 2.例子实验验证 3.功能简单测试 OAuth2.0系列博客: OAuth2.0系列之基本概念和运作流程(一) OAuth2.0系列之授权码模式实践教程(二) OAuth2 ...
- 协同开发、冲突的出现与解决、分支合并出现冲突、线上合并分支、pycharm操作git、git面试题
协同开发 第一步:你到公司(gitee,gitlab),注册账号 第二步:把你的公钥配置在你的gitee,gitlab账号上 第三步:把你的邮箱发给项目的管理者(你老大) 第四步:他把你账号添加为开发 ...
- .net 8.0框架下splitContainer在不断点击放大、缩小情况下分割线越来越粗问题解决
关于其他方面可参考前文:https://www.cnblogs.com/Jesuslovesme/p/18623422此文想说明的问题是:之前在.net framework 4.7.2运行一切正常的项 ...
- JavaScript代码安全性提升:选择和使用JS混淆工具的指南
https://toolin.cn/jsfuck https://maimai.cn/article/detail?fid=1827257627&efid=382Pa05uQ_i7jAl6rm ...