C++引⽤的本质与修改引⽤的⽅法
⽐较新的帖⼦。
包含引⽤在函数传参时的流程分析等。
===========================================================================================
本⽂不探讨罗列引⽤的概念,什么函数传参之类的,这些基础概念和⽤法很容易搜到~!
本⽂主要探讨引⽤和指针在C语⾔的下⼀层——即汇编或者确切的说是伪汇编(AT&T伪汇编都⼀样的代码,你指望下层x86汇编还能不⼀
样么~)——的实现过程,来摸索⼀下他们的特点与本质。
⾸先,引⽤(Reference)在C中没有,是C++ 才有的概念~! 要⽤g++编译器。
定义:引⽤就是某个⽬标变量的“别名”(alias)
在我看来,这个“⽬标变量”也要加上引号,看“⽬标变量”到底是怎么定义的了。如果“⽬标变量”由变量名和值组成,那引⽤应该是不
包含“变量名”这部分的,说⽩了,觉得他就是⼀个“新变量”,只是他和原变量的“值”(即,⽬标地址,存储内容)是共⽤的。
实例测试:
⽤g++编译,gdb调试:
可以看到,让refa引⽤a的过程,其实就是提取地址(lea),并且占⽤了栈空间。和指针的实现是⼀模⼀样的。不管你“理论上”怎么说,⾄少在实现上(⾄少在linux的实现上),他就是指针。
可以看到,操作都是直接或者间接的对a的原地址0x10(%esp)进⾏操作,这个没什么问题。但是说引⽤不占地址是错误的,作为⼀
个“指针”他⾄少占⽤4字节吧~!
这是代码后续的赋值操作:
Breakpoint 2, main () at ref2.cpp:13
13 a= 2;
1: x/i $pc
=> 0x804868d <main()+153>: movl $0x2,0x10(%esp)
Breakpoint 3, main () at ref2.cpp:18
18 refa= 3;
1: x/i $pc
=> 0x8048705 <main()+273>: mov 0x14(%esp),%eax
(gdb) si
0x08048709 18 refa = 3;
1: x/i $pc
=> 0x8048709 <main()+277>: movl $0x3,(%eax)
22 *ptra= 3;
1: x/i $pc
=> 0x804877f <main()+395>: mov 0x18(%esp),%eax
(gdb) si
0x08048783 22 *ptra = 3;
1: x/i $pc
=> 0x8048783 <main()+399>: movl $0x3,(%eax)
可以看到引⽤和指针,从定义到赋值,实现都是⼀样的。
虽然引⽤和 指针的意义,认为差不多,但是使⽤⽅法还是有差别的,想获得右值,引⽤直接⽤变量名, 指针要加*操作符。⽽对引⽤使⽤*操作是不允许的。
另外,不同于指针,引⽤在声明的时候必须初始化,
但引⽤可能只能⼀次初始化⽽不能改变引⽤“⽬标”吗?
⾄少通过如下⽅法是不能的:
int a = 1;
int b = 2;
int &refa = a;
refa = b;
这相当于赋值为b,即赋值为2,连a的值都会变成2.
&refa = &b;
也是不可能的,因为&refa不是左值。
refa = &b;
更不对了,因为这也相当于赋值,不过不是2了,是b的地址(打印成10进制,类似于-1075934160这种),并且,需要强制转换:
refa = (int)&b;
说再多都是YY,实践出真知~!
围绕我的”引⽤即指针“的理念,再做⼀个摸索,既然认为引⽤是指针了,那么sizeof任何指针,包括double的,肯定都是4(我的32位机)。我定义⼀个double的引⽤,看看sizeof结果如何(右侧为输出结果):
这个结果倒是没夸张到直接让ref变成pointer。sizeof(refd)还是按普通的double来算⼤⼩,⽽不是直接按指针来算的。但是也情有可原吧,都说了,虽然他的底层实现和指针⼀样,但是sizeof()需要的是返回类型,它的返回类型——即”操作级别“,还是⽐指针要低的。
sizeof 指针其实我更倾向于这是编译器的优化,碰到sizeof()使⽤引⽤当操作数的,直接转换成原类型来传递了。(sizeof是操作符不是函数)
最后:到底怎样理解引⽤更好?
⾸先,不太同意“引⽤就是⼀个别名,不占⽤内存空间“的说法,⾄少这句话可以再严谨点——”引⽤不为值再去开辟⼀个地址空间,但是其本⾝要占⽤空间~!“
奇了怪了,引⽤确实占⽤栈空间,也确实是存储了⽬标变量的地址~~~那既然有空间,就应该和指针⼀样,我改变你的值不就等于改变你的指向了么?
但是,因为它和指针不在同⼀个“操作级别”上,它的”值“⼜不是地址,也不能像指针那样改变他的指向。
(“操作级别”是通过存储内容来判定的,⽐如普通变量的存储内容是“值”,⽽指针的存储内容是“地址”,可以通过指针独特
的“*”操作来判断这个“级别”)
个⼈倾向于认为引⽤本⾝就是⼀种指针,⾄于他⼜不能像指针⼀样进⾏重定向等操作,觉得这些完全是语⾔级别或者说编译器的刻意限制,只是⼀种规则,没有其他原因。
再次怀疑⼈⽣——编译器的本质如何?到底什么叫做编程语⾔?各层语⾔界限如何?从这么多的实践操作经验来总结,似乎也逐渐理解了些,如果再去看看《编译原理》,或许会有所收获。
本⽂不探讨罗列引⽤的概念,什么函数传参之类的,这些基础概念和⽤法很容易搜到~!
本⽂主要探讨引⽤和指针在C语⾔的下⼀层——即汇编或者确切的说是伪汇编(AT&T伪汇编都⼀样的代码,你指望下层x86汇编还能不⼀样么~)——的实现过程,来摸索⼀下他们的特点与本质。
⾸先,引⽤(Reference)在C中没有,是C++ 才有的概念~! 要⽤g++编译器。
定义:引⽤就是某个⽬标变量的“别名”(alias)
在我看来,这个“⽬标变量”也要加上引号,看“⽬标变量”到底是怎么定义的了。如果“⽬标变量”由变量名和值组成,那引⽤应该是不包含“变量名”这部分的,说⽩了,觉得他就是⼀个“新变量”,只是他和原变量的“值”(即,⽬标地址,存储内容)是共⽤的。
实例测试:
⽤g++编译,gdb调试:
可以看到,让refa引⽤a的过程,其实就是提取地址(lea),并且占⽤了栈空间。和指针的实现是⼀模⼀样的。不管你“理论上”怎么说,⾄少在实现上(⾄少在linux的实现上),他就是指针。
可以看到,操作都是直接或者间接的对a的原地址0x10(%esp)进⾏操作,这个没什么问题。但是说
引⽤不占地址是错误的,作为⼀
个“指针”他⾄少占⽤4字节吧~!
这是代码后续的赋值操作:
Breakpoint 2, main () at ref2.cpp:13
13 a= 2;
1: x/i $pc
=> 0x804868d <main()+153>: movl $0x2,0x10(%esp)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论