C语言入门教程(第6讲):函数——让程序学会“分工合作”的魔法

🌱 文章摘要

写程序就像建城市------

变量是建筑,循环是道路,而函数是城市的"部门"

它让你的代码学会**"分工合作"**,不再混乱无章。


本讲带你彻底掌握函数的定义、参数、返回值与作用域,
让C语言从"能跑"变成"能管理"


📘 导读

到目前为止,我们的程序能输入、能循环、能存数据。

但是不是越来越"长",甚至有点"乱"?

一眼望去,全是printf和scanf堆在一起。

这时候,就需要函数 登场了------
它能把程序"切块",让每一块只干一件事。

在这一讲中,我们将:

  • 理解函数的核心思想;

  • 学会自己定义函数;

  • 掌握参数、返回值与作用域;

  • 明白库函数、自定义函数的区别;

  • 体验真正"模块化编程"的感觉。


准备好了吗?让我们一起打开C语言的"逻辑引擎"!


📘 建议收藏本专栏 :后续章节会持续更新,

包括控制语句、函数、指针、数组、文件操作等完整体系。

如果你能坚持学完,你会发现------
C语言不仅是工具,更是理解计算机世界的第一扇门。


目录

一、为什么要有函数

二、函数的结构

[2.1 调用流程](#2.1 调用流程)

三、函数的分类

[3.1 库函数:C语言的"工具箱"](#3.1 库函数:C语言的“工具箱”)

[3.2 自定义函数:你的"逻辑工厂"](#3.2 自定义函数:你的“逻辑工厂”)

四、形参与实参:函数的"传话机制"

[4.1 示例:形参与实参](#4.1 示例:形参与实参)

[4.2 示例:交换失败的经典陷阱](#4.2 示例:交换失败的经典陷阱)

五、return:函数的"快递出口"

[5.1 示例:有返回值的函数](#5.1 示例:有返回值的函数)

[5.2 示例:无返回值的函数](#5.2 示例:无返回值的函数)

六、数组作为函数参数:批量任务的传递方式

[6.1 示例:打印数组](#6.1 示例:打印数组)

[6.2 示例:修改数组](#6.2 示例:修改数组)

七、实战:用函数做一个"迷你计算器"

[7.1 项目:命令行计算器](#7.1 项目:命令行计算器)

八、函数声明与多文件协作

[8.1 示例:先声明,后使用](#8.1 示例:先声明,后使用)

[九、static 与 extern:函数的"活动范围"](#九、static 与 extern:函数的“活动范围”)

[9.1 static:只在本文件可见](#9.1 static:只在本文件可见)

[9.2 extern:跨文件访问](#9.2 extern:跨文件访问)

十、总结

十一、下一讲预告

[11.1 下一讲预告](#11.1 下一讲预告)

[11.2 提前准备建议](#11.2 提前准备建议)


一、为什么要有函数

先来想一个小故事:

你是一个程序员,在做一款"学生成绩管理系统"。

你要输入成绩、求平均值、求最大值、输出结果。

结果写完的 main 函数大概长这样:

cpp 复制代码
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 是"老板",函数是"员工",

老板发任务,员工干活,干完再汇报结果。


二、函数的结构

先看一个最经典的例子:

cpp 复制代码
#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;
}

输出:

cpp 复制代码
结果为:8

函数结构拆解:

cpp 复制代码
返回值类型 函数名(参数类型 参数名, ...) {
    函数体语句;
}
部分 含义
int 返回一个整数
add 函数名称
(int x, int y) 参数(形参)
return x + y; 返回结果

2.1 调用流程

  1. main 调用 add;

  2. 参数 3、5 传给 x、y;

  3. add 执行完毕;

  4. return 把结果送回 main;

  5. main 输出结果。


函数就是"任务模块",写一次,用很多次。


三、函数的分类

C语言中的函数可以分为两类:

类型 示例 说明
库函数 printf, scanf, strlen, sqrt 系统自带
自定义函数 add, max, print_sum 用户编写

3.1 库函数:C语言的"工具箱"

这些函数早已写好,我们只需调用。

示例:

cpp 复制代码
#include <math.h>
double result = sqrt(9.0);
printf("%.2f\n", result);

输出:

cpp 复制代码
3.00

常见库:

头文件 常见函数 功能
stdio.h printf, scanf 输入输出
math.h sqrt, pow 数学运算
string.h strlen, strcpy 字符串处理
stdlib.h rand, atoi 工具函数

3.2 自定义函数:你的"逻辑工厂"

当库函数不够用时,就自己造!

cpp 复制代码
int max(int a, int b) {
    return (a > b ? a : b);
}

调用:

cpp 复制代码
printf("最大值为:%d\n", max(10, 20));

输出:

cpp 复制代码
最大值为:20

库函数是C语言的"现成工具",自定义函数是你的"创造力体现"。


四、形参与实参:函数的"传话机制"

在现实世界里,老板(main函数)给员工(子函数)下任务时,总得告诉他"干啥"和"数据是多少"。

在C语言里,这种"任务沟通"靠的是------参数传递


4.1 示例:形参与实参

cpp 复制代码
#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;
}

输出:

cpp 复制代码
两数之和:30

解释:

  • 形参:函数定义时用来接收值的"容器";

  • 实参:函数调用时传入的实际值。

调用时,实参的值会被"复制"给形参,函数内部修改形参,不会影响外部的实参。


4.2 示例:交换失败的经典陷阱

cpp 复制代码
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;
}

输出:

cpp 复制代码
3 5   // 没有交换成功

原因:

函数内部的 ab 只是"副本"。

真正的 xy 在外面安然无恙。


C语言的参数默认是"值传递"。
想真正交换,需要传递地址(指针篇将讲)。


五、return:函数的"快递出口"

函数干完活,总得告诉调用者结果。

这个"出口"就是 return


5.1 示例:有返回值的函数

cpp 复制代码
int add(int a, int b) {
    return a + b;   // 返回结果
}

int main() {
    int r = add(5, 7);
    printf("结果是:%d\n", r);
    return 0;
}

输出:

cpp 复制代码
结果是:12

5.2 示例:无返回值的函数

cpp 复制代码
void say_hi() {
    printf("Hello, C!\n");
}

int main() {
    say_hi();   // 不需要接收返回值
    return 0;
}

输出:

cpp 复制代码
Hello, C!

总结:

  • 有返回值:带回数据;

  • 无返回值:只执行操作;

  • return 语句执行后函数立即结束;

  • 一个函数只能有一个返回类型。


return 是函数与外界交流的"语言"。
没有它,函数就像一个"沉默的打工人"。


六、数组作为函数参数:批量任务的传递方式

有时候我们需要让函数处理整组数据,例如统计成绩或查找最大值。

这时我们可以把"数组"整个传给函数。


6.1 示例:打印数组

cpp 复制代码
#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;
}

输出:

cpp 复制代码
1 2 3 4 5

说明:

数组名在传参时会自动转为"指针"。

函数内部访问的是原数组内容。


6.2 示例:修改数组

cpp 复制代码
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]);
}

输出:

cpp 复制代码
2 4 6

结论:

函数中修改数组会影响原数据。

因为传递的是"地址",不是"副本"。


数组参数让函数处理大规模数据,是通向"指针世界"的第一个入口。


七、实战:用函数做一个"迷你计算器"

光讲理论没意思,来点"真刀真枪"的代码吧。


7.1 项目:命令行计算器

目标:输入表达式 3 + 5,自动输出结果。

cpp 复制代码
#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;
}

输出示例:

cpp 复制代码
请输入表达式(如 3 + 5):8 * 9
结果:72

分析:

  • 每个函数只干一件事;

  • main 函数只负责"分工";

  • 代码结构清晰、逻辑分明。


当你能用函数写出这样的程序,就说明你真的理解了"模块化思维"。


八、函数声明与多文件协作

在大型项目中,代码常分散在多个文件。

C语言要求函数在使用前必须声明


8.1 示例:先声明,后使用

cpp 复制代码
#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:只在本文件可见

cpp 复制代码
static void say_hi() {
    printf("Hi, I'm a static function!\n");
}

用途:

  • 防止命名冲突;

  • 常用于内部实现函数。


9.2 extern:跨文件访问

cpp 复制代码
// 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 提前准备建议

强烈建议提前准备以下内容:

  1. 在 VS2022 或 CLion 中新建一个空项目;

  2. 熟悉如何创建多个 .c.h 文件(如 game.c, game.h, test.c);

  3. 回顾第5讲(数组)与第6讲(函数)的知识;

  4. 思考:如果要在 9×9 的棋盘上"埋雷",你会怎么存数据?

  5. 尝试写一个小函数:随机生成 10 个坐标。

💡 提示:

扫雷游戏的核心,不是**"图形界面"** ,而是逻辑控制与数据组织

这正是 C语言最锻炼编程思维的地方。


📘 系列结尾

本文属于《C语言入门教程》系列专栏第6讲

下一讲将带你用代码实现一个真正的小游戏------扫雷

这是一次知识的融合与实战的起点。

点赞 + 收藏 + 关注专栏

一起在代码的世界中"拆解游戏的秘密" 💪

相关推荐
泽虞3 小时前
《Qt应用开发》笔记p3
linux·开发语言·数据库·c++·笔记·qt·面试
晓风残月淡3 小时前
JVM字节码与类的加载(一):类的加载过程详解
开发语言·jvm·python
XXYBMOOO3 小时前
如何自定义 Qt 日志处理并记录日志到文件
开发语言·数据库·qt
玖釉-3 小时前
三维模型数据结构与存储方式解析
数据结构·算法·图形渲染
知南x3 小时前
【QT界面设计学习篇】qt快速开发技巧
开发语言·qt
hqyjzsb3 小时前
2025 年项目管理转型白皮书:AI 驱动下的能力重构与跨域突破
开发语言·人工智能·重构·产品经理·编程语言·caie
奶茶树3 小时前
【C++】12.多态(超详解)
开发语言·c++
草莓熊Lotso3 小时前
《算法闯关指南:优选算法--二分查找》--17.二分查找(附二分查找算法简介),18. 在排序数组中查找元素的第一个和最后一个位置
开发语言·c++·算法
努力努力再努力wz3 小时前
【C++进阶系列】:万字详解特殊类以及设计模式
java·linux·运维·开发语言·数据结构·c++·设计模式