上节课咱们学了怎么盖工厂(定义函数),这节课咱们就来说说怎么让工厂开工干活——也就是函数的调用。
其实调用这事儿,你们早就干过了,只是咱们没往这儿想。你们从学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; }
执行流程是这样的:
-
程序从
main开始跑。先定义了x=3,y=5。 -
走到
int z = add(x, y);这一行的时候,程序跳到上面add函数那里去执行。 -
在
add里,x的值3传给了a,y的值5传给了b。然后a+b算出8,存到result里,return result把8返回。 -
程序跳回到刚才调用
add的地方,把返回的8赋值给z。 -
继续往下执行
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)的时候:
-
先算
add(3,4)得7 -
再算
square(7)得49 -
返回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里调一遍,看看结果对不对。学会“调用”,函数才真正活起来。