在编码过程中,新手经常会遇到的一个问题是,我明明在函数中交换了数据,为什么函数外面显示的不对,最常见的例子就是swap()函数。先看两个代码:
代码一:
#include
void swap(int a, int b)
{
int t = a;
a = b;
b = t;
}
int main()
{
int a = 3, b = 2;
swap(a, b);
printf("%d %d", a, b);
return 0;
}
代码二:
#include
void swap(int *a, int *b)
{
int x = 100;
int y = 200;
a = &x;
b = &y;
}
int main()
{
int a = 3, b = 2;
swap(&a, &b);
printf("%d %d", a, b);
return 0;
}
这两个代码输出的都是原来的3和2,为什么?特别是第二段代码,明明传进去的是指针,为什么还是不起作用?
在函数传参的时候,实际上会发生一次浅拷贝,浅拷贝暂且不表,这里只需要知道,在函数传参的时候,会发生拷贝就可以了。
先看第一段代码,其实际的执行过程为:
(1)声明两个临时变量,int a,int b; (这两个变量跟main中的a和b重名)
(2)让a = a, b = b,=前面的a和b是swap函数声明的临时变量,=后面的是main中的实际变量的值;
(3)int t = a; //注意这里的a是swap中声明的临时变量,它只是跟main中的变量a有相同的名字,但是他们在内存上的位置是不一样的
(4)a = b; //这里的a和b都是swap声明的临时变量,跟main中的a和b没有任何关系
(5)b=t;//b是swap声明的临时变量,跟main中的b没有任何关系
所以,整个swap函数运行完后,跟main中的a和b实际没有半点关系,有关系的地方仅仅在于(1)中,用main中的a和b的值初始化了swap声明的a和b,仅此而已。所以,这个swap函数对main中a和b的值没有任何影响。
再看第二段代码,其执行过程如下:
(1)声明两个临时变量,int *a,int *b
(2)a=&a,b=&b;=前面的a和b是swap函数声明的临时变量,=后面的是main中的实际变量a和b,也就是把main中a和b的地址分别赋值给swap中的临时变量a和b
(3)a=&x; 把x的地址赋值给a,a的地址发生改变,但a是swap中的临时变量!!!
(4)b=&y; 把y的地址赋值给b,b的地址发生改变,但b是swap中的临时变量!!!
所以,函数运行结束后,函数的功能实际上是改变了两个临时变量a和b的地址,而这两个临时变量跟main函数中变量a和b的关系仅仅是:在声明临时变量a和b的时候,用main中变量a和b的地址初始化了一下,然后再函数体内改变了临时变量a和b的值(内存地址),所以swap中的操作,跟main中的变量a和b没有半点关系,也就不会对他们的值产生影响。
既然上面两种方式都不能达到想要的结果,该怎么办呢?
函数传值分为传值和传址两种,传值方式对函数外的变量没有影响,传址方式对函数外的变量有影响,传值和传址的区别不在于参数,而在于在函数体内操作的对象。上面两个代码都属于传值操作,因为再函数体中操作的都是参数值本身。传址方式的差别在于,函数体中操作的是参数指向的内存。
看代码三:
#include
void func(int* a, int* b)
{
static int x = 100;
static int y = 200;
*a = x;
*b = y;
}
int main()
{
int a = 1;
int b = 2;
func(&a, &b);
printf("%d %d", a, b);
return 0;
}
这段代码跟第二段代码的区别仅仅在于swap函数中的最后两句。
在上面的代码分析中已经知道,函数体中的a是swap的临时变量,a被初始化为了main中变量a的地址,所以\*a =x,这个操作,其本质上就是把x的值放在a指向的内存中,而a指向的内存跟main中变量a的内存是同一块内存,所以,main中变量a的值就会发生改变。同理, \*b=y;将y的值放在了b指向的内存块中,main中b的值会发生改变。这种方式,就是传址。
在C++中,有引用的说法,引用就是变量的一个别名,他们拥有相同的内存,所以代码四也能实现预期的效果。
代码四:
#include
using namespace std;
//这里的参数是引用,这里的参数a和b,是main中变量a和b的别名,他们具有相同的内存
void swap(int &a, int &b)
{
int x = 100;
int y = 200;
a = x;
b = y;
}
int main()
{
int a = 3, b = 2;
swap(a, b);
cout << a << " " << b;
return 0;
}