C语言——函数篇(自己也可以轻松写函数)
1、函数的概念
1.1、函数的定义
核心理解
函数是 具有特定功能的独立代码模块,包含四个基本要素:

// 典型函数定义示例
int isEven(int num) { // 判断奇偶的函数
return (num % 2 == 0); // 返回布尔值结果
}
1.2、函数的作用
四大核心价值
① 模块化设计
✅ 将复杂程序分解为多个简单函数
✅ 例:学生管理系统 = 输入函数 + 计算函数 + 输出函数
② 代码复用
✅ 避免重复代码
✅ 例:多次调用sort()函数实现排序
③ 逻辑封装
✅隐藏实现细节,暴露必要接口
✅ 例:直接使用printf()无需了解内部实现
④ 协作开发
✅多人并行开发不同函数
1.3、函数的语法
标准语法结构
// 函数声明(告诉编译器函数存在)
返回类型 函数名(参数类型列表);
// 函数定义(具体实现)
返回类型 函数名(形参列表) {
局部变量声明;
执行语句;
return 返回值; // void函数可省略
}
关键语法点
参数传递
// 值传递示例:原始数据不受影响
void add(int x) { x = x + 1; }
// 地址传递示例:原始数据会被修改
void realAdd(int *x) { *x = *x + 1; }
返回值规则
基本类型直接返回
数组通过指针返回
无返回值时使用void
函数调用
// 带返回值调用
int result = func(3, 5);
// 无返回值调用
func();
常见错误
// 错误1:缺少返回值
int sum(int a, int b) {
int s = a + b; // 缺少return语句
}
// 错误2:参数类型不匹配
double avg(int a, int b) {...}
avg(3.5, 4.2); // 实际传入double类型,但形参是int
本章要点总结
函数 = 功能模块 + 输入接口 + 输出结果
先声明后使用的基本原则
值传递与地址传递的本质区别
return语句的正确使用方法
2、函数的基础
2.1、函数调用
完整调用流程演示
示例代码:
#include <stdio.h>
// 函数声明
int add(int a, int b);
int main() {
int x = 3, y = 5;
// 函数调用
int result = add(x, y);
printf("Sum: %d\n", result);
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
调用过程图解
[主函数栈帧] [add函数栈帧]
| main()执行环境 | | a=3, b=5 |
| x=3 | ◀──┐ |临时变量区 |
| y=5 | │ |返回值=8 |
| result未初始化 | └──参数传递
↓
控制权转移 → 执行add函数
↓
[主函数恢复执行]
result = 8
关键步骤说明:
参数压栈:将x=3和y=5的值复制到add函数的参数a,b
控制转移:跳转到add函数代码段
栈帧创建:为add函数分配局部变量存储空间
执行运算:计算a+b得到8
返回值:将结果存入指定寄存器
栈帧销毁:释放add函数的局部变量空间
结果接收:主函数获取返回值存入result
2.2、参数传递
值传递 vs 地址传递
示例对比:
// 值传递(复制数据)
void modifyCopy(int num) {
num = 99; // 只修改副本
}
// 地址传递(操作原数据)
void modifyReal(int *num) {
*num = 99; // 修改原变量
}
int main() {
int a = 10;
modifyCopy(a); // 调用后a仍为10
modifyReal(&a); // 调用后a变为99
return 0;
}
内存变化演示:
调用modifyCopy(a)时:
主函数内存 函数参数内存
a: 0x1000 num: 0x2000
| 10 | | 10 | → 修改为99
调用modifyReal(&a)时:
主函数内存 指针操作
a: 0x1000 num存储0x1000
| 99 | ←───────通过地址修改
数组参数传递规范
正确写法:
// 必须显式传递数组长度
void printArray(int arr[], int size) {
for(int i=0; i<size; i++) { // 安全访问
printf("%d ", arr[i]);
}
}
int main() {
int data[] = {1,2,3,4,5};
printArray(data, 5); // 同时传递数组和长度
return 0;
}
错误示范分析:
void printArrayWrong(int arr[5]) { // 错误!实际仍视为指针
int size = sizeof(arr)/sizeof(int); // 得到指针大小而非数组长度
// 导致循环次数错误
}
2.3、作用域
三级作用域演示
示例代码:
int global = 100; // 全局作用域
void demo() {
int func_var = 50; // 函数作用域
if(1) {
int block_var = 20; // 块作用域
printf("Block内: %d %d %d\n",
global, func_var, block_var); // 全部可见
}
// printf("%d", block_var); // 错误!block_var不可见
}
int main() {
// printf("%d", func_var); // 错误!func_var不可见
demo();
return 0;
}
可视化作用域范围:
程序内存布局:
┌──────────────┐
│ 全局变量区 │ ← global
├──────────────┤
│ 栈区 │
│ ├─demo() │ ← func_var
│ │ └─if块 │ ← block_var
│ └─main() │
└──────────────┘
2.4、生命周期
三类变量对比
完整示例:
#include <stdio.h>
#include <stdlib.h>
int global_var; // 静态存储期(自动初始化为0)
void test() {
int auto_var = 0; // 自动存储期
static int static_var = 0; // 静态存储期
auto_var++;
static_var++;
int *heap_var = malloc(sizeof(int)); // 动态存储期
*heap_var = 100;
printf("auto:%d static:%d heap:%d\n",
auto_var, static_var, *heap_var);
free(heap_var); // 必须手动释放
}
int main() {
test(); // auto:1 static:1 heap:100
test(); // auto:1 static:2 heap:100
return 0;
}
生命周期示意图:
程序执行过程:
┌─────────────┐ test()第一次调用
│ global_var │────┐
├─────────────┤ │ 创建auto_var
│ 堆内存分配 │◀─┐ │ 创建static_var
└─────────────┘ │ │
│ │
test()结束后: │ │
auto_var销毁 │ │
heap_var内存释放 ←┘ │
static_var保留 ────┘
本章重点总结

3、函数的高级应用
3.1、递归函数
核心概念
基线条件:递归终止条件
递归条件:函数调用自身的条件
执行原理:函数调用栈(后进先出)
经典案例
// 阶乘计算
int factorial(int n) {
if(n <= 1) return 1; // 基线条件
return n * factorial(n-1); // 递归条件
}
// 斐波那契数列
int fibonacci(int n) {
if(n <= 1) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
风险与优化
栈溢出:深度递归可能导致栈空间耗尽
优化策略:
尾递归优化(需编译器支持)
改为迭代实现
3.2、回调函数
实现原理
函数指针:返回值类型 (*指针名)(参数列表)
典型应用:
排序算法(如qsort)
事件驱动编程
案例:通用排序
#include <stdlib.h>
// 比较函数原型
typedef int (*CompareFunc)(const void*, const void*);
void sortArray(int arr[], int n, CompareFunc cmp) {
qsort(arr, n, sizeof(int), cmp);
}
// 比较函数实现
int compareInt(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
// 使用
int nums[] = {5,2,7,1};
sortArray(nums, 4, compareInt);
3.3、可变参函数
实现方法
头文件:#include <stdarg.h>
关键宏:

案例:可变参数求和
#include <stdarg.h>
int sum(int count, ...) {
va_list args;
va_start(args, count);
int total = 0;
for(int i=0; i<count; i++){
total += va_arg(args, int);
}
va_end(args);
return total;
}
// 调用
int s = sum(3, 10, 20, 30); // s=60
注意事项
必须明确参数类型
缺乏类型安全检查
练习
1、用可变参数编写一个自己的my_printf
void my_printf(const char *str, ...)
{
// 扫描 有多少个 %
// 每个参数的位置
unsigned int type_index[128][2] = {0};
// 第一个存位置 第二个存类型
int val_agr = 0; // 获取参数个数
for (size_t i = 0; '\0' != str[i]; i++)
{
if ('%' == str[i])
{
// 存储位置
type_index[val_agr][0] = i;
// 存储类型
type_index[val_agr][1] = str[i + 1];
val_agr++;
}
}
// 创建列表
va_list arg_list;
// 初始化列表
va_start(arg_list, val_agr);
// 打印出每个参数的类型
// for (size_t i = 0; i < val_agr; i++)
// {
// // 续写
// switch (type_index[i][1])
// {
// case 'd':
// printf("%d\n", va_arg(arg_list, int));
// break;
// case 'f':
// printf("%f\n", va_arg(arg_list, double));
// break;
// case 's':
// printf("%s\n", va_arg(arg_list, char *));
// break;
// case 'c':
// printf("%c\n", va_arg(arg_list, int));
// break;
// default:
// break;
// }
// }
// for(int i=0;str[i]!='\0';i++){
// printf("%c",str[i]);
// }
int j=0,i=0;
while(str[j]!='\0'){
if(str[j]=='%'){
switch (type_index[i][1])
{
case 'd':
printf("%d", va_arg(arg_list, int));
break;
case 'f':
printf("%f", va_arg(arg_list, double));
break;
case 's':
printf("%s", va_arg(arg_list, char *));
break;
case 'c':
printf("%c", va_arg(arg_list, int));
break;
default:
break;
}
i++;
j+=2;
}else{
printf("%c",str[j]);
j++;
}
}
va_end(arg_list);
}
int main(int argc, char const *argv[])
{
// print(4 , 20 , 30 , 40 , 50);
my_printf("张三 %d 李四 %f %c %s\n", 13, 13.4 , 'C' , "张三");
return 0;
}
2、用回调函数 编写一个 结构体排序
#include<stdio.h>
#include<stdlib.h>
#define MAX 10
typedef struct
{
char name[128]; // 姓名
char sex[5]; // 性别
short id; // 学号
int age; // 年龄
int sco; // 成绩
}stu;
typedef struct
{
stu data[MAX]; // 学生
int len;
}class;
int Cmp_fuc(const void *val_1,const void *val_2){
stu s1=*(stu *)val_1;
stu s2=*(stu *)val_2;
return s1.sco-s2.sco;
}
void prin(class *classes)
{
// 循环
for (size_t i = 0; i < classes->len; i++)
{
// 打印数据
printf("name = %s sex = %s id = %d sco = %d age = %d \n",
classes->data[i].name, classes->data[i].sex, classes->data[i].id, classes->data[i].age, classes->data[i].sco);
}
}
int main(int argc, char const *argv[])
{
class student = {
.data = {
{"石昊", "男", 1, 18, 120},
{"萧炎", "男", 2, 18, 110},
{"牧尘", "男", 3, 18, 115},
{"林动", "男", 4, 18, 113},
{"韩立", "男", 5, 18, 105},
},
// 学生个数变量初始化
.len = 5};
qsort(student.data,student.len,sizeof(student.data[0]),Cmp_fuc);
prin(&student);
return 0;
}
更多推荐

所有评论(0)