1.函数传参的三种主要形式
首先要知道函数形参和实参的特点
1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元,因此形参只有在函数内部有效,调用结束返回主调函数后则不能再使用该形参变量。
2.实参可以是变量、常量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,他们都必须具有确定的值,以便把这些值传送给形参。因此应该先用赋值、输入等办法获得确定值。
3.实参和形参在数量上、类型上、顺序上应严格一致,否则会发生类型不匹配的错误。
1.1复制传参(值传递)
形神只是将实参的值复制保存了一份,在被调函数中无论如何改变形参,都不影响主调函数中实参的值。
我们来看下面一段代码:
int my_add_ten(int x, int y)
{
printf("func : &x = %p, &y = %p\n", &x, &y);
x = x * 10;
y = y * 10;
int temp = x+y;
printf("func : x = %d, y = %d\n", x, y);//100 200
return temp;
}
被调函数的功能是将主调函数传递过来的参数放大10倍,并将放大后的值相加返回给主调函数,我们在参数放大之前先打印一下被调函数中形参的地址。
接下来是主调函数部分
int main(int argc, const char *argv[])
{
int x = 10;
int y = 20;
printf("main : &x = %p, &y = %p\n", &x, &y);//和上面子函数中xy的地址不一样
printf("main 前 : x = %d, y = %d\n", x, y);//10 20
int ret = my_add_ten(x, y);
printf("ret = %d\n", ret); //300
printf("main 后 : x = %d, y = %d\n", x, y);//10 20
return 0;
}
主调函数中,在定义x,y后我们亦首先打印变量的地址,代码的执行结果如下:
可以看到主调函数中实参x,y和被调函数中的形参x,y并不是同一个变量,因为二者的地址并不一致,当主调函数调用被调函数后,实参x,y的值复制给形参x,y,示意图如下;
对形参x,y无论进行何种操作,并不会影响实参x,y的值。如果我们想改变实参的值,我们需要将实参的地址传递给被调函数,也就是我们所说的地址传参,这将在下面得到验证。
1.2地址传参(地址传递)
如果想在被调函数中改变实参的值,我们需要将实参的地址传递过来,于此同时,在被调函数中的形参部分,我们应当用一个指针来接实参的地址,代码如下:
void my_add(int x, int y, int *z)
{
printf("my_add : z = %p\n", z);
*z = x+y;
printf("my_add : z = %d\n", *z);//30
}
在被调函数my_add中我们一共定义了三个变量:两个普通的整形变量x,y和一个整形指针变量z,z用来保存实参中的地址。接下来是主调函数部分:
int main(int argc, const char *argv[])
{
int a = 10;
int b = 20;
int ret = 0;
printf("main : &ret = %p\n", &ret);
my_add(a, b, &ret);
printf("ret = %d\n", ret); //30
return 0;
}
注意,在调用函数时必须注意形参和实参的类型必须严格一致,因此在对应形参指针的位置上我们传递的是变量ret的地址
可以看到z中保存到的是变量ret的地址,当我们对指针取*操作时,我们便可以操作地址所对应的变量,示意图如下(图中的地址并不是实际值,仅作参考使用):
当形参是指针时,也不一定时地址传递,有可能是值传递,看下面一段代码:
#include <stdio.h>
int m = 10;
int n = 20;
//功能:改变指针指向
void my_chage1(int* q)
{
printf("my_chage1前 q=%p\n", q);
q = &n;
printf("my_chage1后 q=%p\n", q);
}
int main(int argc, const char* argv[])
{
printf("m_add=%p\n",&m);
printf("n_add=%p\n",&n);
int* p = &m;
printf("*p = %d\n", *p);//10
my_chage1(p);
printf("*p = %d\n", *p);//10
return 0;
}
函数执行的结果如下:
我们将实参指针p中所存地址赋值给形参指针q,可以看到在my_chagel函数被调用后,形参指针q的指向改为指向变量n的地址,但是我们对p取*操作时,p的值依旧没有改变,这是为什么呢?原因是因为实参指针p将m的地址传递给形参指针q,执行*q = &n操作后,q指向了n,p中所存m的地址并没有发生改变,也就时前文所提到的被调函数中无论如何改变形参,都不影响主调函数中实参的值。示意图如下(图中的地址并不是实际值,仅作参考使用):
如果想通过形参指针q改变实参指针p的指向,那我们需要将实参指针p的地址传递到形参q,为了接受指针的地址,我们必须要用二级指针,看下面一段代码:
#include <stdio.h>
int m = 10;
int n = 20;
void my_chage2(int** q)
{
*q = &n;
}
int main(int argc, const char* argv[])
{
printf("m_add=%p\n",&m);
printf("n_add=%p\n",&n);
int* p = &m;
printf("*p = %d\n", *p);//10
my_chage2(&p);
printf("*p = %d\n", *p);//20
printf("p = %p &n = %p\n", p, &n);//一样的
return 0;
}
代码的执行结果如下:可以看到指针p所指向的地址正是变量n的地址
示意图如下(图中的地址并不是实际值,仅作参考使用):
1.3全局传参
全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的。实际上,全局变量也是一种静态型变量。使用全局变量传递数据的先后顺序的不同会影响计算结果,应用顺序不当,会导致错误,这种方式尽量少用。