解码C语言函数

函数基本概念

函数定义

概念:将功能的实现流程封装并隐藏细节,仅提供简洁接口,用户无需了解内部即可调用触发功能
角度

  • 对于函数的使用者,可以简单地将函数理解为一个黑箱,使用者只管按照规定给黑箱一些输入,就会得到一些输出,而不必理会黑箱内部的运行细节。
    在这里插入图片描述
  • 对于函数的设计者,最重要的工作是封装,封装意味着对外提供服务并隐藏细节。对于一个封装良好的函数而言,其对外提供服务的接口应当是简洁的,内部功能应当是明确的。

作用:屏蔽功能实现细节以降低使用门槛、避免误改,提供统一调用方式减少重复编码,并执行逻辑完成功能目标、反馈结果。

函数组成要素
  • 返回值类型:依函数功能确定,需在函数头明确指定
  • 参数名:函数体内接收并处理外部数据的变量名
  • 参数列表:函数对外声明的输入接口,明确需传入参数的类型、数量与顺序
  • 函数体:函数头后花括号内,实现核心功能的具体逻辑代码区域

返回值

返回值作用

输出与函数功能相关的运算结果,供调用者后续使用。

返回值类型

需与函数功能产出的结果类型一致,并在函数头声明。

// 示例:返回整型值的函数
// 示例:功能为计算两数之和(结果为整型),故返回值类型为int
int add(int a, int b) {
    return a + b; // 返回与声明类型匹配的计算结果
}

参数传递

值传递

概念:将参数以变量值的形式传入函数,函数体内操作的是该参数的副本。

特点:函数内对参数副本的修改,不会影响外部原始参数的值。

void swap_by_value(int a, int b) {
    int temp = a;
    a = b;
    b = temp;// 只修改副本,不影响原始值
}
地址传递

概念:将变量的内存地址作为参数传入函数,函数通过该地址访问变量。

特点:函数体内可借助指针(或引用)操作地址指向的原始变量,能直接修改外部传入参数的实际值。

void swap_by_reference(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;// 修改原始值
}
函数调用的流程

函数调用时,进程的上下文会切换到被调函数,当被调函数执行完毕之后再切换回去。
待显示图片(缩小一半)
程序内存栈区
在这里插入图片描述

特殊函数类型

静态函数 (Static Functions)

符号:用 static 关键字限制函数可见范围

作用:仅在当前文件内可见,减少跨文件函数名冲突

使用方法

  • 可在同一文件的普通函数中直接调用
  • 不推荐在头文件中定义(可能导致重复定义),建议在 .c 文件中定义,供同文件内函数调用
// 在.c文件中定义静态函数
static void helper_function(void) {
    // 静态函数实现
}

// 同文件内的普通函数间接调用
void public_function(void) {
    helper_function(); // 直接调用静态函数
}
递归函数
递归函数基础
定义

递归函数(Recursive Function)是 直接或间接调用自身 的函数,通过将问题分解为更小的同类子问题来解决问题。

核心要素
  • 基线条件(Base Case):递归终止条件,防止无限递归。
  • 递归条件(Recursive Case):将问题拆解为更小的子问题,逐步接近基线条件。
经典示例
阶乘计算
int factorial(int n) {
    if (n <= 1) {// 基线条件
    	return 1;
    }
    else {// 递归条件
    	return n * factorial(n - 1);
    }
}

调用过程(以 factorial(3) 为例):

factorial(3)
→ 3 * factorial(2)
→ 3 * (2 * factorial(1))
→ 3 * (2 * 1) → 6
斐波那契数列

第 0 项:F(0) = 0第 1 项:F(1) = 1,从第 3 项开始,每一项等于前两项之和

int fibonacci(int n) {
    if (n <= 1) {// 基线条件
        return n;
    }
    else {// 递归条件
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}

问题:存在大量重复计算(如 fib(5) 重复计算 fib(2) 3次),时间复杂度为 O(2ⁿ)

递归的应用场景
场景说明示例
分治算法将大问题分解为独立子问题快速排序、归并排序
树/图遍历处理嵌套结构二叉树遍历、文件目录遍历
动态规划子问题重叠时优化计算带记忆化的斐波那契数列
数学问题自然递归定义的问题阶乘、汉诺塔、组合数计算
回调函数

概念:预先定义的函数,作为参数传递给其他函数,由被调用函数在特定时机触发执行

作用:实现功能解耦,便于扩展和多模块协作

使用场景:当封装功能时预留接口,允许外部自定义具体处理逻辑(通过传递函数地址)

// 回调函数类型定义(函数指针类型,参数为int,返回值为void)
typedef void (*callback_func)(int);

// 接收并触发回调的接口函数
// 功能:处理数据后,通过回调函数返回结果
void process_data(int data, callback_func callback) {
    // 模拟数据处理:比如将数据乘以2
    int processed_data = data * 2;
    
    // 在合适的时机(处理完成后)调用回调函数,传递处理结果
    callback(processed_data); 
}

// 自定义的回调函数实现
// 功能:打印处理后的结果(具体业务逻辑由用户自定义)
void my_callback(int result) {
    printf("处理结果: %d\n", result);
}

// 主函数:演示如何使用回调机制
int main() {
    int input_data = 5; // 原始输入数据
    
    // 调用process_data,传入原始数据和自定义回调函数my_callback
    // 当process_data处理完数据后,会自动调用my_callback并传入结果
    printf("开始处理数据: %d\n", input_data);
    process_data(input_data, my_callback); // 触发回调流程
    return 0;
}
函数指针数组

概念:存储多个同类型函数指针的数组,可通过数组索引直接调用对应的函数

作用:集中管理相关函数,便于批量调用或根据条件动态选择函数,简化多函数分支逻辑

// 定义函数指针类型(指向参数为int、无返回值的函数)
typedef void (*operation_func)(int);

// 函数指针数组(存储同类型函数的地址)
operation_func operations[] = {
    function1,  // 函数1的地址
    function2,  // 函数2的地址
    function3,  // 函数3的地址
    NULL        // 结束标记
};

// 遍历调用数组中的所有函数
void execute_operations(int value) {
    for (int i = 0; operations[i] != NULL; i++) {
        operations[i](value);  // 通过数组索引调用函数
    }
}

字符串处理函数

内存操作函数 (memxxxx系列)

特点:直接操作内存,效率高

#include <stddef.h>  //size_t类型的标准定义头文件
#include <stdlib.h>

/**
 * 内存复制(支持重叠内存区域)
 * @param dest 目标内存地址(需可写)
 * @param src  源内存地址(const修饰,不可修改)
 * @param n    复制的字节数
 * @return     返回dest(目标内存的起始地址)
 * @note       当src和dest内存区域重叠时,仍能保证复制结果正确,效率略低于memcpy
 */
void *memmove(void *dest, const void *src, size_t n);

/**
 * 内存复制(不支持重叠内存区域)
 * @param dest 目标内存地址(需可写)
 * @param src  源内存地址(const修饰,不可修改)
 * @param n    复制的字节数
 * @return     返回dest(目标内存的起始地址)
 * @note       当src和dest内存区域重叠时,结果未定义,但效率高于memmove
 */
void *memcpy(void *dest, const void *src, size_t n);

/**
 * 内存比较(按字节比较)
 * @param s1   第一个内存块地址
 * @param s2   第二个内存块地址
 * @param n    比较的字节数
 * @return     若前n字节s1 < s2返回负数;s1 == s2返回0;s1 > s2返回正数
 * @note       比较基于unsigned char值(避免符号位干扰)
 */
int memcmp(const void *s1, const void *s2, size_t n);

/**
 * 内存填充(按字节设置)
 * @param s    目标内存地址(需可写)
 * @param c    填充的字符(int类型,实际会转换为unsigned char使用)
 * @param n    填充的字节数
 * @return     返回s(目标内存的起始地址)
 * @example    memset(arr, 0, sizeof(arr)) // 将数组所有字节设为0
 */
void *memset(void *s, int c, size_t n);

/**
 * 分配内存并初始化为0
 * @param nmemb 元素数量
 * @param size  每个元素的大小(字节)
 * @return     成功返回分配内存的起始地址;失败返回NULL(如内存不足)
 * @note       总分配大小为nmemb * size(若溢出会返回NULL),所有字节初始化为0
 * @see        malloc(不初始化内存)
 */
void *calloc(size_t nmemb, size_t size);

/**
 * 重分配内存(扩展版,支持元素数量+单个大小参数)
 * @param ptr   原内存地址(NULL则相当于calloc分配新内存)
 * @param nmemb 新的元素数量
 * @param size  每个元素的大小(字节)
 * @return     成功返回新内存地址;失败返回NULL(原内存不释放)
 * @note       非C标准函数(POSIX扩展),功能类似realloc,但总大小为nmemb*size(自动检查溢出)
 */
void *reallocarray(void *ptr, size_t nmemb, size_t size);

/**
 * 分配未初始化的内存
 * @param size  分配的字节数
 * @return     成功返回内存起始地址;失败返回NULL(如内存不足)
 * @note       内存内容未定义(可能包含随机值),需手动初始化(如用memset)
 */
void *malloc(size_t size);

/**
 * 重分配内存(调整已有内存大小)
 * @param ptr   原内存地址(NULL则相当于malloc;非NULL需为malloc/calloc/realloc返回的地址)
 * @param size  新的内存大小(字节)
 * @return     成功返回新内存地址;失败返回NULL(原内存不释放)
 * @note       若新size > 原大小,新增区域内容未定义;若size=0且ptr非NULL,相当于free(ptr)
 */
void *realloc(void *ptr, size_t size);
字符串操作函数 (strxxx系列)

特点:专门处理以'\0'结尾的字符串

#include <string.h>  // 标准字符串函数声明头文件
#include <stddef.h>  // size_t类型定义(部分环境依赖)

/**
 * 字符串复制(复制至终止符'\0')
 * @param dest 目标字符串缓冲区(需可写,且足够容纳src内容+'\0')
 * @param src  源字符串(以'\0'结尾,const修饰不可修改)
 * @return     返回dest(目标字符串的起始地址)
 * @note       1. 会将src中的'\0'一同复制到dest;
 *             2. 若dest与src内存重叠,结果未定义;
 *             3. 无长度检查,可能因dest空间不足导致缓冲区溢出(安全风险)。
 * @example    char dest[20]; strcpy(dest, "hello"); // dest变为"hello\0..."
 */
char *strcpy(char *dest, const char *src);

/**
 * 字符串复制(限定最大复制长度)
 * @param dest 目标字符串缓冲区
 * @param src  源字符串(以'\0'结尾)
 * @param n    最大复制字节数(含可能的'\0')
 * @return     返回dest
 * @note       1. 若src长度(不含'\0')≥n:复制前n个字符,**不自动添加'\0'**(需手动处理,如dest[n-1]='\0');
 *             2. 若src长度(不含'\0')<n:先复制src全部内容(含'\0'),剩余位置用'\0'填充至总长度n;
 *             3. 内存重叠时结果未定义;建议优先用strlcpy(仅POSIX环境支持,如Linux/macOS,非C标准但更安全);
 * @example    char dest[5]; strncpy(dest, "hello", 5); // dest为"hello"(无'\0',风险);
 *             char dest[5]; strncpy(dest, "hi", 5);    // dest为"hi\0\0\0"(标准填充'\0')
 */
char *strncpy(char *dest, const char *src, size_t n);

/**
 * 字符串连接(追加至目标字符串末尾)
 * @param dest 目标字符串(需以'\0'结尾,且空间足够容纳src内容)
 * @param src  待追加的源字符串(以'\0'结尾)
 * @return     返回dest
 * @note       1. 会覆盖dest原有的'\0',并在追加后添加新的'\0';
 *             2. 无长度检查,若dest空间不足会导致缓冲区溢出;
 *             3. 若dest与src重叠,结果未定义。
 * @example    char dest[20] = "hi,"; strcat(dest, " world"); // dest变为"hi, world\0"
 */
char *strcat(char *dest, const char *src);

/**
 * 字符串连接(限定最大追加长度)
 * @param dest 目标字符串(以'\0'结尾)
 * @param src  待追加的源字符串
 * @param n    最大追加字节数(不含自动添加的'\0')
 * @return     返回dest
 * @note       1. 最多追加n个字符(从src的起始位置开始),无论src是否结束;
 *             2. 一定会在追加后添加'\0'(即使n=0,也会保留dest原有的'\0');
 *             3. 相对strcat更安全,但仍需确保dest总长度≥原有长度 + n + 1。
 * @example    char dest[10] = "a"; strncat(dest, "bcdef", 3); // dest变为"abc\0"(追加3个字符)
 */
char *strncat(char *dest, const char *src, size_t n);

/**
 * 字符串比较(按字符ASCII值逐位比较,直到'\0')
 * @param s1 第一个字符串
 * @param s2 第二个字符串
 * @return   若s1 < s2:返回负数;s1 == s2:返回0;s1 > s2:返回正数(具体值取决于首差异字符)
 * @note     1. 比较基于unsigned char值(避免符号位干扰,如'\xff' > 'a');
 *           2. 比较到第一个不同字符或其中一个字符串的'\0'为止。
 * @example   strcmp("apple", "app") → 正数(因"apple"更长且前3位相同)
 */
int strcmp(const char *s1, const char *s2);

/**
 * 字符串比较(限定最大比较长度)
 * @param s1 第一个字符串
 * @param s2 第二个字符串
 * @param n  最大比较字节数
 * @return   同strcmp(负数/0/正数)
 * @note     1. 比较前n个字符,或直到其中一个字符串遇到'\0'(以先到者为准);
 *           2. 若前n个字符完全相同(且均未到'\0'),返回0。
 * @example   strncmp("apple", "apples", 5) → 0(前5个字符相同)
 */
int strncmp(const char *s1, const char *s2, size_t n);

/**
 * 计算字符串长度(不含终止符'\0')
 * @param s 目标字符串(以'\0'结尾)
 * @return  字符串中字符的个数(从首地址到第一个'\0'的字节数)
 * @note    1. 若s不含'\0',会因越界访问导致未定义行为(如崩溃);
 *          2. 时间复杂度O(n),需遍历整个字符串。
 * @example  strlen("test") → 4('t','e','s','t'共4个字符)
 */
size_t strlen(const char *s);

/**
 * 查找字符在字符串中首次出现的位置
 * @param s 目标字符串(以'\0'结尾)
 * @param c 待查找的字符(int类型,实际按unsigned char转换后查找,含'\0')
 * @return  成功:返回指向该字符的指针;失败:返回NULL(未找到)
 * @note    1. 从字符串起始位置开始查找,包括终止符'\0'(若查找'\0',返回指向它的指针);
 *          2. 若s为NULL,结果未定义。
 * @example  strchr("abcabc", 'b') → 指向第一个'b'的指针(索引1处)
 */
char *strchr(const char *s, int c);

/**
 * 查找字符在字符串中最后一次出现的位置
 * @param s 目标字符串(以'\0'结尾)
 * @param c 待查找的字符(同strchr,含'\0')
 * @return  成功:返回指向该字符的指针;失败:返回NULL
 * @note    1. 从字符串末尾向起始位置查找(即找最右侧的目标字符);
 *          2. 若查找'\0',始终返回指向字符串末尾'\0'的指针(因字符串必以'\0'结尾)。
 * @example  strrchr("abcabc", 'b') → 指向第二个'b'的指针(索引4处)
 */
char *strrchr(const char *s, int c);

/**
 * 查找子串在字符串中首次出现的位置
 * @param haystack 主字符串(被查找的字符串)
 * @param needle   子串(待查找的字符串,以'\0'结尾)
 * @return         成功:返回子串在主串中首次出现的起始指针;失败:返回NULL
 * @note           1. 若needle为空串(""),返回指向haystack起始位置的指针;
 *                 2. 若needle长度 > haystack长度,直接返回NULL。
 * @example        strstr("hello world", "wor") → 指向"wor"起始位置的指针(索引6处)
 */
char *strstr(const char *haystack, const char *needle);

/**
 * 字符串分割(按分隔符拆分字符串)
 * @param str    首次调用时为待分割的字符串(非const,会被修改);后续调用传NULL
 * @param delim  分隔符集合(字符串,包含所有可能的分隔符)
 * @return       成功:返回当前分割出的子串指针;失败:返回NULL(无更多子串)
 * @note         1. 会修改原字符串(将分隔符替换为'\0'),因此str必须可写;
 *               2. 内部使用静态变量保存分割位置,**线程不安全**(多线程需用strtok_r);
 *               3. 连续的分隔符会被视为单个分隔符(如"a,,b"分割后为"a"、"b")。
 * @example      分割"a,b;c"(delim为",;"):
 *               strtok("a,b;c", ",;") → "a";
 *               strtok(NULL, ",;") → "b";
 *               strtok(NULL, ",;") → "c";
 *               strtok(NULL, ",;") → NULL。
 */
char *strtok(char *str, const char *delim);
注意事项

注意各函数之间的差异

  • memxxxx 系列:操作指定字节数的内存块,不关心'\0'终止符
  • strxxxx 系列:操作以'\0'结尾的字符串,自动处理字符串长度

函数设计最佳实践

函数接口设计原则
  • 参数列表力求简洁,仅包含必要输入
  • 返回值明确反映函数执行结果或产出
  • 用const修饰无需修改的参数(如只读数据)
  • 为函数添加清晰文档注释(说明功能、参数含义、返回值等)
错误处理

通过返回错误码或状态标识明确函数执行状态,便于调用者处理异常:

// 定义错误码枚举
typedef enum {
    SUCCESS,         // 执行成功
    INVALID_PARAM,   // 参数无效
    MEMORY_ERROR,    // 内存分配失败
    FILE_ERROR       // 文件操作错误
} error_code_t;

// 带错误处理的函数示例
error_code_t process_file(const char *filename) {
    if (filename == NULL) {
        return INVALID_PARAM;  // 参数检查失败时返回对应错误码
    }

    // 处理逻辑...
    return SUCCESS;  // 成功执行返回成功标识
}
函数复用性

概念:核心是设计通用接口,减少对特定场景的依赖

建议:建议通过函数指针、回调机制等方式,使函数适应不同业务逻辑,提升灵活性和复用范围

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值