【C语言-函数-调用函数】函数的调用——怎么让咱们盖好的工厂跑起来

开发技术 作者: 精灵星空 SKY315.COM 更新于: 浏览: 293
码字发文不易,转载请注明出处【精灵星空 SKY315.COM】

上节课咱们学了怎么盖工厂(定义函数),这节课咱们就来说说怎么让工厂开工干活——也就是函数的调用。

其实调用这事儿,你们早就干过了,只是咱们没往这儿想。你们从学C语言第一天起就在调用函数——printf是不是函数?scanf是不是函数?你们天天用,只是从来没想过“哦,原来这就是调用啊”。

调用函数的语法,简单到一句话就能说清:

函数名(要传给它的实际数据);

就这么简单。比如你上节课定义了一个加法函数int add(int a, int b),现在要让它干活,就这么写:

int x = add(3, 5);

你看,你叫了一声add,把3和5扔进去了,函数在里头算了半天,return了一个8出来,你把这个8存到了变量x里。

这整个过程,就是函数的调用。

咱们得把两个概念分清楚:形参和实参。

这两个词听着唬人,实际上特别好懂。

  • 形参:你定义函数的时候,括号里写的那几个变量,叫形式参数。它就是个“占位符”,告诉你这个工厂需要接收什么东西,但实际上它自己没值,得等别人给它。

  • 实参:你调用函数的时候,括号里传进去的那些值,叫实际参数。这才是真正要加工的数据。

举个例子。你定义函数的时候写的int add(int a, int b),里面的a和b就是形参——它俩就是个空盒子,等着被塞东西。

当你调用add(3, 5)的时候,3和5就是实参——实实在在的数据被塞进了a和b这两个盒子里。

调用的时候还有一个关键点:实参和形参要一一对应。

啥意思?

  • 第一个实参传给第一个形参

  • 第二个实参传给第二个形参

  • 依此类推

你得保证数量对得上——你定义了三个形参,结果调用的时候就给了俩,编译器直接报错。

你还得保证类型匹配——形参是int,你传个字符串进去,虽然编译器可能只给个警告,但结果多半是错的。

你写add(3, 5),3传给a,5传给b,一一对应,没毛病。

你写add(3, 5, 7),多了一个,不行。

你写add(3.5, 5.2),类型不对,虽然C语言会帮你自动转成整数,但小数部分就丢了,结果就不准了。

调用一个函数的时候,程序到底是怎么走的?这个得说清楚。

很多同学一开始不理解“跳转”这个概念——觉得程序是一行一行往下走的,怎么突然跑到别的地方去了?

其实函数的调用就是一次“跳出去再跳回来”的过程。

咱们用代码走一遍:

#include <stdio.h>

int add(int a, int b) {
    int result = a + b;
    return result;
}

int main() {
    int x = 3;
    int y = 5;
    int z = add(x, y);   // 在这里调用了add
    printf("%d\n", z);
    return 0;
}

执行流程是这样的:

  1. 程序从main开始跑。先定义了x=3,y=5。

  2. 走到int z = add(x, y);这一行的时候,程序跳到上面add函数那里去执行。

  3. 在add里,x的值3传给了a,y的值5传给了b。然后a+b算出8,存到result里,return result把8返回。

  4. 程序跳回到刚才调用add的地方,把返回的8赋值给z。

  5. 继续往下执行printf,输出8。

你看,程序执行到调用函数的时候,并不会“卡住”或者“停下来”,它是跳出去执行完再跳回来,从哪儿跳出去的,就回哪儿接着干。这就是函数调用的本质。

调用函数的方式,有几种花样,你得知道。

1. 把返回值赋给一个变量

这是最常见的。函数算完了给你一个结果,你拿个变量接住。

int z = add(3, 5);

2. 直接把返回值用在表达式里

不非得赋值,你可以直接拿返回值参与运算。

int z = add(3, 5) * 2 + 10;   // 先调add得8,然后8*2+10得26

3. 把函数调用作为另一个函数的参数

这是“套娃”玩法。add(add(1, 2), 3)——先算里面的add(1,2)得3,再算外面的add(3,3)得6。

int z = add(add(1, 2), 3);   // z = 6

4. 调用没有返回值的函数

如果函数返回值是void,那就不能把它当值来用。你只能单独写一行调用它。

printMenu();   // 直接调用,没有返回值可以接收

你不能写int x = printMenu();,因为printMenu啥也没返回,赋值没意义,编译器会报错。

还有一个很重要的概念叫函数的嵌套调用。

啥意思?就是在函数里面调用别的函数。你可以在main里调用add,也可以在add里调用别的函数。这就像工厂里面还能再盖个小车间一样,是完全允许的。

举个栗子:

int square(int a) {
    return a * a;   // 算平方
}

int add_and_square(int a, int b) {
    int sum = add(a, b);     // 先调用add算和
    return square(sum);      // 再调用square算平方
}

你调用add_and_square(3, 4)的时候:

  1. 先算add(3,4)得7

  2. 再算square(7)得49

  3. 返回49

这里面add_and_square嵌套调用了add和square两个函数。这就是函数的嵌套调用,在写复杂程序的时候极其常见。

对了,还有一个知识点叫函数声明,很多同学第一次看到的时候一脸懵。

你看这段代码,猜猜能不能编译通过?

#include <stdio.h>

int main() {
    int z = add(3, 5);
    printf("%d\n", z);
    return 0;
}

int add(int a, int b) {
    return a + b;
}

你可能会觉得“这有啥问题?add定义在后面,但在前面调用了,不行吗?”

答案是:不行。 C语言要求“先声明后使用”。你在main里调用add的时候,编译器还不知道add是啥玩意儿,它就懵了。

解决方法有两种:

方法一:把add的定义写在main前面。

int add(int a, int b) {
    return a + b;
}

int main() {
    int z = add(3, 5);   // 编译器知道add长啥样了
    printf("%d\n", z);
    return 0;
}

方法二:在前面写个函数声明(也叫函数原型)。

函数声明就是把函数头单独拎出来,加个分号,告诉编译器“有这个函数,它的样子是这样的,具体的定义在后面。”

int add(int a, int b);   // 声明:告诉编译器有这么一个函数

int main() {
    int z = add(3, 5);   // 编译器知道add的存在了
    printf("%d\n", z);
    return 0;
}

int add(int a, int b) {  // 定义:真正实现这个函数
    return a + b;
}

函数声明的作用,就是提前告诉编译器函数的“签名”——叫什么名、接收啥参数、返回啥类型。这样编译器在遇到调用的时候就不会懵了。

在实际开发中,函数声明通常放在头文件(.h文件)里,而函数定义放在源文件(.c文件)里。但现在你刚学,知道有这么回事就行,别被吓到。

最后总结一下这节课的核心:

函数的调用,就是通过函数名去执行函数体里的代码,执行完再回到调用处继续往下走。调用的时候,实参传给形参,一一对应;返回值用变量接住,或者直接用。如果函数定义在调用处的后面,需要在前面加一个函数声明。

好了,同学们自己练练。把上节课写的那些函数都在main里调一遍,看看结果对不对。学会“调用”,函数才真正活起来。