通过调试来理解形参与实参的区别
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊过,尤其是遇到形参名和实参名一样时,更加晕头转向,出现一种“是谁把值传给了我,而我又传给了谁”的疑惑。我也有过类似的迷茫,更是被其他初学的同学问过很多次。想了好久还是决定整理一下,把它写成博客供更多人学习,而且是通过调试这一种直观的方式。下面进入本次的正题,不足之处请多多包涵,欢迎各位大佬在评论区补充有关知识。
首先我们来看一下二者的区别和特点:
一、形参和实参区别:
(1)函数定义方式不同
形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。
实参出现在主调函数中,进入被调函数后,实参变量也不能使用。
(2)使用原理不同
函数的形参列于函数声明中,在函数定义的函数体内使用。当函数调用时,形参(任何种类的)是一类将被填充的空白或是占位符。
实参是用来填充形参的。当函数被调用时,形参列在函数名后面的括号里。执行函数调用时,实参被传递给形参。
(3)传值调用和引用调用不同
传值调用和引用调用指的是用于参数传递过程中的一种机制。传值调用中,只使用了实参的值。
传值调用机制里,形参是一个局部变量,其初始值为相应实参的值。在引用调用机制里,将实参的地址传递给形参,从表面上看是以实参变量取代形参,因此任何发生在形参上的改变实际上都发生在实参变量上。
二、形参和实参的特点:
(1)形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。
(2)实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值、输入等办法使参数获得确定值。
(3)实参和形参在数量上,类型上、顺序上应严格一致,否则就会发生类型不匹配的错误。
然后再来看看这次用到的源代码:
1 #include<stdio.h> 2 3 void swap1(int x, int y) { //传值调用 4 int temp = x; 5 x = y; 6 y = temp; 7 } 8 9 void swap2(int *x, int *y) { //引用调用 10 int temp = *x; 11 *x = *y; 12 *y = temp; 13 } 14 15 void init1(int x[]) { //引用调用 16 int i; 17 for(i = 0; i < 10; ++i) 18 x[i] = i; 19 } 20 21 int main() { 22 int a = 1, b = 5, i; 23 int c[10]; 24 int *p, *q; 25 p = &a; 26 q = &b; 27 28 swap1(a, b); 29 printf("经swap1函数处理后a = %d, b = %d\n", a, b); 30 31 swap2(p, q); 32 printf("经swap2函数处理后a = %d, b = %d\n", a, b); 33 34 init1(c); 35 printf("使用init函数对数组c赋值后结果如下:\n"); 36 for(i = 0; i < 10; ++i) 37 printf("%d ", c[i]); 38 39 return 0; 40 }
然后是具体的调试情况:
使用Dev-C++第一次调试之前需要先进行配置,首先打开编译选项:
之后选择“代码生成/优化”这一项,接着选择“连接器”一项,把其中的“产生调试信息”一项的NO改为YES,然后点击确认就可以啦,具体情况如下:
配置完毕,我们首先将断点设置在swap1函数处(点击代码行所在的序号即可设置断点),如图:
然后开始进行调试(注意:调试前需要先对源码进行编译),操作及介绍如下:
其中“添加查看”代表添加要观察的变量,“下一步”即在本函数内执行下一步,如果想查看它调用的函数的执行情况,可在执行到调用函数这一步时选择“单步进入”(本例中,刚开始就要点击单步进入,观察函数swap1的内部执行情况),其他选项同其它们的字面意思一样。
本次观察对象为变量a,b,x,y的值以及他们的地址,具体的添加操作如下(点击右下角添加查看后输入要查看的变量名,然后回车确认):
待查看的变量会显示在左上角,本次查看的变量如图(Execute to evaluate代表变量的值还无法知晓):
执行单步进入后结果如下:
不难发现,传值调用的情况下,x,y只是复制了a,b的值。它们在内存中的位置(地址)是完全不同的,所以传值调用情况下形参的值发生变化并不影响实参,输出结果也证实了我们的结论:a,b的值并没有发生变化
同理,我们来通过调试来观察swap2函数的内部执行情况,本次观察p,q和x,y的值:
代码中,p和q为int类型的指针,分别指向a和b的地址。从上图可以看到引用调用的情况下我们传递的地址被x和y复制,所以我们对x和y进行的操作都会反映到原来的变量a和b上,输出结果也能证明我们的猜测:
上面的例子都是简单的单个变量传参,那么数组作为参数时是如何传递的呢?还是来调试一下:
结果很明显:数组作为参数传递时,也是引用调用方式,传的是地址。所以把数组作为参数传递后,数组在函数内发生的变化都会被保留下来。所以各位在设计函数时务必注意执行完某个函数需不需要实参变量发生变化,根据情况选择传值调用还是引用调用方式。讲的不足的地方还请见谅,欢迎各位批评指正!