1 函数基础概念
1.1 引入函数的必要性
在《街霸》这类游戏中,实现出拳、出脚、跳跃等动作,每项通常需编写 50 - 80 行代码。若每次调用都重复编写这些代码,程序会变得臃肿不堪,代码可读性与维护性也会大打折扣。
为解决这一问题,可将出拳、出脚、跳跃相关的代码提取出来,用大括号 {} 包裹,并为这段代码命名。之后在需要执行这些动作的地方,通过名称调用这段代码即可。提取出的这部分代码,就是程序中的函数。游戏执行相应动作时,调用对应函数即可,此方法减少了代码重复,提升了程序整洁度与可维护性。
1.2 什么是函数
函数是可重复使用的代码块,用于执行特定任务或操作。它能够将代码逻辑组织成独立单元,进而提高代码的可读性、可维护性与重用性。
在 C 语言中,一个程序可由一个或多个源文件(扩展名为 .c)组成。每个源文件都是编译单位,可包含多个函数,这些函数间能相互调用,所以函数是 C 程序的基本构成单元。
1.3 函数的作用
- 封装功能:将完整功能封装成函数,提升代码结构化程度与复用性。
- 代码模块化:按功能将程序拆分为多个模块单元,降低程序复杂度。
- 增强可维护性:若需修改某项功能,只需调整对应函数的代码。
- 隔离细节:通过函数调用隐藏实现细节,调用方只需关注输入输出。
1.4 函数的分类
从使用角度看,C 语言中的函数可分为两类:
- 库函数(标准函数):由 C 系统提供,用户无需自行定义,可直接使用。使用库函数时,必须包含对应的头文件(#include 语句)。
- 自定义函数:为满足具体需求而自行定义的函数,需先定义后使用。
2 函数的定义与调用
2.1 函数的定义
函数定义提供了函数的具体实现代码(函数体),包含函数完成任务的所有指令。其结构如下:
返回类型 函数名(参数列表)
{
函数体语句1;
函数体语句2;
…………………………
return 返回值; // 若返回类型为 void,可省略 return
}
结构说明:
- 函数名:调用函数时使用的名称,需符合标识符规范。
- 函数体:实现函数功能的代码块。
- 参数列表(形参列表):接收调用时传递的值(实参)。
- 返回值:函数执行后返回的值,类型需与返回类型一致。无返回值时,返回类型为 void。
2.2 案例:函数定义
以下代码演示了 C 语言中函数的定义:
#include <stdio.h>
// 无参数无返回值的函数
void func()
{
printf("hello func\n");
}
// 计算两整数差值的函数
int minus(int m, int n)
{
int result = m - n; // 先计算结果
return result; // 再返回结果
}
// 计算两浮点数和的函数
double add(double i, double j)
{
return i + j; // 也可以直接返回结果
}
// 返回两整数中较大值的函数
int max(int a, int b)
{
return a > b ? a : b;
}
// 主函数
int main()
{
return 0;
}
2.3 注意:函数不能嵌套定义
C 语言中,函数是独立的,不可嵌套定义。一个函数不能定义在另一个函数内部。
int func1(int a, int b)
{
// 错误:C 语言不允许在函数内部定义另一个函数
int func2(int c, int d)
{
// ...
}
}
某些编译器扩展支持嵌套定义,但不符合 C 标准,影响可移植性,强烈不建议使用。
2.4 函数的调用
语法:通过函数名 + 圆括号 () 调用函数,参数(实参)写在括号内,多个参数用逗号分隔。
执行机制:每次调用函数时,函数体内的代码会完整执行一次,从入口到返回或结束。
#include <stdio.h>
// 无参数无返回值的函数
void func()
{
printf("hello func\n");
}
// 计算两整数差值的函数
int minus(int m, int n)
{
int result = m - n; // 先计算结果
return result; // 再返回结果
}
// 计算两浮点数和的函数
double add(double i, double j)
{
return i + j; // 也可以直接返回结果
}
// 返回两整数中较大值的函数
int max(int a, int b)
{
return a > b ? a : b;
}
// 主函数
int main()
{
// 调用无参函数
func(); // 每次调用函数,函数体内的语句会执行一次
func(); // 每次调用函数,函数体内的语句会执行一次
// 调用有参函数并打印结果
printf("10-20的结果:%d\n", minus(10, 20)); // -10
printf("20-10的结果:%d\n", minus(20, 10)); // 10
// 传递变量和字面量作为参数
double d1 = 10.0, d2 = 90.0;
printf("10.0+90.0的结果:%.2f\n", add(d1, d2)); // 100.00
printf("20.0+80.0的结果:%.2f\n", add(20.0, 80.0)); // 100.00
// 比较并打印较大值
printf("66和88之间较大的是:%d\n", max(66, 88)); // 88
printf("45和31之间较大的是:%d\n", max(45, 31)); // 45
// 对返回值进行操作
printf("max(66,88) + max(12,6) = %d\n", max(66, 88) + max(12, 6)); // 88+12=100
return 0;
}
程序在 VS Code 中的运行结果如下所示:
3 函数的返回值
函数调用后可返回一个确定的值,称为返回值。返回值通常表示计算结果或函数执行状态。
3.1 无返回值类型
函数无返回值时,使用 void 作为返回类型。
正常情况
void 函数仅执行操作,不返回任何值。
#include <stdio.h>
void fun01() // 无返回值函数
{
printf("调用了 fun01 函数\n");
}
int main()
{
fun01(); // 调用无返回值函数
return 0;
}
程序在 VS Code 中的运行结果如下所示:
硬要返回一个值
若尝试从 void 函数返回一个值(如通过 return 返回具体值),会导致编译错误或警告。
#include <stdio.h>
void fun02() // 声明为 void 函数,但尝试返回值
{
printf("调用了 fun02 函数\n");
return 666; // 编译错误:void 函数不能返回值
}
int main()
{
fun02(); // 调用函数(编译报错)
return 0;
}
程序在 VS Code 中的运行结果如下所示:
3.2 有返回值类型
正常情况
明确指定返回值类型(如 int、double),并通过 return 返回具体值。
#include <stdio.h>
double fun02() // 返回 double 类型
{
return 3.1415926;
}
int main()
{
printf("fun02() 返回的数据:%.2f\n", fun02()); // 输出 3.14
return 0;
}
程序在 VS Code 中的运行结果如下所示:
无 return 语句
若函数声明了非 void 返回类型但未包含 return 语句,将返回不确定值(未定义行为)。
#include <stdio.h>
int fun03() // 声明返回 int 但无 return 语句
{
10 + 20; // 仅计算,未返回
// return; 只有 return,没有返回值,效果和没有 return 一样
}
int main()
{
printf("fun03() 返回的数据(不确定值):%d\n", fun03()); // 输出不确定值
return 0;
}
程序在 VS Code 中的运行结果如下所示:
返回类型不一致
若 return 表达式类型与函数声明类型不一致,编译器会尝试隐式转换(如 int → float),但需注意转换的安全性。
- 安全转换:低精度 → 高精度(如 int → double),数据无损失。
- 不安全转换:高精度 → 低精度(如 double → int),可能导致数据截断。
#include <stdio.h>
int fun04() // 声明返回 int,但 return 值为 double
{
return 20.89; // 隐式转换为 int,丢失小数部分
}
int main()
{
printf("fun04() 返回的数据(精度损失):%d\n", fun04()); // 输出 20
return 0;
}
程序在 VS Code 中的运行结果如下所示:
3.3 return 语句的作用
返回值传递
若函数声明了返回类型(非 void),return 语句将函数内部的计算结果返回给调用者。
int add(int a, int b) {
return a + b; // 返回两数之和
}
终止函数执行
return 语句会立即终止当前函数的执行,并跳转回调用位置。
- 无论 return 出现在函数体的何处(如循环、条件分支),后续代码均不再执行。
- 适用于提前退出函数(如错误处理或条件满足时)。
int divide(int a, int b) {
if (b == 0) {
return -1; // 除数为零时提前终止,返回错误码
// return; // 或者直接退出函数
}
return a / b; // 仅当 b != 0 时执行
}
多 return 路径
函数可通过不同条件分支包含多个 return 语句,实现灵活的控制流。
- 根据输入参数或状态选择不同的返回值或退出路径。
- 替代 if-else 嵌套,提升代码可读性。
int check_grade(int score) {
if (score >= 90) return 'A';
if (score >= 80) return 'B';
if (score >= 70) return 'C';
return 'D'; // 默认路径
}
4 函数的参数
函数的参数分为形式参数(形参)和实际参数(实参)。
4.1 形参与实参
- 形参:在函数定义或声明时,函数名后括号 () 中定义的变量,函数的 “输入接口”,生命周期短暂,仅在函数内有效。
- 实参:在函数调用时,函数名后括号 () 中使用的常量、变量或表达式,外部数据的 “传递媒介”,可以是任意合法表达式。
类别 | 形参(Formal Parameter) | 实参(Actual Parameter) |
---|---|---|
定义位置 | 函数定义或声明时,括号 () 内定义的变量 | 函数调用时,括号 () 内传递的具体值(常量、变量、表达式) |
作用 | 接收外部传入的数据,作为函数内部处理的临时变量 | 提供实际数据,初始化形参的值 |
作用域 | 仅在函数体内有效(局部作用域),函数结束后销毁 | 作用域由实参本身决定(如变量实参的作用域在其定义的位置) |
存储位置 | 栈内存(函数调用时分配,结束后释放) | 取决于实参类型(如变量实参存储在原有作用域的内存中) |
4.2 参数传递 → 值传递
C 语言中,函数调用时实参的值会被复制给形参(即 “值传递”)。形参相当于一个临时变量,存储实参的值,修改形参不影响实参。
- C 语言不支持引用传递,但可通过传递地址间接实现(后续学习)。
- 若实参数量与形参数量不一致,编译器会报错。
#include <stdio.h>
// 函数定义,接受两个整数参数,返回它们的和
int func(int x, int y)
{
return x + y; // 返回两个整数的和
}
int main()
{
int sum = func(3, 5); // 调用 func 函数,实参为 3 和 5
printf("func(3, 5) 返回的数为:%d\n", sum); // 输出:8
// 实参与形参数量不一致时,编译报错
// func(100, 299, 300); // 错误:func 函数只接受两个参数
// func(100); // 错误:func 函数需要两个参数
return 0;
}
程序在 VS Code 中的运行结果如下所示:
实参与形参数量必须一致,否则编译报错,如下所示:
C 语言默认采用值传递:实参的值被复制给形参,函数内对形参的修改不会影响实参。
#include <stdio.h>
// 交换两个变量的值
void swap(int a, int b)
{
// 注意:实参的值被复制给形参,函数内对形参的修改不会影响实参!!!
int temp = a;
a = b;
b = temp;
}
int main()
{
int x = 1, y = 2;
printf("交换前:x=%d, y=%d\n", x, y); // 输出:x=1, y=2
swap(x, y); // 调用后 x 和 y 的值不变
// 注意:实参的值被复制给形参,函数内对形参的修改不会影响实参!!!
printf("交换后:x=%d, y=%d\n", x, y); // 输出:x=1, y=2
return 0;
}
程序在 VS Code 中的运行结果如下所示:
5 文档注释
在 C 语言中,文档注释是一种用于生成函数或代码说明的特殊注释格式,便于开发人员理解代码功能。以下是常见的文档注释标签及其用途:
5.1 常用文档注释标签
- @brief:简要描述函数的功能。
- @param:描述函数的每个参数(格式:@param 参数名 参数说明)。
- @return:描述函数的返回值(仅用于有返回值的函数)。
- @note:补充说明函数的注意事项或其他信息。
- @warning:提示函数的使用风险或潜在问题。
5.2 工具支持
- VS Code:输入 /** 后按回车,可自动生成文档注释模板。
- Doxygen:通过解析文档注释生成代码文档(如 HTML、PDF 格式)。
5.3 案例演示
#include <stdio.h>
/**
* @brief 计算两个整数的和
*
* @param x 第一个整数
* @param y 第二个整数
* @return 返回 x 和 y 的和
* @note 此函数仅支持整数加法,不处理溢出问题
*/
int func(int x, int y)
{
return x + y; // 返回两个整数的和
}
int main()
{
int sum = func(3, 5);
printf("%d\n", sum); // 输出:8
return 0;
}
在 VS Code 中,将鼠标悬停在函数名上即可查看对应的文档注释,便于快速理解函数的功能、参数及返回值信息。
6 函数的原型声明
在 C 语言中,函数原型声明(Function Prototype)是编译器处理函数调用的重要依据。
6.1 函数原型的作用
提前告知编译器接口信息:函数必须 “先声明后使用”。若函数定义在 main() 或其他调用代码之后,需通过原型声明提前告知编译器函数的返回类型和参数列表,确保调用时能正确检查参数类型和返回值。
避免隐式声明风险:未声明直接调用函数时,编译器会假设返回类型为 int 并尝试猜测参数类型(C89 标准),可能导致未定义行为。函数原型可消除此类隐患。
6.2 函数原型的语法
函数原型声明需包含以下内容(不包含函数体):
- 返回类型:与函数定义一致(如 int、void)。
- 函数名:与定义完全匹配(区分大小写)。
- 参数列表:
- 必须指定参数类型(如 int、double)。
- 形参名称可选(仅用于文档说明,编译器忽略)。
// 完整形式(推荐,提升可读性)
int add(int a, int b);
// 省略形参名(仅保留类型)
int add(int, int);
- 分号必需:函数原型以分号结尾,区别于函数定义。
- void 参数:若函数无参数,需显式写 void(如 int func(void);),否则可能被误认为接受任意参数(C 语言旧标准)。
6.3 案例演示
#include <stdio.h>
// 函数原型声明(分号必需,形参名称可省略)
int twice1(int num1, int num2); // 完整形参名,推荐,提升可读性
int twice2(int, int, int); // 省略形参名
int main(void)
{
int result1 = twice1(10, 5); // 调用函数
printf("twice1(10, 5) = %d\n", result1); // 输出:30
int result2 = twice2(10, 5, 2); // 调用函数
printf("twice2(10, 5, 2) = %d\n", result2); // 输出:34
return 0;
}
// 函数 twice1 的定义
/**
* @brief 返回两个整数和乘以 2 的结果
* @param num1 第一个整数
* @param num2 第二个整数
* @return 返回 int 类型
*/
int twice1(int num1, int num2)
{
return (num1 + num2) * 2;
}
// 函数 twice2 的定义
/**
* @brief 返回三个整数和乘以 2 的结果
* @param num1 第一个整数
* @param num2 第二个整数
* @param num3 第三个整数
* @return 返回 int 类型
*/
int twice2(int num1, int num2, int num3)
{
return (num1 + num2 + num3) * 2;
}
程序在 VS Code 中的运行结果如下所示: