
大家好!今天我们来聊聊C语言中两个非常重要的概念:函数和递归。
那我们开始吧!
数学中我们其实就⻅过函数的概念,⽐如:⼀次函数 y=kx+b ,k和b都是常数,给⼀个任意的x,就得到⼀个y值。
其实在C语⾔也引⼊函数(function)的概念,有些翻译为:⼦程序,⼦程序这种翻译更加准确⼀些。 C语⾔中的函数就是⼀个完成某项特定的任务的⼀⼩段代码。
那我们为什么要用函数呢?
想象一下,如果你要写一个很长的程序,里面有好多重复的操作,比如计算两个数的和、打印欢迎信息等。每次都把代码写一遍,不仅麻烦,还容易出错。这时候,函数就派上用场了!
函数就像一个“小工厂”。你给它一些“原料”(输入),它帮你完成特定的任务,然后把“成品”(输出)交给你。这样,下次再需要做同样的事情时,直接“召唤”这个小帮手就行了,不用再重复写代码。

一个函数通常长这样:
返回值类型 函数名(参数列表)
{
函数体(要做的事情)
return 返回结果(把结果交上去)
}int)、小数(float)、字符(char),还是什么都不给(void)?void,可以没有return,或者只用return;表示结束举个例⼦:
写⼀个加法函数,完成2个整型变量的加法操作。
#include<stdio.h>
//定义一个加法函数
int add(int a,int b) //这里的a,b就是形参
{
return a + b;
}
int main()
{
int n =0;
int m =0;
scanf("%d %d",&m,&n);
int result = add(n,m); //这里的m,n就是实参,调用add函数
printf("%d\n",result);
return 0;
}在这里,a和b是形参,它们在函数add内部代表传进来的两个数。m和n是实参,是我们在main函数里真正要相加的两个数。
函数的参数部分需要交代清楚:参数个数,每个参数的类型是啥,形参的名字叫啥。
上⾯只是⼀个例⼦,未来我们是根据实际需要来设计函数,函数名、参数、返回类型都是可以灵活变化的
值传递(Pass by Value)
关系详解:

总结一句话:
实参是“数据”,形参是“容器”;调用时,数据装进容器,函数用容器里的数据干活。
• return后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式,再返回表达式的结果。
• return后边也可以什么都没有,直接写 return; 这种写法适合函数返回类型是void的情况。
• return返回的值和函数返回类型不⼀致,系统会⾃动将返回的值隐式转换为函数的返回类型。
• return语句执⾏后,函数就彻底返回,后边的代码不再执⾏。
• 如果函数中存在if等分⽀的语句,则要保证每种情况下都有return返回,否则会出现编译错误。
在 C 语言中,数组作为函数参数是一个重要且容易混淆的知识点。它的核心机制是:数组名作为实参传递时,实际上传递的是数组首元素的地址(即指针),而不是整个数组的副本。
void func(int arr[]); // 形式1:不指定大小
void func(int arr[10]); // 形式2:指定大小(编译器忽略)
void func(int *arr); // 形式3:指针形式(最本质)这三种写法在函数参数中完全等价! 编译器都会将其视为
int *arr。
int main() {
int a[5] = {1, 2, 3, 4, 5};
func(a); // 传递数组名 → 实际上传递的是 &a[0]
return 0;
}数组名
a在大多数情况下代表首元素地址&a[0](类型为int*)
void func(int arr[]) {
printf("%zu\n", sizeof(arr)); // 输出的是指针大小(如 8 字节),不是数组总大小!
}✅ 解决方法:必须额外传递数组长度
void printArray(int arr[], int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int a[] = {10, 20, 30, 40, 50};
int len = sizeof(a) / sizeof(a[0]); // 只能在定义数组的作用域内这样计算
printArray(a, len);
return 0;
}void modify(int arr[], int len) {
for (int i = 0; i < len; i++) {
arr[i] *= 2; // 直接修改原数组内容
}
}
int main() {
int a[] = {1, 2, 3, 4, 5};
modify(a, 5);
// a 现在是 {2, 4, 6, 8, 10}
return 0;
}int* badFunc() {
int arr[5] = {1, 2, 3, 4, 5};
return arr; // ❌ 错误!返回局部数组地址,函数结束后内存被回收
}✅ 正确做法:
递归听起来很玄乎,其实很简单。递归就是一个函数在执行过程中,直接或间接地调用自己。就像俄罗斯套娃,一层套一层。
递归的基本原理是将一个大问题分解为一个或多个更小的问题,然后通过调用自身来解决这些更小的问题,直到达到基本情况,即不再需要递归调用的情况
这里我们写了一个简单的递归函数:
#include <stdio.h>
void daz(int n)
{
printf("%d\n",n);
daz(n+1);
}
int main()
{
daz(0);
return 0;
}运行一下:

这个时候我们发现,程序陷入了无限死循环,此时按 ctrl + L 可以停止
那么为什么会死循环呢?看一下这个流程图

我们通过这个流程图可以发现,一旦这个程序开始运行,进入蓝色方框后就出不来了,于是就一直转下去,陷入了死循环
那么我们如何避免死循环呢?即
如何正确的递归:两要素
阶乘的定义:
#include <stdio.h>
int factorial(int n) {
// 1. 递归基:最简单的情况
if (n == 0) {
return 1; // 0的阶乘是1
}
// 2. 递归关系:把问题变小
else {
return n * factorial(n - 1); // 调用自己计算(n-1)!
}
}
int main() {
int n = 5;
int result = factorial(n);
printf("%d! = %d\n", n, result); // 输出: 5! = 120
return 0;
}执行过程模拟 (n=3时):
factorial(3)调用 -> 因为3!=0,执行3 * factorial(2)factorial(2)调用 -> 因为2!=0,执行2 * factorial(1)factorial(1)调用 -> 因为1!=0,执行1 * factorial(0)factorial(0)调用 -> 因为0==0,直接返回1(递归基触发!)1 * 1 = 12 * 1 = 23 * 2 = 6斐波那契数列:1, 1, 2, 3, 5, 8, 13...
#include <stdio.h>
int fibonacci(int n) {
// 递归基
if (n == 1 || n == 2) {
return 1;
}
// 递归关系
else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
int main() {
int n = 6;
int result = fibonacci(n);
printf("第%d个斐波那契数是: %d\n", n, result); // 输出: 第6个斐波那契数是: 8
return 0;
}很多递归能解决的问题,用循环也能解决。它们各有千秋:

选择建议:
恭喜你!看到这里,你已经掌握了C语言函数和递归的核心知识。
函数和递归是C语言乃至所有编程语言的基础。多加练习,动手写代码,你一定能熟练运用它们,写出更优秀的程序!加油!