我转载了roony的一篇文章 C++的那些事:你真的了解引用吗
以备以后可以查到。

一、引用的本质是什么
  说到引用,一般C++的教材中都是这么定义的:

    1.引用就是一个对象的别名。

    2.引用不是值不占内存空间。

    3.引用必须在定义时赋值,将变量与引用绑定。

  那你有没有想过,上面的定义正确吗?编译器是如何解释引用的?

  这里先给出引用的本质定义,后面我们再进一步论证。

    1.引用实际是通过指针实现的。

    2.引用是一个常量指针。

    3.引用在内存中占4个字节。

    4.在对引用定义时,需要对这个常量指针初始化。

二、探究本质

  我们从最简单的变量的定义开始,看编译器会做哪些事情。

 int var = ;
mov dword ptr [var],2Ah // 对应汇编代码

  上面语句申请了一块内存空间,占4个字节,存放了一个int型的变量。内存里放的是42的二进制码。

  汇编代码向我们表达的意思就是把42写入以var为地址的内容区域。var有点像我们理解上的指针,只是编译器并没有把它抽象出来,而是让我们更表象的理解:申请一个变量,它的值为42。

  那么var这个变量名放在哪呢?

  我们知道程序如果访问内存里的数据,需要通过地址来进行访问,所以上面的代码在经过编译器生成目标代码时,用存放42的地址了所有的var,所以结论时,目标文件中不存在var,所以变量名本身是不占内存的。

  而我们知道,引用是变量的一个别名。那么,从这很多人会联想到,引用会不会也只是一个名字而已,编译器在生成目标代码的时候,会用实际地址替换引用呢?

  答案并非这样!

  那我们接下来看看,当我们定义一个引用时,发生了什么:

 int var = ;
01303AC8 mov dword ptr [var],2Ah int& refVar = var;
01303ACF lea eax,[var]
01303AD2 mov dword ptr [refVar],eax

  上面的代码显示,当定义一个引用时,编译器将var的地址赋给了以refVar为地址的一块内存区域。
  也就是说refVar其实存放的是var的地址。

  这让我们联想到了指针,那么我们看看定义一个指针是发生了什么:

 int var = ;
01213AC8 mov dword ptr [var],2Ah int* ptrVar = &var;
01213ACF lea eax,[var]
01213AD2 mov dword ptr [ptrVar],eax

  没错,没有任何差别,定义一个引用和一个指针的汇编代码完全一致!

三、const哪里去了
  相信从上面的分析时,你可能已经相信了,引用实际上就是一个指针。那么为什么说引用是一个常量指针呢,在目标代码里有什么体现呢?

  这个问题其实要从C++底层机制谈起,C++为我们提供的各种存取控制仅仅是在编译阶段给我们的限制,也就是说编译器确保了你在完成任务之前的正确行为,如果你的行为不正确,那么编译器就是给你在编译时提示错误。所谓的const和private等在实际的目标代码里根本不存在,所以在程序运行期间只要你愿意,你可以通过内存工具修改它的任何一个变量的值。

  这也就解释了为什么上面的两段代码中引用和指针的汇编代码完全一致。

  C++设计引用,并用常量指针来从编译器的角度实现它,目标是为了提供比指针更高的安全性,因为常量指针一旦与变量地址绑定将不能更改,这样降低了指针的危险系数,它提供了一种一对一的指针。

  但是你觉得使用引用就安全了吗?它同样会有与使用指针一样的问题

 int *var = new int();
int &ref = *var;
delete var;
ref = ;
return ;

  上面这段代码就很不安全,因为ref引用的内存区域不合法。

  为了进一步验证引用与指针在本质上的相同,我们看当引用作为函数参数传递时,编译器的行为:

 void Swap(int& v1, int& v2);
void Swap(int* v1, int* v2); int var1 = ;
00A64AF8 mov dword ptr [var1], int var2 = ;
00A64AFF mov dword ptr [var2], Swap(var1,var2);
00A64B06 lea eax,[var2]
00A64B09 push eax
00A64B0A lea ecx,[var1]
00A64B0D push ecx
00A64B0E call Swap (0A6141Fh)
00A64B13 add esp, Swap(&var1, &var2);
00A64B16 lea eax,[var2]
00A64B19 push eax
00A64B1A lea ecx,[var1]
00A64B1D push ecx
00A64B1E call Swap (0A61424h)
00A64B23 add esp,

上面代码再次证明了,引用与指针的行为完全一致,只是编译器在编译时对引用作了更严格的限制。

四、引用占多大的内存空间
因为在在表达式中,使用引用实际上就像使用变量本身一样,所以直接用sizeof是得不到引用本身的大小的。

 double var = 42.0;
double& ref = var; cout << sizeof var << endl; // print 8
cout << sizeof ref << endl; // print 8

我们可以通过定义一个只含有引用的类来解决这个问题:

 class refClass{
private:
double& ref;
public:
refClass(double var = 42.0) :ref(var){}
}; cout << sizeof refClass << endl; // print 4

所以结论就是引用和指针一样实际占内存空间4个字节。

文章来源:http://www.cnblogs.com/ronny/p/3662556.html

参考资料:http://www.cnblogs.com/rollenholt/articles/1907408.html

[转载] C++的那些事:你真的了解引用吗的更多相关文章

  1. C++的那些事:你真的了解引用吗

    一.引用的本质是什么 说到引用,一般C++的教材中都是这么定义的: 1,引用就是一个对象的别名. 2,引用不是值不占内存空间. 3,引用必须在定义时赋值,将变量与引用绑定. 那你有没有想过,上面的定义 ...

  2. [转载]Jmeter那点事·ForEach和If控制器

    如果我们要实现一个循环,如果城市是北京,则返回首都:否则,返回城市.   一.新建用户自定义变量 添加-配置元件-用户自定义变量, 定义变量注意命名格式:变量名 加 下划线 加 数字(从1开始计数) ...

  3. 新iPhone要推出双卡双待这事是真的吗?

    自2007年发布以来,iPhone似乎一直都是"异类"--以自己独特的方式走着一条引领智能手机前进的路!如,在当年遍地按键键盘的年代,iPhone以触摸屏的奇葩姿态引领了新潮流:刚 ...

  4. 【转载】C++ 11中的右值引用

    本篇随笔为转载,原博地址如下:http://www.cnblogs.com/TianFang/archive/2013/01/26/2878356.html 右值引用的功能 首先,我并不介绍什么是右值 ...

  5. 那些容易忽略的事4-(正则表达式反向引用\n)

    n 是一个正整数.一个反向引用(back reference),指向正则表达式中第 n 个括号(从左开始数)中匹配的子字符串. ps1:从左开始数,从左到优,也是从外到里,依次是\1,\2,\3... ...

  6. 【转载】 996是没前途的!996.ICU来了,回忆我对996的态度是如何从支持变成了怀疑!

    原文地址: https://www.meiqiantu.com/20138.html --------------------------------------------------------- ...

  7. 不加班的实践(1)——这真的该用try-catch吗?

    前言 我有个技能,就是把“我”说的听起来特别像“老子”. 以前是小喽啰的时候,会跟领导说“我!不加班.”,听起来就像“老子不加班!”一样.到最后发现,我确实没有把计划内的工作拖到需要加班才能完成,这个 ...

  8. 你确定你真的懂Nginx与PHP的交互?

    Nginx是俄国人最早开发的Webserver,现在已经风靡全球,相信大家并不陌生.PHP也通过二十多年的发展来到了7系列版本,更加关注性能.这对搭档在最近这些年,叱咤风云,基本上LNMP成了当下的标 ...

  9. 这真的该用try-catch吗?

    前言 我有个技能,就是把“我”说的听起来特别像“老子”. 以前是小喽啰的时候,会跟领导说“我!不加班.”,听起来就像“老子不加班!”一样.到最后发现,我确实没有把计划内的工作拖到需要加班才能完成,这个 ...

随机推荐

  1. 谈一谈chrome浏览器使用

    F3或Ctrl+F5: 查找本网页里面的内容,匹配到即可高亮. F6:回到地址栏 Ctrl+J:显示下载内容 Ctrl+N: 新建一个标签页 F10:选项 F11:显示全屏 F12:调试网页 大家有什 ...

  2. Linux安装 微信开发者工具(deepin linux ubt)

    一.环境:: deepin linux15.4.1 二.安装过程: 2.1 安装wine sudo apt-get install wine 2.2 安装nwjs-sdk 2.2.1 下载linux版 ...

  3. Windows下安装配置SBT

    1:安装包下载界面 http://www.scala-sbt.org/download.html 下载后进行安装. 安装路径:D:\Java\sbt\conf 2:进行配置 (1)sbtconfig. ...

  4. 用Python读取大文件

    通常我们在读取文件的时候,会用到read(), readline(), readlines(). 通常可能会有这样的用法: def test1(): with open("/tmp/test ...

  5. Android进程注入

    全部代码在这里下载:http://download.csdn.net/detail/a345017062/8133239 里面有两个exe.inj是一个C层进程注入的样例.inj_dalvik是我写的 ...

  6. python之模块csv之CSV文件的写入(基本结构)

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #CSV文件的写入(基本结构) import csv #csv文件,是一种常用的文本格式,用以存储表格数据,很 ...

  7. php-fpm 日志

    1.php-fpm 错误日志 #默认位置 安装目录下的 log/php-fpm.log error_log = log/php-fpm.log #错误级别 alert(必须立即处理), error(错 ...

  8. div最小高度的2种写法

    1.第一种写法: 原理:在IE6中,使用CSS定义div的高度的时候经常遇到这个问题,就是当div的最小高度小于一定的值以后,就会发现,无论你怎么设置最小高度,div的高度会固定在一个值不再发生变动, ...

  9. linux shutdown命令

    shutdown [-t 秒] [-arkhncfF] 时间 [警告信息] 常用选项与参数: -t sec : -t 后面加秒数,亦即『过几秒后关机』的意思 -k : 不要真的关机,只是发送警告信息出 ...

  10. 交叉编译OpenWrt 定制固件

    在Centos7上交叉编译生成OpenWrt固件 安装ss-* 获取最新的ss, 当前是 wget https://github.com/shadowsocks/shadowsocks-libev/a ...