取地址符&做函数形参?
引入
这个问题花去了整整一天的研究。
先看一段严蔚敏的《数据结构》中栈的例程:
Status Pop(SqStack &S, SElemType &e);
Status Pop(SqStack &S, int &e);
类比的疑问
我们都知道:
int *a,*b;
int **a, **b;
那么难道说是
int &e;
仔细看下接下来的函数定义:
显然这里可以看出由于top指针指向的是SElemType类型,所以e是SElemType类型的。所以以上类比显然是不对的。
C/C++中的引用参数
查找了很多的资料发现,这个实际上是C++里的形参符号,必须要在跟在数据类型的后面使用。在函数内部对形参的操作都等同于直接操作原变量。
先说形参和实参
学过C语言的都知道,一个经典的例子是关于写一个交换两个变量a,b的值的函数:
void swap(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void main()
{
int a = 1, b = 2;
swap(a,b)l
}
形象的说,实参a是一份讲义,你在调用函数的时候,函数就像学生一样去要讲义(传递的实参)。函数向系统要了张白纸(栈区空间),然后把这篇文章抄了一份拿去用了,取名也叫作a。然后他怎么修改都不会
继续准确点说, 在程序运行的时候会分配一个全局区,我们这里说的a,b实际上属于全局变量,存储在全局区,也有的地方叫做静态区。而这里的形参存储在栈区,仅仅是保存了全局量的值,所以所有对形参a,b的操作都和静态区的a,b无关。
这里实参传递给形参的过程叫做值传递。
附:C/C++程序的内存分配知识
一个由C/C++编译的程序占用的内存分为以下几个部分 :
1、栈区(stack)― 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) ― 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。这个空间是公用的,如果没有释放会使得可用堆区空间变小,最好在申请后手动释放。
3、全局区(静态区)(static)―,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放
4、文字常量区 ―常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区―存放函数体的二进制代码。
所以我们可以理解为,这里的&e是为了说明e变量不是仅仅的把值传递进了函数内部。
// 利用指针的经典解法
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void main()
{
int a = 1, b = 2;
swap(&a,&b);
}
必须先弄清楚各种地址
这里用图简单通俗的说下,为了通俗易懂,严格上并不准确:
那么我们可以总结:
实际上在C语言中,使用&取地址符取出的是变量的[逻辑地址],就算用汇编进行操作也是一样。变量的物理地址只有操作系统知道,实际上逻辑地址和物理地址都是32位整数(32位机)。两个不同进程,就算逻辑地址一样,实际的物理地址也不同。
关于C语言的函数调用过程更加深度严谨(也更难懂)的知识,墙裂推荐这篇文章:
深入理解C语言的函数调用过程
通过引用传递和通过指针传递?
之前的两个例子,分别用常规的值传递和指针的传递实现数据交换的过程看起来不同,其实都是差不多的。实质上都是值传递。
第一个例子的执行过程:
第二个例子的执行过程:
下面是通过引用参数完成的交换过程。
void swap(int &a, int &b){
int temp;
temp = a;
a = b;
b = temp;
}
void main(){
void swap(int&, int&);
int a = 1, b = 2;
swap(a,b);
printf("%d %d\n",a,b);
}
引用实现原理的讨论
先讨论引用实现的系列文章,大佬们讲得比较透彻,而且论据丰富。
c++中的引用的使用原理以及使用实例
C++ 引用 参数传递 机制【强烈推荐】
C++引用的本质与修改引用的方法
举例剖析C++中引用的本质及引用作函数参数的使用
如果不想看干货长文的就看下下面的通俗简短讨论吧。
我们看下下面这段小程序:
int i = 0;
int &j = i;
j = 10;
int *p = &j;
汇编(伪汇编)解析如下:
;int i = 0;
mov dword ptr [i],0;
;int &j = i;
lea eax, [i];
mov dword ptr[j],eax;
; j = 10;
mov eax, dword ptr[j];
mov dword ptr [eax], 0Ah;
;int *p = &j;
mov eax,dword ptr [j]
mov dword ptr [p],eax
实际上,通过对汇编的分析可以看出:
对第2条的解释,关于指针操作拷贝副本和引用节省空间的详细解释可以看上面的文章—— 。