序
几天前写过关于引用和指针的区别,当时心里就有一个疑惑,既然都说引用是对象的一个别名,那引用变量到底有没有被分配存储空间。我去Google了一下,发现网上有两种截然不同的看法,一种说是在栈里分配了空间,另一种则是说没有分配空间,仅仅是别名而已。经过几天的看书以及思考,我又有了新的体会,特写此篇。同时也感谢博文《c++引用深入探讨》给我的灵感
http://www.cnblogs.com/winter-cn/archive/2010/07/15/1777668.html
引用分配了栈空间
要想证明这一点,很自然的就想到了通过使用(void*)函数或是&取址符号来获得,遗憾的是无论哪一种方式返回的都是被引用对象的地址。那么引用变量地址是不是就是被引用的被引用对象的地址呢?
换一种思路,如果引用变量被分配了栈空间,那么很明显这会引起周围其他变量地址的偏移。如果能够找到这样的偏移,也间接地证明了引用变量被分配了栈空间。
void fa(){
int a[4];
cout<<a<<endl;
}
void fb(){
int a[4];
//int &b=a[0];
//注释掉后,fb内存空间会变小
cout<<a<<endl;
fa();
}
void fc(){
int a[4];
cout<<a<<endl;
fb();
}
定义了三个函数,分别是fa(),fb(),fc(),其中fb()被其他两个函数夹在中间。我们通过输出函数的地址,知道每个函数被分配的空间大小。在VS2012,win8.1 32位系统下,发现在注释掉下面这行代码后,
int &b=a[0];
函数空间少了近12字节,注意这个12字节,有没有感觉很奇怪,int型数据不是4字节吗,怎么多出了8字节?这有待我以后慢慢思索。
确定引用变量地址
虽然证明了引用变量也会被分配空间,但是没找到具体的地址空间还是缺少说服力,难以令人信服。
我们知道在c++中是不会对数组进行越界处理的,在数组下标越界时,通常情况下系统是不会报错,而是直接读取当前地址的值,通常是未知的,因此在码代码时要十分注意数组边界的问题。然而这次要利用C++的这个性质为我们服务。先上代码片段
int a = -1;
int b[4] = {0,1,2,3};
int c = 4;
cout<<&a<<endl;
cout<<b<<"\t"<<b[0]<<endl<<&b[1]<<"\t"<<b[1]<<endl<<&b[2]<<"\t"<<b[2]<<endl<<&b[3]<<"\t"<<b[3]<<endl;
cout<<&c<<endl;
cout<<b[-3]<<endl<<b[6]<<endl;
我们可以发现:
- 分配内存时,是从高位地址开始往低位地址分配的;
- 数组分配内存时,低位地址分配低下标数据,高位地址分配高下标数据。这里的高位地址不是形如高8位的意思。
- 虽然a和b数组是顺序声明的,但是之间隔着12个字节,多出了的8字节是什么呢!
接下来就是把上面的int型变量变成引用的过程了。
int ri = -1;
int ra[4] = {0,1,2,3};
int &c = ri;
int b = 2;
//c引用ri,且地址为ra[-3]
cout<<ra[-3]<<endl;
cout<<ra[-6]<<endl;
cout<<"修改ra[-3],使其指向ra[1]"<<endl;
ra[-3] = ra[1];
cout<<"修改ra[-3]后,ri值为:"<<ri<<endl;
ri = -100;
cout<<"修改原引用ri后,ra[-3]值为:"<<ra[-3]<<endl;
ra[1] = 100;
cout<<"继续修改ra[1]后,ra[-3]的值为:"<<ra[-3]<<endl;
输出的3537468即代表当前引用变量地址里存的数据,之所以加上b=2是为了确信ra[-3]里存放的就是引用变量。后面的对ra[-3]的一系列操作是为了确认能否通过修改引用变量地址存放的数据来修改对引用变量的重绑定操作。很遗憾,答案是否定的。
新的问题
到目前为止,我基本已经搞清了引用变量的地址以及大小,然而新的问题又涌现了出来。
1. 引用变量或是原对象的一些修改操作是如何实时地反映到原对象或是引用变量上的。
2. 如上面所示,输出的3537468代表了什么意思,很确信这不是一个地址,因为原对象地址与这个不同。
3. 之前提到的12字节问题,多出来的8字节到底是什么。