10. 函数(一):函数定义与声明、函数参数传递、return语句、函数原型
函数这东西,说白了就是C语言的积木块。你把一段逻辑封装起来,给它起个名字,以后想用的时候直接喊它一声就行。我刚开始学的时候,总觉得写函数麻烦,所有代码都往main里塞。后来项目一复杂,自己都看不懂自己写的啥了……嗯,从那以后,我养成了“功能模块化”的习惯。
10.1 函数的定义与声明
先说说定义。函数定义就是告诉编译器:“嘿,我这有个功能模块,它长这样,你记好了。” 格式其实挺固定的:
返回值类型 函数名(参数列表) {
// 函数体
// 各种操作
return 返回值;
}
举个例子,写一个计算两个整数和的函数:
int add(int a, int b) {
int sum = a + b;
return sum;
}
这里 int 是返回值类型,add 是函数名,int a, int b 是参数列表。函数体里做了加法,最后用 return 把结果送回去。
那声明又是什么?声明就是提前打个招呼:“注意了,后面我会用到这个函数,它长这样,别到时候不认识。” 声明通常放在文件开头或者头文件里:
int add(int a, int b); // 这就是函数原型
我个人习惯,不管函数定义在main前面还是后面,都会先写声明。为什么?因为这样代码结构清晰,别人一看就知道这个文件里有哪些“积木块”。
核心区别:定义是“造积木”,声明是“告诉别人有这块积木”。定义只能有一次,声明可以有多次。
10.2 函数原型
函数原型其实就是函数的“身份证”。它包含了函数名、返回值类型、参数类型和个数。编译器看到原型,就知道怎么调用这个函数了。
原型长这样:
返回值类型 函数名(参数类型1, 参数类型2, ...);
注意,原型里可以只写参数类型,不写参数名。比如:
int add(int, int); // 合法,编译器只关心类型
但我建议你写上参数名。为什么?因为可读性更好。你想想看,int add(int, int) 和 int add(int a, int b),后者一看就知道是加两个数。
注意:如果函数定义在调用之后,而且没有提前声明,编译器会报错或者给出警告。我曾经在项目里吃过这个亏——函数定义写在文件末尾,调用写在前面,结果编译报了一堆“隐式声明”的错。从那以后,我写任何函数都先写原型。
10.3 函数参数传递
参数传递,说白了就是“怎么把数据交给函数”。C语言里只有一种传递方式:值传递。嗯,你没看错,只有这一种。
值传递的意思是:调用函数时,把实参的值复制一份给形参。函数内部操作的是副本,不影响原来的变量。
#include <stdio.h>
void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
printf("函数内部:x=%d, y=%d\n", x, y);
}
int main() {
int a = 5, b = 10;
printf("调用前:a=%d, b=%d\n", a, b);
swap(a, b);
printf("调用后:a=%d, b=%d\n", a, b);
return 0;
}
运行结果:
调用前:a=5, b=10
函数内部:x=10, y=5
调用后:a=5, b=10
看到了吗?函数内部确实交换了,但main里的a和b纹丝不动。这就是值传递——你给函数的是复印件,原件还在自己手里。
小技巧:如果想在函数内部修改外部变量,怎么办?用指针。把变量的地址传进去,函数通过地址操作原数据。这个后面会详细讲,先记住这个思路。
10.4 return语句
return 的作用有两个:一是返回结果,二是结束函数执行。格式很简单:
return 表达式;
或者:
return;
如果函数返回值类型是 void,那就用第二种,表示“我不返回任何东西,只是结束”。
举个例子:
int max(int a, int b) {
if (a > b) {
return a; // 如果a大,直接返回a,函数结束
}
return b; // 否则返回b
}
这里有两个 return,但每次只会执行一个。函数执行到第一个 return 就结束了,后面的代码不会执行。
避坑指南:我曾经写过这样的代码:
int func() {
int x = 10;
// 忘记写return了
}
结果函数返回了一个不确定的值。因为函数没有 return,它会返回栈上残留的随机数。这种bug特别难找,因为有时候“运气好”返回的值刚好是对的,有时候又不对。所以,非void函数一定要保证所有路径都有 return。
10.5 知识体系总览
下面这张图,把本章的核心知识点串起来了。你可以把它当作一个“函数入门地图”:
10.6 综合示例
最后,来个完整的例子,把今天讲的东西串起来:
#include <stdio.h>
// 函数原型声明
int add(int a, int b);
void print_result(int x, int y, int sum);
int main() {
int num1 = 15, num2 = 25;
int result = add(num1, num2); // 调用add函数
print_result(num1, num2, result);
return 0;
}
// 函数定义:加法
int add(int a, int b) {
return a + b;
}
// 函数定义:打印结果
void print_result(int x, int y, int sum) {
printf("%d + %d = %d\n", x, y, sum);
}
运行结果:
15 + 25 = 40
这个例子虽然简单,但包含了函数声明、定义、参数传递、return语句所有要素。你想想看,如果以后要写一个计算器程序,是不是可以把加减乘除分别写成函数?然后main里只需要调用就行了。代码瞬间变得清爽。
我的建议:刚开始学函数,别怕多写。哪怕只有两行逻辑,也值得封装成一个函数。养成这个习惯后,你会发现调试和阅读代码都轻松很多。
公众号:蓝海资料掘金营,微信deep3321