c++中的参数传递有三种:
1.传值
默认的传值,即在函数被调用的时候,给形参申请一个空间,再将实参的值传递给形参,对形参的任何改变不会影响实参数的值:
#include<iostream>
using namespace std;
#define ok 0
int add(int x)
{
cout << "调用前的形式参数所在地址,如果和实参地址一样说明新申请了内存空间" << &x << endl;
cout << "变化前的形式参数值" << x << endl;
x++;
cout << "变化后的形式参数值" << x << endl;
cout << "调用后的形式参数所在地址" << &x << endl;
return ok;
}
int main()
{
int a = 1;
cout << "调用前的实在参数所在地址,如果和形参地址一样说明新申请了内存空间" << &a << endl;
cout << "调用前的实在参数值" << a << endl;
add(a);
cout << "调用后的实在参数值" << a << endl;
cout << "调用后的实在参数所在地址" << &a << endl;
return ok;
}
输出结果如下:
可以看到形参实参的地址不一样,说明新申请了内存空间,大小刚好为int数据的4个字节。
注:x是在函数内部定义的变量因而其作用范围是局部的(仅在该函数范围内能被调用)0,生命周期是动态的(调用该函数时候创建,函数调用完成后就消除。)
2.传址
所谓传址又叫做传指针,即在函数被调用的时候,给形参开辟一个空间用来存放传递过来的地址,将实参所在的内存地址传递给形参,对形参的任何改变也不会影响实参所指的内容,但是对形参所指的内容的改变将会影响到实参所指的内容(因为这两个指针都指向同一个内存空间)
#include<iostream>
using namespace std;
#define ok 0
int add(int *x)
{
cout << "指针x所在的地址,和指针a所在地址比较,如果相同说明函数调用时,没有重新开辟内存空间,反之则开辟了内存空间用以存储传来的地址" << &x << endl;
cout << "指针x的值" << x << endl;
cout << "指针x所指的值" <<* x << endl;
x++;
cout << "指针x变化后的指针x的值" << x << endl;
cout << "指针x变化后的指针x所指的值" << *x << endl;
return ok;
}
int main()
{
int c = 1;
int *a = &c;
cout <<"调用前指针a所在的地址"<< &a<<endl;
cout << "调用前指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用前指针a所指的值" << *a << endl;
add(a);
cout << "调用后指针a所在的地址" << &a << endl;
cout << "调用后指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用后指针a所指的值" << *a << endl;
return ok;
}
运行下结果如下:
指针a和指针x所在地址不同说明新申请内存空间用以存放传来的地址。
对指针x的值的改变不会改变指针a的值,也不会改变指针a所指的值,所以输出结果为0。
#include<iostream>
using namespace std;
#define ok 0
int add(int *x)
{
cout << "指针x所在的地址,和指针a所在地址比较,如果相同说明函数调用时,没有重新开辟内存空间,反之则开辟了内存空间用以存储传来的地址" << &x << endl;
cout << "指针x的值" << x << endl;
cout << "指针x所指的值" << *x << endl;
(*x)++;
cout << "指针x的所指的内容变化后的指针x的值" << x << endl;
cout << "指针x所指的内容变化后指针x所指的值" << *x << endl;
return ok;
}
int main()
{
int c = 1;
int *a = &c;
cout << "调用前指针a所在的地址" << &a << endl;
cout << "调用前指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用前指针a所指的值" << *a << endl;
add(a);
cout << "调用后指针a所在的地址" << &a << endl;
cout << "调用后指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用后指针a所指的值" << *a << endl;
return ok;
}
运行结果如下:
指针a和指针x所在地址不同说明新申请内存空间用以存放传来的地址。
对指针x所指的值的改变会改变指针a所指的值。所以输出结果为2。
3.传引用
引用是什么?c++中的引用可以理解为typedef的作用(c中是没有此用法),引用相当于是实参的别名,对形参的任何操作,就是对实参的操作,函数调用时不会再内存中新开辟空间。
#include<iostream>
using namespace std;
#define ok 0
int add(int &x)
{
cout << "x所在的地址,和a所在地址比较,如果相同说明函数调用时,没有重新开辟内存空间,反之则开辟了内存空间" << &x << endl;
cout << "x的值" << x << endl;
x++;
cout << "x的变化后的值" << x << endl;
cout << "x变化后的地址" << &x << endl;
return ok;
}
int main()
{
int a = 1;
cout << "调用前a所在的地址" << &a << endl;
cout << "调用a的值" << a << endl;
add(a);
cout << "调用后a所在的地址" << &a << endl;
cout << "调用后a的值" << a << endl;
return ok;
}
运行结果如下:
可以看到实参和形参的地址是相通的,也就是说并没有开辟一个新的空间。
4.对指针引用
其形式为function(数据类型 *&指针名)
,这里的指针名就是引用,就是传来的指针的别名。对它的操作就是对实参的操作,对它所指的值的操作,就是对实参所指的值的操作。
#include<iostream>
using namespace std;
#define ok 0
int add(int *&x)
{
cout << "指针x所在的地址,和指针a所在地址比较,如果相同说明函数调用时,没有重新开辟内存空间,反之则开辟了内存空间用以存储传来的地址" << &x << endl;
cout << "指针x的值" << x << endl;
cout << "指针x所指的值" << *x << endl;
x++;
cout << "指针x变化后的指针x的值" << x << endl;
cout << "指针x变化后的指针x所指的值" << *x << endl;
return ok;
}
int main()
{
int c = 1;
int *a = &c;
cout << "调用前指针a所在的地址" << &a << endl;
cout << "调用前指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用前指针a所指的值" << *a << endl;
add(a);
cout << "调用后指针a所在的地址" << &a << endl;
cout << "调用后指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用后指针a所指的值" << *a << endl;
return ok;
}
运行结果如下:
可以看到调用时没有在内存中开辟空间存储传递来的地址。
对形参的改变就是对实参的改变。
#include<iostream>
using namespace std;
#define ok 0
int add(int *&x)
{
cout << "指针x所在的地址,和指针a所在地址比较,如果相同说明函数调用时,没有重新开辟内存空间,反之则开辟了内存空间用以存储传来的地址" << &x << endl;
cout << "指针x的值" << x << endl;
cout << "指针x所指的值" << *x << endl;
(*x)++;
cout << "指针x变化后的指针x的值" << x << endl;
cout << "指针x变化后的指针x所指的值" << *x << endl;
return ok;
}
int main()
{
int c = 1;
int *a = &c;
cout << "调用前指针a所在的地址" << &a << endl;
cout << "调用前指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用前指针a所指的值" << *a << endl;
add(a);
cout << "调用后指针a所在的地址" << &a << endl;
cout << "调用后指针a的值(也就是变量c的内存地址)" << a << endl;
cout << "调用后指针a所指的值" << *a << endl;
return ok;
}
运行结果如下:
可以看到调用时没有在内存中开辟空间存储传递来的地址。
对形参所指内容的改变就是对实参所指内容的改变。
5.传数组
数组作为形参传递,实质传递的数组的首地址,一维数组 :
数据类型 数组名[],二维数组:
数据类型 数组名[][上界]` ,对数组来说作为参数传递时候没有“引用”这种说法,可以理解为,默认为引用,不用加引用符号“&”,对形参操作就是对实参的操作
6.总结
###5.1一般时候对传入的实参如果要做改变优先考虑传引用(也可以传址,然后对指针所指内容做改变,但对变量的操作相对对指针的操作一般要更容易思考一些)或者传入的数据非常大时候,调用函数时可以节约拷贝的时间和空间;如果对传入的实参不做改变可以传值也可传指针。
###5.2传引用有些时候可以避免未初始化的错误。
#include<iostream>
using namespace std;
#define ok 0
struct linknode
{
int data;
linknode *next;
};
typedef linknode linklist;
int initlist(linklist *L)
{
L = (linknode*)malloc(sizeof(linknode));
return ok;
}
int main()
{
linklist *L;
initlist(L);
}
这里由于在主函数定义了指针变量L后没有初始化赋值,因而出现未初始化的错误。将int initlist(linklist *L)
改成int initlist(linklist *&L)
就可以免于此错误。
ps:其原理以前我一直以为是定义之后未对其初始化因而调用函数时候传递的地址是不可知的,因而出错。但是又发现如果在主函数中添加一行cout<<&L
不用使用引用也可以正常,并且此时L传递的地址仍然也是不可知的。
#include<iostream>
using namespace std;
#define ok 0
struct linknode
{
int data;
linknode *next;
};
typedef linknode linklist;
int initlist(linklist *L)
{
L = (linknode*)malloc(sizeof(linknode));
return ok;
}
int main()
{
linklist *L;
cout<<&L
initlist(L);
}