2.3 形参、实参与参数传递 | Formal and Actual Parameters, Value Passing

发布于 2025-09-20  636 次阅读


形参、实参与参数传递 | 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-2a这些量,是实际(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就被舍弃了。
至于BC孰对孰错,则涉及到参数传递的“值传递”性

简单来说:

  • 编程时传递的参数是“值传递”,在实际参数n传入函数x的过程中,并不是在对变量n做函数定义中的各种操作;而是“新建”一个变量x,把n的值赋值给x,对x进行各种操作。因此参数传递时,实参不会因函数内部的形参而改变。

在这个例子里,实参n=5被传入函数func的过程,实际上是“新建”一个变量x,让x=n赋值为5。执行函数体的语句时,也是对x赋值为2,并返回3

因此我们容易发现,由于x的值没有被return实际利用,x = 2即使赋值也被舍弃了。更何况我们也没有对返回值3进行操作,也被舍弃了。变量n在在函数执行结束后,没有变动,仍然为5

六、练习与思考

  1. 定义一个函数,输入a, b, c, x,计算 ax² + bx + c 的值,并调用它输出结果。
  2. 定义一个函数,输入姓名和年龄,输出一句问候语。
  3. 思考:如果没有参数,函数有什么局限?有了参数能带来哪些灵活性?

七、小结

  • 数学里的自变量,就是编程里的“参数”
  • 形参是函数定义时的“占位”,实参是调用时的真实数据
  • 参数让函数可以处理不同的输入,变得更灵活

下一节预告:后续还会介绍“值传递与引用传递”,以及如何用参数让函数修改外部变量。现在,先掌握最基础的参数传递!


这里是 /* Huajidawang */ 的个人主页