C语言入门教程(第6讲):函数——让程序学会“分工合作”的魔法
🌱 文章摘要
写程序就像建城市——
变量是建筑,循环是道路,而函数,是城市的“部门”。
它让你的代码学会“分工合作”,不再混乱无章。
本讲带你彻底掌握函数的定义、参数、返回值与作用域,
让C语言从“能跑”变成“能管理”。
📘 导读
到目前为止,我们的程序能输入、能循环、能存数据。
但是不是越来越“长”,甚至有点“乱”?一眼望去,全是printf和scanf堆在一起。
这时候,就需要函数登场了——
它能把程序“切块”,让每一块只干一件事。在这一讲中,我们将:
理解函数的核心思想;
学会自己定义函数;
掌握参数、返回值与作用域;
明白库函数、自定义函数的区别;
体验真正“模块化编程”的感觉。
准备好了吗?让我们一起打开C语言的“逻辑引擎”!
📘 建议收藏本专栏:后续章节会持续更新,
包括控制语句、函数、指针、数组、文件操作等完整体系。如果你能坚持学完,你会发现——
C语言不仅是工具,更是理解计算机世界的第一扇门。
目录
一、为什么要有函数
二、函数的结构
2.1 调用流程
三、函数的分类
3.1 库函数:C语言的“工具箱”
3.2 自定义函数:你的“逻辑工厂”
四、形参与实参:函数的“传话机制”
4.1 示例:形参与实参
4.2 示例:交换失败的经典陷阱
五、return:函数的“快递出口”
5.1 示例:有返回值的函数
5.2 示例:无返回值的函数
六、数组作为函数参数:批量任务的传递方式
6.1 示例:打印数组
6.2 示例:修改数组
七、实战:用函数做一个“迷你计算器”
7.1 项目:命令行计算器
八、函数声明与多文件协作
8.1 示例:先声明,后使用
九、static 与 extern:函数的“活动范围”
9.1 static:只在本文件可见
9.2 extern:跨文件访问
十、总结
十一、下一讲预告
11.1 下一讲预告
11.2 提前准备建议
一、为什么要有函数
先来想一个小故事:
你是一个程序员,在做一款“学生成绩管理系统”。
你要输入成绩、求平均值、求最大值、输出结果。
结果写完的 main 函数大概长这样:
int main() {int a, b, c;scanf("%d%d%d", &a, &b, &c);int max = (a > b ? (a > c ? a : c) : (b > c ? b : c));float avg = (a + b + c) / 3.0;printf("最高分:%d\n平均分:%.2f\n", max, avg);return 0;
}
能跑,但很“糟糕”:
逻辑混在一起;
不可复用;
一改一个错。
于是,C语言告诉你:“让每件事,都交给一个人去做。”
那个人,就是函数。
定义:
函数(Function)是一段可以被多次调用的代码,用于完成特定任务。
你可以理解为:
main 是“老板”,函数是“员工”,
老板发任务,员工干活,干完再汇报结果。
二、函数的结构
先看一个最经典的例子:
#include <stdio.h>// 定义函数
int add(int x, int y) {return x + y;
}int main() {int result = add(3, 5); // 调用函数printf("结果为:%d\n", result);return 0;
}
输出:
结果为:8
函数结构拆解:
返回值类型 函数名(参数类型 参数名, ...) {函数体语句;
}
部分 | 含义 |
---|---|
int | 返回一个整数 |
add | 函数名称 |
(int x, int y) | 参数(形参) |
return x + y; | 返回结果 |
2.1 调用流程
main 调用 add;
参数 3、5 传给 x、y;
add 执行完毕;
return 把结果送回 main;
main 输出结果。
函数就是“任务模块”,写一次,用很多次。
三、函数的分类
C语言中的函数可以分为两类:
类型 | 示例 | 说明 |
---|---|---|
库函数 | printf, scanf, strlen, sqrt | 系统自带 |
自定义函数 | add, max, print_sum | 用户编写 |
3.1 库函数:C语言的“工具箱”
这些函数早已写好,我们只需调用。
示例:
#include <math.h>
double result = sqrt(9.0);
printf("%.2f\n", result);
输出:
3.00
常见库:
头文件 | 常见函数 | 功能 |
---|---|---|
stdio.h | printf, scanf | 输入输出 |
math.h | sqrt, pow | 数学运算 |
string.h | strlen, strcpy | 字符串处理 |
stdlib.h | rand, atoi | 工具函数 |
3.2 自定义函数:你的“逻辑工厂”
当库函数不够用时,就自己造!
int max(int a, int b) {return (a > b ? a : b);
}
调用:
printf("最大值为:%d\n", max(10, 20));
输出:
最大值为:20
库函数是C语言的“现成工具”,自定义函数是你的“创造力体现”。
四、形参与实参:函数的“传话机制”
在现实世界里,老板(main函数)给员工(子函数)下任务时,总得告诉他“干啥”和“数据是多少”。
在C语言里,这种“任务沟通”靠的是——参数传递。
4.1 示例:形参与实参
#include <stdio.h>void print_sum(int a, int b) { // 形参:占位符printf("两数之和:%d\n", a + b);
}int main() {int x = 10, y = 20;print_sum(x, y); // 实参:实际值return 0;
}
输出:
两数之和:30
解释:
-
形参:函数定义时用来接收值的“容器”;
-
实参:函数调用时传入的实际值。
调用时,实参的值会被“复制”给形参,函数内部修改形参,不会影响外部的实参。
4.2 示例:交换失败的经典陷阱
void swap(int a, int b) {int temp = a;a = b;b = temp;
}int main() {int x = 3, y = 5;swap(x, y);printf("%d %d\n", x, y);return 0;
}
输出:
3 5 // 没有交换成功
原因:
函数内部的
a
、b
只是“副本”。
真正的x
、y
在外面安然无恙。
C语言的参数默认是“值传递”。
想真正交换,需要传递地址(指针篇将讲)。
五、return:函数的“快递出口”
函数干完活,总得告诉调用者结果。
这个“出口”就是 return
。
5.1 示例:有返回值的函数
int add(int a, int b) {return a + b; // 返回结果
}int main() {int r = add(5, 7);printf("结果是:%d\n", r);return 0;
}
输出:
结果是:12
5.2 示例:无返回值的函数
void say_hi() {printf("Hello, C!\n");
}int main() {say_hi(); // 不需要接收返回值return 0;
}
输出:
Hello, C!
总结:
-
有返回值:带回数据;
-
无返回值:只执行操作;
-
return
语句执行后函数立即结束; -
一个函数只能有一个返回类型。
return
是函数与外界交流的“语言”。
没有它,函数就像一个“沉默的打工人”。
六、数组作为函数参数:批量任务的传递方式
有时候我们需要让函数处理整组数据,例如统计成绩或查找最大值。
这时我们可以把“数组”整个传给函数。
6.1 示例:打印数组
#include <stdio.h>void print_array(int arr[], int n) {for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() {int nums[5] = {1, 2, 3, 4, 5};print_array(nums, 5);return 0;
}
输出:
1 2 3 4 5
说明:
数组名在传参时会自动转为“指针”。
函数内部访问的是原数组内容。
6.2 示例:修改数组
void double_array(int arr[], int n) {for (int i = 0; i < n; i++) {arr[i] *= 2;}
}int main() {int a[3] = {1, 2, 3};double_array(a, 3);for (int i = 0; i < 3; i++) printf("%d ", a[i]);
}
输出:
2 4 6
结论:
函数中修改数组会影响原数据。
因为传递的是“地址”,不是“副本”。
数组参数让函数处理大规模数据,是通向“指针世界”的第一个入口。
七、实战:用函数做一个“迷你计算器”
光讲理论没意思,来点“真刀真枪”的代码吧。
7.1 项目:命令行计算器
目标:输入表达式 3 + 5
,自动输出结果。
#include <stdio.h>int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }int main() {int x, y;char op;printf("请输入表达式(如 3 + 5):");scanf("%d %c %d", &x, &op, &y);switch(op) {case '+': printf("结果:%d\n", add(x, y)); break;case '-': printf("结果:%d\n", sub(x, y)); break;case '*': printf("结果:%d\n", mul(x, y)); break;case '/': printf("结果:%d\n", divide(x, y)); break;default: printf("暂不支持该运算符。\n");}return 0;
}
输出示例:
请输入表达式(如 3 + 5):8 * 9
结果:72
分析:
-
每个函数只干一件事;
-
main 函数只负责“分工”;
-
代码结构清晰、逻辑分明。
当你能用函数写出这样的程序,就说明你真的理解了“模块化思维”。
八、函数声明与多文件协作
在大型项目中,代码常分散在多个文件。
C语言要求函数在使用前必须声明。
8.1 示例:先声明,后使用
#include <stdio.h>int max(int a, int b); // 声明int main() {printf("%d\n", max(10, 20));return 0;
}int max(int a, int b) { // 定义return (a > b ? a : b);
}
说明:
声明告诉编译器“函数长什么样”;
定义告诉它“函数怎么做”。
九、static 与 extern:函数的“活动范围”
这两个关键字决定函数能在哪被访问。
9.1 static:只在本文件可见
static void say_hi() {printf("Hi, I'm a static function!\n");
}
用途:
-
防止命名冲突;
-
常用于内部实现函数。
9.2 extern:跨文件访问
// file1.c
int num = 100;// file2.c
extern int num;
printf("%d\n", num);
用途:
让不同源文件之间共享数据或函数。
static
管内部,extern
管外部。
它们是多文件工程的基础。
十、总结
这一讲,我们正式掌握了C语言的“灵魂武器”——函数。
你现在已经会:
-
定义函数、理解参数;
-
使用 return 返回结果;
-
让数组作为参数传递;
-
用函数构建小型项目;
-
掌握 static / extern 的作用。
到此为止,你的C语言不再是“顺序执行”,而是“逻辑分层”的程序。
十一、下一讲预告
11.1 下一讲预告
《C语言入门教程(第7讲):数组和函数实践——扫雷游戏》
这一讲,你将真正把前六讲学到的知识“融会贯通”。
我们要用数组存储棋盘,用函数管理逻辑,
一步步实现一个可玩的扫雷游戏!主要内容包括:
扫雷游戏的功能分析与模块设计
9×9 棋盘与 10 个雷的随机布置
数组越界的处理与扩展技巧
函数模块化:初始化、布雷、排雷、显示
多文件结构(.h、.c)的实战分工
扫雷游戏的完整代码与扩展玩法
这一讲不仅是数组与函数的实践,更是你第一次亲手写出一个完整程序的时刻。
从这里开始,C语言不再只是语法,而是“创造力”的工具。
11.2 提前准备建议
强烈建议提前准备以下内容:
在 VS2022 或 CLion 中新建一个空项目;
熟悉如何创建多个
.c
和.h
文件(如game.c
,game.h
,test.c
);回顾第5讲(数组)与第6讲(函数)的知识;
思考:如果要在 9×9 的棋盘上“埋雷”,你会怎么存数据?
尝试写一个小函数:随机生成 10 个坐标。
💡 提示:
扫雷游戏的核心,不是“图形界面”,而是逻辑控制与数据组织。
这正是 C语言最锻炼编程思维的地方。
📘 系列结尾
本文属于《C语言入门教程》系列专栏第6讲
下一讲将带你用代码实现一个真正的小游戏——扫雷。
这是一次知识的融合与实战的起点。点赞 + 收藏 + 关注专栏,
一起在代码的世界中“拆解游戏的秘密” 💪