一、概述
函数是 C 语言程序的基本结构单元,用于封装可复用的计算逻辑。使用函数能够提升代码模块化程度、可维护性与可扩展性。
二、函数的组成
C 语言中的函数通常包含三部分:
- 函数声明(Prototype):告知编译器函数的名称、参数与返回类型。
- 函数定义(Definition):函数的实际实现。
- 函数调用(Call):执行函数逻辑并获取返回值。
示例:f(x) R y = f(x) = x^2 x = 2
int add(int a, int b); // 声明
int add(int a, int b) { return a+b; } // 定义
add(3, 5); // 调用
三、函数的定义
1. 基本格式
返回类型 函数名(参数列表)
{
// 函数体
}
示例:
int max(int x, int y)
{
if (x > y)
return x; // 退出函数
return y;
}
四、函数的声明
函数声明的作用是在定义之前向编译器提供函数接口信息。例如:
返回值 函数名(传入的参数)
int max(int x, int y);
声明可放在文件开头或头文件中,便于分离编译。
五、函数调用
调用方式:
result = max(10, 20);
实参与形参之间采用值传递机制,形参 n接收的是实参 a的副本。
示例:
#include <stdio.h>
int square(int n)
{
n = n + 1;
return n * n;
}
int main()
{
int a = 5;
int r = square(a);
printf("%d\n%d", r, a);
return 0;
}
- 函数调用需要在函数声明之后
六、返回值与返回语句
- 使用
return返回表达式的值。 - 返回类型必须与函数声明一致。
- 函数不能返回数组
- 无返回值时使用
void。
示例:
void hello()
{
printf("Hello\n");
}
bool big(int a, int b){
return a < b;
}
七、参数传递与作用
C 语言采用值传递:函数内部修改形参不会 影响外部变量。
示例:
#include <stdio.h>
void setZero(int a)
{
a = 0;
}
int main()
{
int a = 10;
setZero(a);
printf("a = %d\n", a); // 输出仍为 10
return 0;
}
当需要修改外部变量时使用指针(扩展内容)。
传递数字型参数
在 C 语言中,数组作为函数参数时会退化为指向首元素的指针。因此函数接收的是指针形式,无法从参数本身获知数组长度,需显式传递长度。
1. 典型定义方式
void printArray(int arr[], int n)
{
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
printf("
");
}
等价写法:
void printArray(int *arr, int n)
2. 调用方式
int a[5] = {1, 2, 3, 4, 5};
printArray(a, 5);
3. 修改数组内容
函数内部可修改数组元素,外部可见。
void setZero(int arr[], int n)
{
for (int i = 0; i < n; i++)
arr[i] = 0;
}
int main(){
int arr[3] = {1, 2, 3};
setZero(arr); // 传入只需要传名称
}
4. 多维数组的传递
二维数组需指定列数:
void show(int *arr[4], int r)
{
for (int i = 0; i < r; i++)
for (int j = 0; j < 4; j++)
printf("%d ", arr[i][j]);
}
八、静态变量与生命周期
函数中的变量默认为自动变量,每次调用创建并销毁。使用 static 可让变量在多次调用间保持状态。
示例:
#include <stdio.h>
void counter()
{
static int c = 0;
c++;
printf("c = %d\n", c);
}
int main()
{
counter();
counter();
counter();
return 0;
}
输出依次为 1,2,3。
九、函数与多文件模块化
目录结构示例
project/
├── main.c
├── math.c
└── math.h
math.h 用于函数声明以及全局变量声明
#ifndef MATH_H
#define MATH_H
int add(int a, int b);
int sub(int a, int b);
#endif
math.c 用于函数的定义
#include "math.h"
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
main.c
#include <stdio.h>
#include "math.h"
int main()
{
printf("%d\n", add(3, 7));
printf("%d\n", sub(10, 5));
return 0;
}
编译方式(MinGW-w64):
gcc main.c math.c -o app.exe
十、设计函数的原则
- 单一职责:每个函数只完成一个逻辑任务。
- 命名清晰:函数名应准确描述其行为。
- 控制长度:尽量避免过长函数(>80 行)。
十一、综合示例
以下示例展示一个输入、处理与输出分离的函数结构:
#include <stdio.h>
int readNumber()
{
int x;
scanf("%d", &x);
return x;
}
int compute(int a, int b)
{
return a * b + 10;
}
void showResult(int r)
{
printf("Result = %d\n", r);
}
int main()
{
int x = readNumber();
int y = readNumber();
int r = compute(x, y);
showResult(r);
return 0;
}
十二、函数递归入门
递归是一种函数直接或间接调用自身的编程方式。其关键是将大问题分解为更小的同类子问题,并设置终止条件。
1. 递归的结构
递归函数通常包含两部分:
- 基本情况(终止条件):不再递归时的返回值。
- 递归情况:将问题拆分为规模更小的同类问题。
典型示例:
int n = 5;
result = factorial(5);
int factorial(int n)
{
if (n == 0)
return 1; // 基本情况
return n * factorial(n - 1); // 递归调用
}
stackoverflow 栈溢出
result = factorial(5);
factorial(5) = 5 * factorial(4); = 120 = 5!
f(4) = 4 * f(3) =24
f(3) = 3 * f(2) = 6
f(2) = 2 * f(1) = 2
f(1) = 1 * f(0) = 1
f(0) = 1;
n! = n * (n – 1)!
2. 使用递归的必要条件
- 子问题必须继续向终止条件收敛。
- 每次调用都应缩小问题规模。
- 必须避免无限递归。
3. 经典示例
(1) Fibonacci 数列
int fib(int n)
{
if (n <= 1)
return n;
return fib(n - 1) + fib(n - 2);
}
1 1 2 3 5 8 13
fib(7)
f(7) = f(6) + f(5)
f(6) = f(5) + f(4)
尾递归:
递归在函数return 的时候调用
(2) 递归遍历数组
void printArray(int arr[], int n)
{
if (n == 0)
return;
printArray(arr, n - 1);
printf("%d ", arr[n - 1]);
}
4. 递归与栈
每次递归调用都会占用栈空间保存现场,包括参数与返回地址。深度过大可能导致栈溢出。适当情况下需使用迭代替代递归。
十三、总结
本讲义覆盖函数的完整使用方法,包括声明、定义、调用、值传递机制、静态变量、多文件组织结构等内容,适合作为系统学习 C 语言函数的参考材料。