相信看了这篇博客,你会对c++的参数传递有更深的了解
1、按值传递
简单来说,在给一个函数传递参数时,该函数获得的参数只是你传递的参数的副本,彼此之间分开存储,不共享一片内存。
举个例子
void swap(int x,int y)
{
int temp=x;
x=y;
y=temp;
}
int main()
{
int a=3;
int b=4;
swap(a,b);
cout<<"a="<<a<<", b="<<b<<endl;
}
结果输出"a=3, b=4"。
显然,swap函数并没有起到作用,为什么呢?因为在调用swap(a,b);的时候,传递swap函数的形参x和y的并不是a和b本身,而是存储在另一个地方的a和b的值,通俗地讲,也就是a和b的值的副本。你无法通过对副本的操作去改变本体。
编译后的汇编代码
call ___main
movl $3, -12(%ebp)
movl $4, -16(%ebp)
movl -16(%ebp), %eax
movl %eax, 4(%esp)
movl -12(%ebp), %eax
movl %eax, (%esp)
call __Z4swapii
subl $16, %esp
movl 8(%ebp), %eax
movl %eax, -4(%ebp)
movl 12(%ebp), %eax
movl %eax, 8(%ebp)
movl -4(%ebp), %eax
movl %eax, 12(%ebp)
在底层上,a和b存储在内存中,在调用swap函数的之前,main函数会把a和b的值的压入栈中,接着调用swap函数,swap函数便可以拿栈中的值进行运算,swap函数返回后,控制又回到main函数上。栈中的a和b的值交换,并不会改变存储在内存中a和b本身的值。
2、指针传递
简单来说,在给一个函数传递参数的时候,该函数获得的是你传递参数的地址的值,任何对该地址的操作,都会引起参数的改变。
举个例子
void swap(int *x,int *y)
{
int temp=*x;
*x=*y;
*y=temp;
}
int main()
{
int a=3;
int b=4;
swap(&a,&b);
cout<<"a="<<a<<", b="<<b<<endl;
}
结果输出"a=4, b=3"。
swap函数起作用了,来看看发生了什么,传递给swap函数的形参x和y的是参数a和b的地址,而swap函数直接对两个参数的地址进行解引用,意味着直接在存储a和b的内存上改变a和b的值,所以main函数中a和b的值会发生改变。
在底层上,指针传递的实现和按值传递的实现几乎是一样的,不同的是指针传递是main函数把a和b的地址压入栈中,而按值传递是main函数把a和b的值压入栈中,前者调用的函数拿到的是参数的地址,后者调用的函数拿到的仅仅是参数的值,而且该值存储在与参数存储内存不一样的地方,仅仅是一个副本。
3、引用传递
简单来说,引用传递在底层上的实现跟指针传递是一样的,但C++为引用传递添加了一点东西,使得引用传递有自己的特点。
1)、当引用被创建时,它必须被初始化(指针则可以在任何时候被初始化)
2)、一旦一个引用被初始化为指向一个对象,它就不能改变为另一个对象的引用(指针则可以在任何时候指向另一个对象)。
3)、不可能有NULL引用。必须确保引用是和一块合法的存储单元关联(指针可以置为NULL)。
下面的汇编代码对应上面指针传递的main函数代码,也对应于上面引用传递的的main函数代码,两者的产生的汇编代码是等价的。
call ___main
movl $3, -28(%ebp)
movl $4, -32(%ebp)
leal -32(%ebp), %eax
movl %eax, 4(%esp)
leal -28(%ebp), %eax
movl %eax, (%esp)
call __Z4swapRiS_
subl $16, %esp
movl 8(%ebp), %eax
movl (%eax), %eax
movl %eax, -4(%ebp)
movl 12(%ebp), %eax
movl (%eax), %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
movl 12(%ebp), %eax
movl -4(%ebp), %edx
movl %edx, (%eax)
举个例子
void swap(int &x,int &y)
{
int temp=x;
x=y;
y=temp;
}
int main()
{
int a=3;
int b=4;
swap(a,b);
cout<<"a="<<a<<", b="<<b<<endl;
}
结果输出"a=4, b=3"。
本质上,引用传递在底层上和指针传递的实现方式是一样的,我们可以认为引用仅仅是语法上的一种不同方法(有时称为“语法糖”)。什么是语法糖,举个例子,a[i]和*(a+i)本质上是一样的,但a[i]的可读性更强,比*(a+i)更加直观容易理解,语法糖使得程序更加不容易出错。
同样的,引用传递虽然在底层的实现上和指针传递一样,但是从语言来说,引用不等于指针,因为c++给引用赋予了一些特性,这些特性使得引用传值在某些方面比指针传值要好,比如你不必担心参数是否被初始化了(编译器强迫它初始化),也不必知道怎样对它间接引用(这由编译器做)。