关于“C++中引用不占内存”问题的进一步解释

存储 存储软件
本问题的关键是一个视角问题。同一个问题,站在C++程序员的角度与站在编译器开发者的角度来看是不一样的。

本问题的关键是一个视角问题。同一个问题,站在C++程序员的角度与站在编译器开发者的角度来看是不一样的。

在C++中我们说“引用不占内存”是从C++程序员的角度来看。请看下列程序的运行结果。

  1. #include <iostream> 
  2. using namespace std; 
  3. int main(int argc, char** argv) 
  4.       char c; 
  5.       double i; 
  6.       double &k = i; 
  7.       char   &m = c; 
  8.       double *p = &i; 
  9.       char   *q = &c; 
  10.       cout << "Result :" << endl; 
  11.       cout << " i = " << sizeof(i) << endl; 
  12.       cout << " k = " << sizeof(k) << endl; 
  13.       cout << " c = " << sizeof(c) << endl; 
  14.       cout << " m = " << sizeof(m) << endl; 
  15.       cout << " p = " << sizeof(p) << endl; 
  16.       cout << " q = " << sizeof(q) << endl; 
  17.       return 0; 

运行结果为:

  1. Result : 
  2.  i = 8 
  3.  k = 8 
  4.  c = 1 
  5.  m = 1 
  6.  p = 4 
  7.  q = 4 

显然,对于引用变量k来说,sizeof(k)的结果为8,sizeof(m)的结果为1,这样的结果实际是被引用对象所占内存的大小,k引用的是double类型,所以占8个字节;m引用的是char类型,所以占1个字节。这与指针变量完全不同,从运行结果可以看出,不论指针变量指向的数据类型是什么,指针变量(p、q)均占用4个字节。进一步可以用C++程序证明,对于引用变量的处理直接操作的对象就是被引用对象。所以从C++的角度来说,引用本身不会为变量开辟新的存储空间,引用只是为实际对象起了一个别名。

C++程序不能在计算机上直接运行,必须经过编译器将C++程序编译生成汇编程序,从编译的角度来看,C++编译器对引用的处理与对于指针的处理是相同的,均是为变量分配一个对应的内存空间。所以,站在编译器开发者的角度来看,在编译器中要实现引用就必须要为引用变量分配一个内存空间。

所以两个视角不能混淆。

为了更深入解释,我们可以在DEV C++环境中运行下列C++程序。

  1. #include <iostream> 
  2. using namespace std; 
  3. int main(int argc, char** argv) 
  4.        int i = 5; 
  5.        int j = 6; 
  6.        int &k = i; 
  7.        int *p = &i; 
  8.        i = j; 
  9.        k = j; 
  10.        i = k; 
  11.        &k = j;  // 编译错误,无法通过 
  12.        cout << "运行结果:" << endl; 
  13.        cout << " i = " << i << endl; 
  14.        cout << " &i = " << &i << endl; 
  15.        cout << " j = " << j << endl; 
  16.        cout << " k = " << k << endl; 
  17.        cout << " &k = " << &k << endl; 
  18.        cout << " p = " << p << endl; 
  19.        cout << " &p = " << &p << endl; 
  20.        return 0; 

运行结果:

  1. i = 6  
  2. &i = 0x22fe98  
  3. j = 6  
  4. &j = 0x22fe94  
  5. k = 6  
  6. &k = 0x22fe98  
  7. p = 0x22fe98  
  8. &p = 0x22fe90 

上述C++程序对应的汇编代码如下:

  1. 0x00401576 <+86>: mov DWORD PTR [ebp-0x20],0x5 

; 语句i = 5。ebp=0x22feb8

  1. 0x0040157d <+93>: mov DWORD PTR [ebp-0x24],0x6 

; 语句j = 6

  1. 0x00401584 <+100>:lea eax,[ebp-0x20] 

; 语句&k = i。eax=0x22fe98

  1. 0x00401587 <+103>:mov DWORD PTR [ebp-0x1c],eax 
  2. 0x0040158a <+106>: lea eax,[ebp-0x20] 

; 语句p = &i。eax=0x22fe98

  1. 0x0040158d <+109>:mov DWORD PTR [ebp-0x28],eax 
  2. 0x00401590 <+112>:mov eax,DWORD PTR [ebp-0x24] 

; 语句i = j。eax=0x6

  1. 0x00401593 <+115>:mov DWORD PTR [ebp-0x20],eax 
  2. 0x00401596 <+118>:mov edx,DWORD PTR [ebp-0x24] 

; 语句k = j。

  1. 0x00401599 <+121>:mov eax,DWORD PTR [ebp-0x1c] 
  2. 0x0040159c <+124>: mov DWORD PTR [eax],edx 
  3. 0x0040159e <+126>: mov eax,DWORD PTR [ebp-0x1c] 

; 语句i = k。

  1. 0x004015a1 <+129>: mov eax,DWORD PTR [eax] 
  2. 0x004015a3 <+131>: mov DWORD PTR [ebp-0x20],eax 
  3. 0x004015a6 <+134>: mov eax,DWORD PTR [ebp-0x28] 

; 语句*p = j。

  1. 0x004015a9 <+137>: mov edx,DWORD PTR [ebp-0x24]  
  2. 0x004015ac <+140>: mov DWORD PTR [eax],edx 
  3. 0x004015ae <+142>: mov eax,DWORD PTR [ebp-0x20] 

; 下一条语句

  1. 0x004015b1 <+145>: …… 

通过分析可以看到,整型变量i在内存中分配的绝对地址为0x22fe98,相对地址为ebp-0x20;整型变量j的绝对地址为0x22fe94,相对地址为ebp-0x24;指针变量p的绝对地址为0x22fe90,相对地址为ebp-0x28;引用变量k在内存中相对地址为ebp-0x1c。注意:在C++程序员的视角中是无法得到引用变量k在内存中的地址。变量在内存中地址分配关系见下表。

将C++程序与对应的汇编指令相对照。语句“&k = i”对应的汇编语句是<+100>和<+103>,编译为引用变量k分配了内存单元,且保存的是变量i的地址,语句“p = &i”的汇编语句是<+106>和<+109>,编译为指针变量p分配了内存单元,且保存的是变量i的地址,两个语句对应的汇编是一样的,都是在变量对应的单元中保存了相关对象的地址。

语句“k = j”对应的汇编语句是<+118>、<+121>和<+124>,语句“*p = j” 对应的汇编语句是<+134>、<+137>和<+140>,两相对照,生成的汇编指令没有本质区别,可以认为是完全等价的。因此可以得出结论,通过编译之后,生成的最终代码在汇编级对于引用变量和指针变量的处理是相同的。

在C++的视角来看,引用变量与指针变量是不同的。指针变量是一个实体,在运行过程中可以改变;而引用仅是某个变量的别名,引用变量在被创建的同时必须被初始化,且在运行过程中不能被再次改变。

在我们给出的上述源程序示例中,语句“&k = j;”是无法通过编译。因此可以认为,C++语言中对于引用型变量的限制是由编译器本身限制的。

责任编辑:武晓燕 来源: 开点工作室
相关推荐

2009-08-26 14:48:05

C#委托与事件

2011-07-27 12:58:43

Android MarAndroid应用商店

2009-12-13 15:23:36

2009-12-09 11:13:34

Sc_Visio_En

2023-09-01 18:20:43

Chrome代码测试版

2019-03-22 10:20:39

加速Windows 10启动

2011-07-29 15:02:22

LifeSize视频协作

2020-12-10 20:00:04

数字货币比特币区块链

2009-11-30 18:35:05

BizSparkDreamSparkWebSiteSpar

2014-01-08 10:22:28

思科Videoscape

2015-10-19 14:57:51

2010-03-15 09:40:19

Windows 8研发

2009-12-28 10:08:07

OracleSQLDevelope开发框架

2012-04-30 21:35:08

Windows Pho

2020-09-22 10:49:12

大数据旅游技术

2013-06-17 11:53:49

思科云服务思科

2021-04-05 18:06:36

谷歌安卓Google Play

2009-03-31 11:12:59

万兆以太网

2010-04-14 16:11:09

Oracle服务器

2015-11-13 16:27:13

ivvi
点赞
收藏

51CTO技术栈公众号