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;
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐