形参、实参与参数传递 | Formal and Actual Parameters, Value Passing
写在前面:参数与参数传递也是函数的重要组成部分,且理解起来有一定难度。作为函数章节的重难点内容,理解好参数的用途对后续的学习至关重要。
一、引言:参数是什么?
我们在上一节讲返回值的时候举过这样一个例子:
比如,计算圆的面积:
float calculateArea() {
float pi = 3.14159;
float radius = 5.0;
float area = pi * radius * radius;
return area; // 把结果“交出去”
}
这里我们定义一个函数calculateArea()用以计算一个圆的面积,圆的半径radius在函数体内声明,并规定为5.0。
这样便存在一个问题:如果我们希望在主函数的不同地方,计算不同半径的圆的面积,那是否意味着原本意图是减少重复代码的出现的函数,因为功能是固定的(指圆的半径在定义时规定好)而不再适用?
理想的情况是:如果在主函数里有多处计算圆面积的需求,但半径不同。我们希望有这样一个函数:我们可以“告诉”它对应圆的半径,它“返回”圆的面积。
这样,函数从我们最开始学习的 “不问不答” 的void()函数变为 具有灵活性的“既问又答” 的带有返回值的函数。这是利用什么实现的呢?
这时,参数便发挥它的作用。我们利用下面一个例子来简单认识参数的作用:
还记得我们在数学课上学过的函数吗?比如:
f(x) = x² + x + 1
这里,
- "f" 是函数的名字
- "x" 是自变量,也就是我们想让函数处理的数据
- 每次带入不同的 x,就能得到不同的结果:
- f(2) = 2² + 2 + 1 = 7
- f(5) = 5² + 5 + 1 = 31
在编程中,函数也可以“接受”外部的数据,这些数据就叫参数。
参数让函数变得灵活,可以处理不同的输入,给出不同的结果。
二、形参和实参的概念
- 形参(形式参数):函数定义时的“形式表达”,比如数学中,函数里的“x”。
- 实参(实际参数):函数调用时带进去的真实数值,比如 f(2) 里的“2”。
这里是我们第一次接触到“形式”与“实际”的概念,我们必要地在这里做出一些说明:
在上面的例子f(x) = x² + x + 1中,x这一变量不代表任何的实际数据。换句话说,不论x最后带入的时什么值,它只规定了在函数f(x)运算时,对x的逻辑操作:对x平方,再加上x,再加1。对于这样在函数定义中使用的变量x,我们说它是 形式(Formal) 的。
与之相反,在使用函数f(x),例如计算f(1)、f(-2)、f(a)(a是一个声明了的变量)时,1、-2、a这些量,是实际(Actual)的。
总结一下:
f(x) 是定义时的函数表达式,x是形参。
f(3) 是调用时的实际计算,3是实参。
在编程里也是一样。我们通过一个和数学中f(x) = x² + x + 1相似的例子来学习参数的使用:
三、编程例子:模拟数学函数
1. 定义一个带有参数的函数
int f(int x) { // 这里的 int x 是形参
return x * x + x + 1;
}
要定义一个带有参数的函数,与无参数函数不同的是:我们需要在函数声明的圆括号()中,声明函数的参数。
声明函数的参数与声明变量的语法数据类型 变量名类似:
返回值类型 函数名(参数1数据类型 参数1变量名, 参数2数据类型 参数2变量名, ...) {
// 函数体
// ...
}
2. 调用函数
要调用一个含参函数,与调用无参数函数不同的是:在调用时,我们需要在圆括号里给出与函数定义相匹配的参数。例如对于上面定义的int f(int x) {...}函数:
int result = f(3); // 这里的 3 是实参
printf("f(3) = %d\n", result); // 输出 f(3) = 13
可以多次调用这个函数,传入不同的实参:
printf("f(2) = %d\n", f(2)); // 输出 f(2) = 7
printf("f(5) = %d\n", f(5)); // 输出 f(5) = 31
调用含参函数时,我们给出的实际参数数量、顺序、数据类型需要和函数定义中确定的形式参数相一致。
3. 参数传递流程
- 定义时,函数写成
int f(int x),表示以后要处理一个“x”。 - 调用时,比如
f(2),就把“2”传给“x”,函数内部用这个“x”做运算。 - 每次调用传入的实参不同,返回的结果也不同。
四、多参数情况
如果我们想表示更复杂的数学式子,比如 f(x, y) = x² + y
int f(int x, int y) { // 两个形参
return x * x + y;
}
int main() {
printf("f(2, 3) = %d\n", f(2, 3)); // 输出 7
printf("f(5, 1) = %d\n", f(5, 1)); // 输出 26
return 0;
}
五、参数传递的特点: “值传递”性
我们看这样一个例子:
#include <stdio.h>
int func(int x){
x = 2;
return 3;
}
int main() {
int n = 5;
func(n);
printf("%d", n);
return 0;
}
在这个例子里,我们定义了一个函数func(int x),它以一个整型变量x为参数,执行的功能是把x赋值为2,并最后返回3。
我们在主函数中定义一个整型变量n,并赋值为5,接着使用func(n);调用这个含参函数,最后输出变量n的值。
思考这个值会是几呢?
A. 3,因为函数返回值为3
B. 2,因为n被带入x,而x最后赋值为2
C. 5,因为n自始至终没有被重新赋值
答案是C。
A是显然错误的,这是因为我们使用func(n);调用时,没有对返回值进行赋值等操作,函数执行结束后返回值3就被舍弃了。
至于B、C孰对孰错,则涉及到参数传递的“值传递”性
简单来说:
- 编程时传递的参数是“值传递”,在实际参数
n传入函数x的过程中,并不是在对变量n做函数定义中的各种操作;而是“新建”一个变量x,把n的值赋值给x,对x进行各种操作。因此参数传递时,实参不会因函数内部的形参而改变。
在这个例子里,实参n=5被传入函数func的过程,实际上是“新建”一个变量x,让x=n赋值为5。执行函数体的语句时,也是对x赋值为2,并返回3。
因此我们容易发现,由于x的值没有被return实际利用,x = 2即使赋值也被舍弃了。更何况我们也没有对返回值3进行操作,也被舍弃了。变量n在在函数执行结束后,没有变动,仍然为5。
六、练习与思考
- 定义一个函数,输入a, b, c, x,计算 ax² + bx + c 的值,并调用它输出结果。
- 定义一个函数,输入姓名和年龄,输出一句问候语。
- 思考:如果没有参数,函数有什么局限?有了参数能带来哪些灵活性?
七、小结
- 数学里的自变量,就是编程里的“参数”
- 形参是函数定义时的“占位”,实参是调用时的真实数据
- 参数让函数可以处理不同的输入,变得更灵活
下一节预告:后续还会介绍“值传递与引用传递”,以及如何用参数让函数修改外部变量。现在,先掌握最基础的参数传递!



Comments | NOTHING