蓝布编程网

分享编程技术文章,编程语言教程与实战经验

CPU眼里的:引用

看似相似的指针与引用,其实承载着截然不同的设计哲学。掌握它们,才能真正理解程序与内存的对话


01 提出问题

你有没有发现有些语法规则,它们的作用看上去非常相似,但在编写起来又大相径庭的情况呢?其中最典型的例子,或许就是:引用和指针了。引用和指针到底有什么差异?这可能是一个在语法层面,颇难解释清楚的问题,但在CPU眼里,却根本就不是一个问题,因为它们几乎没有任何区别。


02 代码分析

话不多说,打开Compiler Explorer,让我们编写一个简单的函数func1,定义一个指针变量p,用来改变变量a的值;然后我们再编写一个相似的引用版本的函数func2,如图所示。

老规矩,不要理会每条CPU指令的具体含义,我们只比较两个函数对应的CPU指令差异,如你所见,它们完全相同!

我们定义的引用变量r,实际上是在定义一个指向变量a的指针p;我们对引用变量r的读、写操作,实际上是指针变量p的*读、*写操作。

或许,你还不能接受这个现实,没有关系。让我们再写一个简单的传指针的函数func3;再写一个传引用的函数func4;最后作一个call函数的调用,如图所示。

如你所见,不仅两个函数func3、func4的函数体,而且它们的调用部分,对应的CPU指令都完全相同!

所以,跟有指针参数的函数func3一样,在函数体func4里面,改变变量r的值,一样会影响到函数外变量a的值。至于指针参数,是如何改变函数外变量a的值?可以参看“CPU眼里的参数传递”中的详细讲解。

至此,结论已经非常明显了,在CPU眼里,指针和我们常见的“左值引用”几乎没有任何区别。“左值引用”可以做到的事情,用指针都可以做到。如果非要说说它们的区别的话,我想主要集中在下面这些语法规则的层面:


  1. 引用显得更加简洁,特别是在读、写的时候,不需要像指针那样,加上*号操作。
  2. 指针可以被赋值成NULL:int*p = NULL,但引用不行:int &r = NULL。
  3. 指针可以随时改变它所指向的变量;而引用不能随意改变它所引用的变量,否则,会被视为重新定义了一个已经存在的引用变量。
  4. 指针存在“指针的指针”;而引用则不存在“引用的引用”。


03 总结

1. “引用变量”也是变量,在底层实现上面,跟“指针变量”完全相同。

2. “引用变量”也被称为某个变量的别名,这非常形象。但似乎很难解释为什么在函数func4中改变r的值,会同时改变外部变量a的值。但如果你把“引用”当作“指针”看待的话,这个问题就迎刃而解了。


04 热点问题

Q1:C语言也支持:“引用”这个语法规则吗?

A1: 不支持的,引用这个语法规则,是在C++才被支持的。但如你所见,所有的引用,都可以通过指针来达到相同的效果;但引用在使用起来,会简洁不少,更像是一个语法糖。


Q2:引用的本质是“指针常量”吗?例如:int* const p = &a

A2:非常精彩的总结!二者确实有许多相似之处,但它们在语法上也存在一些关键差异。例如,引用不能初始化为nullptr,也不支持间接引用(即 * 操作)。下面是引用与各种指针之间的对比,供大家参考和比较:

特性

引用

指针常量

需要初始化

是否可更改指向的对象

否,绑定后不可变

否,指针本身不能改指向,即指针变量的值不可写

是否可以为nullptr

不可以

可以

是否支持间接引用(*操作)

不可以

可以

类型

语法

可修改指向的值?

可修改指向的地址?(等价于:是否可重写指针变量的值?)

普通指针

int *p

可以

可以

指针常量

int *const p

可以

不行

常量指针

const int *p

不行

可以

常量指针常量

const int *const p

不行

不行


Q3:“引用”、“直接引用”、“间接引用”的区别是什么?

A3:很好的问题!由于翻译的原因,计算机中经常出现一些一词多义的情况。“引用”在被当作“变量”使用的时候,引用是一个名词,表示一个变量;当“引用”用来作读、写数据的时候,它是一个动词,对于普通变量,往往可以直接读、写,也叫“直接引用”;但对于指针变量,读、写指针所指向的内存时,往往使用“间接引用”的读、写方式,也就是 * 操作。


Q4:C++里面还有“右值引用”、“万能引用”、“引用折叠”,它们也能用指针来解释吗?

A4: 非常好的问题!用本文提供的方法,你会发现“右值引用”,在底层实现上,跟“左值引用”和指针,也非常相似。同样的方法,你也可以分析出:“万能引用”、“引用折叠”的底层实现。

当然,穷举所有的C++语法规则,并不是本书的特点。除杂去冗,化繁为简,才是本书的意义所在。

C++的语法规则复杂、繁琐,而且还在不断变化、扩展;但底层实现,则相对简单、统一。就像全球有上亿个不同功能的网站,但后台可能都在做一类事情:增、删、改、查。

今天在LLVM的支持下,我们很容易创造出一种新的编程语言或语法规则,但底层的机器汇编部分,可能完全不用调整。

我相信未来还会有更多的语言诞生、C++还会涌现更多的语法规则,但只要我们理解底层的实现逻辑,“眼中有代码,心中有指令”,就能快速领悟语法精髓,以不变应万变。


05 更多知识

如果喜欢阿布这种解读方式,希望更加系统学习这些编程知识的话,也可以考虑看看由阿布亲自编写,并由多位微软大佬联袂推荐的新书《CPU眼里的C/C++》

心中藏奥义,指下写乾坤!

<script type="text/javascript" src="//mp.toutiao.com/mp/agw/mass_profit/pc_product_promotions_js?item_id=7514888708865196596"></script>
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言