深入理解指针与引用的区别

指针和引用都是一种内存地址的概念,指针与引用本质上的区别在于:指针是一个实体,而引用是一个别名。

在参数传递中,指针需要被解引用后才可以对对象进行操作,而直接对引用进行的修改都会作用到引用所指对象上。

从编译的角度来讲,指针指向一块内存,指针的内容是所指向的内存的地址。在程序编译的时候,将指针添加到符号表中时,会把“指针变量名-指针变量的地址”放到符号表中。

因此指针包含的内容可以改变,有 const 和非 const 的区别,甚至可以为空,sizeof 指针得到的是指针类型的大小。

而对于引用来说,它只是一块内存的别名。添加符号表时,是将“引用变量名-引用对象的地址”加入到符号表中。

由于符号表一经完成不能改变。所以引用必须而且只能在定义时被绑定到一块内存上,后续不能更改,也不能为空,也没有 const 和非 const 的区别,sizeof 引用得到的是它所代表的对象的大小。

假设我们定义了一个变量a :

int a = 10;

在编译器的编译的某个阶段(随着编译的进行,符号表是不断变化的)有如下符号表(简化过后的符号表,实际的符号表并不长这个样子

变量名 首地址 类型 空间大小
a 0X3333 int 4字节 10

那么当我们将a++时,编译器会查找符号表,找到变量名为a的条目(符号表本质上就是一个表格,或者说是一个数据库),找到之后,根据a的首地址、类型、值等将a的值变为11

编译器将代码翻译成机器代码后,是没有变量名的,学过汇编的都知道,汇编语言都是直接操作地址的,根本没有变量名。

所以变量名可以理解为编译器符号表的一个索引,我们再做a++运算时,实际上编译器是根据变量名a找到了我们要操作的内存的首地址,然后在根据符号表中记录的属性,对该内存区域进行操作。

所以说变量名可以理解为地址的索引,或者说变量名代表了符号表中的一行。这一行中不进有首地址,还有类型、空间大小、值等。

所以我们要找到变量的地址需要用&(取地址)符号。(当然对于数组是不需要的)

所以我们可以这样说,变量名可以理解为被各种属性修饰的地址(这里的属性指类型、空间大小、值等)。进一步,变量名可以理解为一个受限制的地址(受各种属性的限制)。

我们知道指针变量中存储的就是符号表中,某一行的首地址字段。然而只有这个首地址,没有那些属性(其他字段的限制),我们能做的事情就会很多:

我们可以人为的给这个首地址分配内存空间(malloc函数)、我们可以人为地给这个地址空间设定访问规则为按double类型访问(malloc时,强制类型转换成double),这也就是说我们可以人为规定首地址的类型和空间大小等属性。

这也是指针的强大之处,指针中存的就是首地址,而这个首地址的各种属性都可以由程序员决定。

但是通过变量名,我们可以访问到的地址是受限制的,因为在我们写int a= 10的时候,a的地址空间的大小、类型等都是编译器为我们分配和决定的,我们没有权利去做。但是通过声明一个指针,我们就可以自己决定这块内存的属性。

这也就是指针的强大之处,也是它的可怕之处。

所以一句话总结一下变量名和地址的关系就是:变量名可以理解为一个受各种属性限制的地址(而这些属性,是编译器决定的),指针就是地址,创建一个指针,就是创建一个不受各种属性限制的地址。

再来说引用,引用就是变量的一个别名字,那我们写,比如int &b = a;这个时候编译器的符号表中就会多一个条目:

变量名 首地址 类型 空间大小
b 0x3333 int 4字节 10

 除了变量名不一样,其他的与a都一样,所以a和b就是一个东西。

因此从编译的角度来讲,引用和指针的区别,就是变量名和指针的区别,进一步可以简单理解为变量名和首地址的区别。

猜你喜欢

转载自www.cnblogs.com/chengeer/p/13394203.html