指针、值和引用

1. 指针和引用

1.1 指针和引用概述

1.1.1 指针

对于char* ptr,ptr为指向char的指针,即char*类型的变量ptr保存的是一个char对象的地址,而char可加限定c词const、volatile等。(char可换为其他类型)

如下所示,p中存储的是c的地址,c中存储的是‘A’字符

1
2
char c = 'A';
char* p = &c;

通过*可取p所指向的内容,通过&可取p的地址,即:

  • *p == c的内容 == 'A'
  • p == c的地址 == &c
  • &p == p的地址

1.1.2 引用

引用相当于一个对象的别名,主要用于函数参数和返回值类型。int&表示对int类型的引用(int可换为其他类型)

1
2
3
4
5
6
int i = 1;
int& r = i;	// r指向了i的空间,此时对i和r的操作将是相同的
i = 2;
cout << r << endl;	// 输出 2
r = 3;
cout << i << endl;	// 输出 3

可以认为引用就是将 i 和 r 相关联(绑定)了,使得 i 和 r 代表了相同的一块空间

1.1.3 引用与指针的区别

  1. 引用一旦指向某一对象就不可更改: 如上面程序,引用后的 r 就和普通的整形变量没有什么区别(若不考虑 i 的存在,完全可以把int i = 1; int& r = i;当成int r = 1;来看待)。引用即是将两个变量进行了绑定,而指针仅仅是存储了指向内存的地址,所以通过引用名(如r)可以直接访问指向的内存,而通过指针名(如p)却只能访问到地址,要通过(如*p)才能访问到地址所指的内存1

  2. 对引用的操作将与所指对象同步,而不是像操作指针一样会改变指针的指向:

    如引用的++操作将直接使得指向内容+1,而指针的++会让指针指向下一个地址。如int i = 1; int& r = i; int* p = &r;此时 r 和 p 都指向了 i 所在的空间,但其意义是完全不同的,p是开辟了一个内存来存储 i 的地址,而 r 就是 i

  3. 引用不可以为空,但指针可以为空: 正因如此指针在使用前都需要进行判空操作,而引用变量若不进行初始化甚至无法通过编译

虽说引用和指针有许多区别,但两者在本质上是相同的,可以根据汇编代码看出:

1
2
3
4
5
6
//引用int& ref = i; 
8048727: 8d 44 24 1c			lea 0x1c(%esp), %eax		// esp寄存器里的变量i的地址传给eax
804872b: 89 44 24 18			mov %eax, 0x18(%esp)		// 将寄存器eax中的内容(i的地址)传给寄存器中的变量ref
//指针int* p = &i;
8048777: 8d 44 24 1c        	lea 0x1c(%esp), %eax		// esp寄存器里的变量i的地址传给eax
804877b: 89 44 24 10    		mov %eax, 0x10(%esp) 		// 将寄存器eax中的内容(i的地址)传给寄存器中的变量p

引用和指针在汇编上的实现是完全相同的,即是说引用的本质其实就是指针,只是比指针更加安全。

1.1.4 const关键字

引用和const指针是不是几乎是相同的呢?引用的本质其实就是指针,只是在指针上增加了一些规则,使得它更加安全。实际上,若不考虑赋空值,那么:

  • 引用 type& x 等于 const指针 const type* x
  • const引用 const type& x 等于 指向const的const指针 const type* const x

1.2 值传递、指针传递、引用传递

1.2.1 值传递

值传递是形参对实参的拷贝,即将实参赋值到了形参上,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的,参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。2

1.2.2 指针传递

形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作,但对于形参的操作并不会改变实参的值(改变形参存储的地址不会改变实参的指向),因为指针传递的本质也是值的传递(将指针变量存储的地址复制给形参的变量)

1.2.3 引用传递

形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

参考

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计