`

C++引用的本质 --另一种指针

    博客分类:
  • C++
c++ 
阅读更多

文章来源:http://www.itnose.net/detail/6033934.html 更多文章:http://www.itnose.net/type/55.html

本文不探讨罗列引用的概念,什么函数传参之类的,这些基础概念和用法很容易搜到~!

本文主要探讨引用和指针在C语言的下一层??即汇编或者确切的说是伪汇编(AT&T伪汇编都一样的代码,你指望下层x86汇编还能不一样么~)??的实现过程,来摸索一下他们的特点与本质。

 

首先,引用(Reference)在C中没有,是C++ 才有的概念~! 要用g++编译器。


定义:引用就是某个目标变量的“别名”(alias)

 

在我看来,这个“目标变量”也要加上引号,看“目标变量”到底是怎么定义的了。如果“目标变量”由变量名和值组成,那引用应该是不包含“变量名”这部分的,说白了,觉得他就是一个“新变量”,只是他和原变量的“值”(即,目标地址,存储内容)是共用的。

 

 

实例测试:

 

 引用,另一种,c++,指针,本质0

 

用g++编译,gdb调试:

 引用,另一种,c++,指针,本质1

 

可以看到,让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结果如何(右侧为输出结果):

引用,另一种,c++,指针,本质2

这个结果倒是没夸张到直接让ref变成pointer。sizeof(refd)还是按普通的double来算大小,而不是直接按指针来算的。但是也情有可原吧,都说了,虽然他的底层实现和指针一样,但是sizeof()需要的是返回类型,它的返回类型??即”操作级别“,还是比指针要低的。

 

最后:到底怎样理解引用更好?

首先,不太同意“引用就是一个别名,不占用内存空间“的说法,至少这句话可以再严谨点??”引用不为值再去开辟一个地址空间,但是其本身要占用空间~!“

 

奇了怪了,引用确实占用栈空间,也确实是存储了目标变量的地址~~~那既然有空间,就应该和指针一样,我改变你的值不就等于改变你的指向了么?

但是,因为它和指针不在同一个“操作级别”上,它的”值“又不是地址,也不能像指针那样改变他的指向。

(“操作级别”是通过存储内容来判定的,比如普通变量的存储内容是“值”,而指针的存储内容是“地址”,可以通过指针独特的“*”操作来判断这个“级别”)

 

个人倾向于认为引用本身就是一种指针,至于他又不能像指针一样进行重定向等操作,觉得这些完全是语言级别或者说编译器的刻意限制,只是一种规则,没有其他原因。

再次怀疑人生??编译器的本质如何?到底什么叫做编程语言?各层语言界限如何?从这么多的实践操作经验来总结,似乎也逐渐理解了些,如果再去看看《编译原理》,或许会有所收获。

 

本文不探讨罗列引用的概念,什么函数传参之类的,这些基础概念和用法很容易搜到~!

本文主要探讨引用和指针在C语言的下一层??即汇编或者确切的说是伪汇编(AT&T伪汇编都一样的代码,你指望下层x86汇编还能不一样么~)??的实现过程,来摸索一下他们的特点与本质。

 

首先,引用(Reference)在C中没有,是C++ 才有的概念~! 要用g++编译器。


定义:引用就是某个目标变量的“别名”(alias)

 

在我看来,这个“目标变量”也要加上引号,看“目标变量”到底是怎么定义的了。如果“目标变量”由变量名和值组成,那引用应该是不包含“变量名”这部分的,说白了,觉得他就是一个“新变量”,只是他和原变量的“值”(即,目标地址,存储内容)是共用的。

 

 

实例测试:

 引用,另一种,c++,指针,本质3

 

用g++编译,gdb调试:

 引用,另一种,c++,指针,本质4

 

可以看到,让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;

 


围绕我的”引用即指针“的理念,再做一个摸索。既然认为引用是指针了,那么sizeof任何指针,包括double的,肯定都是4(我的32位机)。我定义一个double的引用,看看sizeof结果如何(右侧为输出结果):

引用,另一种,c++,指针,本质5

这个结果倒是没夸张到直接让ref变成pointer。sizeof(refd)还是按普通的double来算大小,而不是直接按指针来算的。但是也情有可原吧,都说了,虽然他的底层实现和指针一样,但是sizeof()需要的是返回类型,它的返回类型??即”操作级别“,还是比指针要低的,和普通的变量相仿。

 

最后:到底怎样理解引用更好?

首先,不太同意“引用就是一个别名,不占用内存空间“的说法,至少这句话可以再严谨点??”引用不为值再去开辟一个地址空间,但是其本身要占用空间~!“

 

奇了怪了,引用确实占用栈空间,也确实是存储了目标变量的地址~~~那既然有空间,就应该和指针一样,我改变你的值不就等于改变你的指向了么?

但是,因为它和指针不在同一个“操作级别”上,它的”值“又不是地址,也不能像指针那样改变他的指向。

(“操作级别”是通过存储内容来判定的,比如普通变量的存储内容是“值”,而指针的存储内容是“地址”,可以通过指针独特的“*”操作来判断这个“级别”)

 

个人倾向于认为引用本身就是一种指针,至于他又不能像指针一样进行重定向等操作,觉得这些完全是语言级别或者说编译器的刻意限制,只是一种规则,没有其他原因。

再次怀疑人生??反正翻译成下层的东西,都是那点破事,转换成最后就是一些地址一些寄存器,你能找到地址你就能改(不能改的话,又是哪层编译器或者汇编器限制你的呢?)~!那么,编译器的本质如何?到底什么叫做编程语言?各层语言界限如何?从这么多的实践操作经验来总结,似乎也逐渐理解了些,如果此时再去看看《编译原理》,或许会有所收获。

 

 

完~!

 

------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ---------------

OTHER:

------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ---------------

写的是关于引用的,但是通过用gdb调试,个人还是有其他方面的收获:

例如,AT&T汇编中括号的含义,目测带括号是取地址,不带括号是原寄存器,好像和之前《计算机组成原理》的伪指令规则差不多。

对比:

存入数值到eax寄存器??用%eax。

存入数值到eax寄存器所储存的内存地址处??用(%eax)。

esp的操作同样如此:比如0x18(%esp)应该是从esp取出内存地址,再加上0x18偏移量。

 

还有,之前看linux的伪汇编,esp一般都是不变的,变的是偏移值,使用类似于0x1c(%esp)的形式进行操作。

 

每次编译运行,esp起始都是230结尾的(系统决定,具体:0xbffff230),但是通过本例观察,说esp不变是不准确的,执行系统调用,涉及各种库的时候,一直在变:从230到22c、228、224。。。等于栈下移了?在同一函数内不移,切换了才移?

引用,另一种,c++,指针,本质6

也许试试嵌套个函数什么的也会有发现~

关于栈指针怎么跳转,甚至发生函数跳转时十几个寄存器到底保存上下文需要几个,而这几个压栈又是怎么压的,有一个规则,按顺序压,按倒序取?这又是另外一篇日志要探索的事情了。

其他未完成作业:

 

看看const的实现又是怎样的,是否有什么特殊的方法规定”只读“,比如转存寄存器之类的。

使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

(待验证)

 

分享到:
评论

相关推荐

    C++重要的谨记指针和引用的区别

    在C++编程语言中,指针和引用是两种非常关键的概念,它们都允许我们间接访问内存中的对象。然而,它们之间存在着本质的区别,理解和掌握这些差异对于编写高效、安全的代码至关重要。 首先,指针是一个变量,它存储...

    c++关于指针的学习资料

    在C++中,指针是一个变量,它存储的是另一个变量的内存地址。例如,当声明一个整型指针`int* p`,我们实际上创建了一个可以保存整型变量地址的变量`p`。通过`*p`,我们可以访问和修改指针所指向的内存位置上的值。在...

    C++中指针和引用的区别

    C++template.ppt文件可能包含有关C++模板的进一步信息,模板是C++中另一种强大的工具,允许我们创建泛型代码,以实现更高的代码复用和灵活性。模板可以用于创建泛型函数和类,支持不同类型的参数,使得算法和数据...

    C++指针资料大合集

    在C++中,指针是一个变量,其存储的是另一个变量的地址。通过指针,我们可以间接访问和修改该地址所指向的数据。这使得指针成为一种强大的工具,但也带来了潜在的风险,如空指针引用、未初始化的指针和悬挂指针等...

    指针和引用的区别

    引用不是一个独立的实体,它只是一个现有变量的另一个名字,引用在声明时必须被赋值,并且在程序的运行过程中不能改变引用的对象。 2. **初始化** - **指针**:指针在声明时可以不初始化,但未初始化的指针不能...

    C++指针与引用的详解

    C++中的指针和引用是两种非常重要的编程概念,它们都是用来间接访问内存中的对象,但有着本质的区别和各自的用途。 首先,指针是一个变量,它存储了一个内存地址,这个地址指向另一个对象。指针可以被赋值为NULL,...

    C++中的引用

    常引用(Const Reference)是另一种形式的引用,它通过在引用声明前加上`const`关键字,如`const int &ra = a;`。常引用不能用来修改目标变量的值,增强了代码的健壮性。例如,尝试通过常引用`ra`修改`a`的值会引发...

    c++中引用的用法和应用实例

    在C++编程语言中,引用(Reference)是一个非常重要的特性,它为程序员提供了另一种方式来访问和操作已存在的变量。引用不同于指针,它更像是一个变量的别名,一旦引用被初始化指向某个变量,就不能再改变引用的目标...

    CqxianPDF_C/C++指针_

    指针是C和C++中的一种特殊类型的数据,它存储的是一个内存地址,该地址指向程序中的另一个变量。理解指针可以帮助我们编写更高效、更灵活的代码,尤其是在处理大型数据结构、动态内存分配和函数参数传递时。 1. **...

    C(C++)指针

    C/C++还支持多级指针,即指针指向的变量是另一个指针。例如,我们可以声明一个指向指针的指针: ```cpp int **ptrToPtr; int *ptr = new int(10); // 动态分配内存 ptrToPtr = &ptr; // ptrToPtr指向ptr ``` 五、...

    C++程序设计第八章指针和引用[收集].pdf

    1. **引用的引入**:引用是C++特有的概念,它是一个别名,让一个变量可以通过另一个名称访问。引用在声明时必须初始化,并且一旦初始化后,就不能改变引用的对象。引用不能是NULL,总是必须引用某个已存在的对象。 ...

    C++指针专题讲解.zip

    1. **迭代器**:STL容器(如`vector`、`list`)的迭代器本质上是一种指针,可以用来遍历容器中的元素。 2. **指针与算法**:C++标准库中的算法函数(如`sort`、`find`)经常与指针一起使用,操作原始数组或容器的...

    C++基础之this指针与另一种“多态”

    多态是C++中的另一个核心特性,它允许我们通过基类的指针或引用来调用派生类的成员函数。这种多态性主要体现在虚函数上。虽然`this`指针在所有成员函数中都存在,但只有在虚函数调用中,它才会体现多态性。当通过...

    从汇编看c++中引用与指针的使用分析

    引用提供了一种安全、简单的间接访问方式,而指针则提供了更多的灵活性和控制权。理解它们的工作原理以及如何在汇编层面上实现,对于编写高效、可靠的C++代码至关重要。在实际编程中,应根据具体需求选择使用引用或...

    C+++实训大一实训作业

    在大学的计算机科学课程中,C++是一种常用于教学的编程语言,因为它具有高效、灵活且功能强大的特性。"C++实训大一实训作业"通常会涵盖基础的编程概念、语法以及面向对象编程的核心概念。以下是一些可能涉及的知识点...

    C++ Primer注解之引用和指针

    总结来说,引用和指针都是C++中用于间接访问对象的手段,但它们有本质的区别。引用更安全、更简洁,而指针提供了更多的灵活性,但也伴随着潜在的风险。在编程时,根据需求选择合适的工具,是提高代码质量和效率的...

    C++程序设计与数据结构基础:第3章 数组与指针.ppt

    指针是C++中另一个核心概念,它是一个变量,其值是另一个变量的地址。指针变量可以指向任何类型的数据,包括数组。在C++中,可以声明一个指针变量,如`float *p;`,然后通过`&`运算符获取变量的地址赋给指针,或者...

    指针和指针变量以及在程序中的实现方法

    指针本质上是一个变量,它存储的是另一个变量的内存地址,即变量在计算机内存中所处的位置。理解并熟练运用指针是成为C语言高手的关键。 在C语言中,不同的数据类型占据的内存大小不同。例如,`int`类型的变量`a`和...

    c++ 入门 教程 由浅入深学习c++

    C++是一种广泛应用于系统软件、应用软件、游戏开发、嵌入式系统等领域的高级编程语言。作为初学者,了解并掌握C++的基础至关重要。本教程旨在帮助你从零开始,逐步深入地学习C++。 首先,"C++ Primer"是一本经典的...

    指针的使用总结

    如果一个指针在另一个之前,那么`p1 为真。 12. **空指针**: `NULL`或`nullptr`常量代表空指针,用于表示指针未初始化或不指向任何有效内存。 理解并熟练运用这些概念是掌握C++指针的关键。在实际编程中,合理地...

Global site tag (gtag.js) - Google Analytics