从原理上理解C++中引用和指针的区别

C++中引入了引用这种数据类型,可以对引用所指向的数据直接进行修改,和对原始数据操作一样,类似一种别名;而指针操作却需要考虑引用与解引用,那么这两种有什么区别呢?仅从语法上死记硬背总是忘记,用起来也不得心应手,从原理上理解才是最好的选择,接下来我试图从我个人理解的角度进行分析(可能是错的),最后再做语法上的对比。

引用只是别名,并不占有内存

设计引用目的只是为了将程序员从指针操作中解放出来,例如int &i = a; 对编译器来说操作引用i与直接操作原始数a是一个效果,编译器并不区分i和a,编译器在进行编译的时候直接将i和a用同一个内存表示,运算时占有同一个寄存器。编译器在编译之后i和a都被转换为编译器认识的标记x。就好比对朋友来说你有好几个外号,但都是指你,而户籍所并不区分这些,只认识你户口本上注册的名字。

指针是个数据,拥有自己的内存

指针是一个单独的数据结构,拥有自己的内存,和其所指向的数据无关;只是语法上指针这种数据可以操作其所指向的对象,修改指向对象的数据,对指针本身来说并没有什么区别。

为了验证以上猜测,用一个小程序测试一下,代码如下

#include<iostream>
using namespace std;

int main()
{
        int a = 1;
        int &i = a;
        int *p = &a; 

        cout << "&a = " << &a << endl;
        cout << "&i = "  << &i << endl;
        cout << " p = "  << &p << endl;

        return 0;
}

运行结果如下:

&a = 0x7ffc74318914
&i = 0x7ffc74318914
 p = 0x7ffc74318918

从上面的程序运行结果我们可以看出引用i和原始数据a占用同一个内存,而指针拥有自己的内存。(内存分配的如此相近主要是因为这是通过栈临时分配的,若不是程序中强行访问这些变量的地址,编译器是不会给这些数据分配内存的,全部保存在寄存器上)

将引用理解为宏定义(错误的!!!

开始的时候我一直将引用理解为宏定义,在进行预处理之后引用全部被替换为所指向的对象,所以引用并不占有单独的内存,只是其所引用对象的别名;将引用理解为宏定义完全符合引用的所有性质,但实际上引用并不是通过宏定义这样的原理。我们将之前的程序进行预处理,观察一下预处理后的程序

执行预处理

g++ -E reference.cc > reference.i

预处理后的main部分(无关部分略去):

using namespace std;

int main()
{
 int a = 1;
 int &i = a;
 int *p = &a;

 cout << "&a = " << &a << endl;
 cout << "&i = " << &i << endl;
 cout << " p = " << &p << endl;

 return 0;
}

我们可以看到引用i并没有被解释为宏,所以引用并不是通过宏定义这样的原理实现。

编译的角度来阐述

程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

语法上的区别

在了解原理后,语法上的区别也就顺理成章了。

  • 指针是一个实体,而引用仅是个别名;
  • 引用只能在定义时被初始化一次,之后不可变;指针可变;
  • 引用没有const,指针有const,const的指针不可变;
  • 引用不能为空,指针可以为空;
  • “sizeof引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
  • 指针和引用的自增(++)运算意义不一样;
  • 引用是类型安全的,而指针不是 (引用比指针多了类型检查);

猜你喜欢

转载自blog.csdn.net/wareric/article/details/79941930