14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数

本文主要是介绍14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数

文章目录

  • 14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数
    • 一、静态函数
      • 1.1 语法
    • 二、递归函数
      • 2.1 示例:输出n个自然数
      • 2.2 内存变化
    • 三、函数指针
    • 四、指针函数
    • 五、回调函数
    • 六、内联函数
    • 七、变参函数
      • 7.1 示例:实现一个简单的变参函数
        • 7.1.1 分析 `printf` 函数
      • 7.2 实现步骤
        • 7.2.1 代码示例

一、静态函数

背景:在C语言中,普通函数默认是跨文件可见的。这意味着,如果在a.c文件中定义了一个函数swap(),则在b.c中也可以调用这个函数。这种特性在大型项目中可能会导致函数命名冲突。

静态函数:静态函数只能在其定义所在的文件中使用,限制了函数的作用范围,避免了命名冲突

1.1 语法

定义静态函数的语法是在函数的返回类型前加上static关键字:

static int func(int a, int b) {// 函数体
}

注意:

  • 静态函数的作用范围仅限于定义它的文件。
  • 静态函数可以防止不同文件中同名函数的冲突。
  • 静态函数不应被定义在头文件中,因为头文件可能会被多个源文件包含,这违背了静态函数的设计初衷。

二、递归函数

概念:递归函数是指在其内部包含对自身调用的函数。

递归问题示例

  1. 阶乘计算
  2. 幂运算
  3. 字符串反转

要点

  • 递进与回归:递归函数包含两个过程,逐步递进(问题规模逐渐缩小)和逐步回归(达到基准条件后开始返回)。
  • 基准条件:递归函数必须包含一个明确的基准条件以避免无限递归,防止栈溢出。

2.1 示例:输出n个自然数

思路:先输出n-1个自然数,最后输出n

#include <stdio.h>void printNaturalNumbers(int n) {if (n > 0) {printNaturalNumbers(n - 1);  // 递进printf("%d ", n);  // 回归}
}int main() {int n = 5;printf("Natural numbers up to %d:\n", n);printNaturalNumbers(n);return 0;
}

2.2 内存变化

递归调用时,每次调用都会在栈上分配新的栈帧。栈帧包括函数的局部变量和返回地址等。当递归深度过大时,会导致栈空间耗尽,可能引起栈溢出。
在这里插入图片描述


总结:

  • 递归栈增长:递归调用时,栈空间不断增长,直到满足基准条件或栈空间耗尽。
  • 递进和回归:问题逐步递进,达到基准条件后开始逐步回归。
  • 基准条件重要性:基准条件确保递归能正常终止,避免无限递归导致的栈溢出。

三、函数指针

函数指针是指向函数的指针,指针可以调用它指向的函数。函数指针的定义和使用如下:

#include <stdio.h>int Printf(int a, float f) {printf("a: %d, f: %f\n", a, f);return 0;
}int main(int argc, char const *argv[]) {// 定义一个函数指针, 名字为 p ,它指向的函数有一个整型返回值,需要一个整型参数以及一个浮点参数int (*p)(int, float);p = Printf;  // 函数名其实也是这个函数的入口地址// 直接调用函数Printf(10, 3.14);// 使用函数指针调用函数p(56, 9.8888);return 0;
}

四、指针函数

指针函数是一个返回指针的函数。例如:

int* func(int a, int b) {int* kk = (int*)malloc(sizeof(int));*kk = a + b;return kk;
}

注意:在实际使用中要避免返回局部变量的地址,应返回动态分配的内存或全局变量的地址。

五、回调函数

回调函数是一种通过函数指针实现的,函数的实现方不直接调用该函数,而是由接口提供方来调用该函数。例如:

案例一

#include <stdio.h>void func(int num) {printf("当前收到信号,军师让我蹲下 !!\n");
}void test(int num, void (*p)(int)) {for (size_t i = 0; i < num; i++) {printf("num: %d\n", num--);if (num == 50) {p(1);}}
}int main(int argc, char const *argv[]) {void (*p)(int); // 定义一个函数指针p = func; // 让指针 p 指向函数 func test(100, p);return 0;
}

案例二

#include <stdio.h>
#include <signal.h>void func(int num )
{  printf("当前收到  3 号信号 , 军师让我蹲下 !!\n");}int main(int argc, char const *argv[])
{void (*p)(int); // 定义一个函数指针p = func ; // 让指针p 指向函数  func // 设置进程捕获信号 ,如果信号值 为 3的时候 , 会自行调用 p 所指向的函数 (代码/指令)signal( 3 , p );while(1);     return 0;
}

signal( 3 , function);是一个用于捕获信号的函数,当他捕获到指定信号的时候则会执行用户所提供的函数。
由于signal函数是内核提供的函数,修改内核的代码不现实, 因此它提供的接口是一个函数指针,只要某个条件满足则会自动执行我们所给的函数。
使得不同软件模块的开发者的工作进度可以独立出来,不受时空的限制,需要的时候通过约定好的接口(或者标准)相互契合在一起。

六、内联函数

内联函数通过将函数调用替换为函数体来提高运行效率,避免函数调用的开销。语法上在函数前加上 inline 关键字。

语法:与普通函数区别不大, 只是在前面增加了函数的修饰 inline

inline int max_value(int x, int y) {return (x > y) ? x : y;
}

不适用内联函数的情况下,有可能某一个函数被多次重复调用则会浪费一定的时间在调用的过程中(现场保护+恢复)
在这里插入图片描述

如果使用内联函数就相当与把需要调用的函数的内容(指令)拷贝到需要调用的位置,可以节省函数调用的过程中浪费的时间
在这里插入图片描述

内联函数在提高运行效率的过程中,消耗了更多的内存空间。

七、变参函数

变参函数允许接受可变数量和类型的参数,通过 stdarg.h 头文件中的宏来实现。这些宏包括 va_listva_startva_argva_end

7.1 示例:实现一个简单的变参函数

下面是一个示例,实现一个类似 printf 的变参函数,用于输出格式化字符串。

7.1.1 分析 printf 函数

printf 函数可以接受可变数量的参数。比如 printf("%d%c%lf", 100, 'x', 3.14);。这里的各个参数的入栈顺序是从右往左进行的。
在这里插入图片描述

7.2 实现步骤

  1. 定义变参函数:定义一个接受变参的函数 my_printf
  2. 使用 stdarg.h 中的宏:利用 va_listva_startva_argva_end 来处理变长参数。
  3. 处理格式化字符串:解析格式化字符串,根据不同的格式符号输出相应类型的参数。
7.2.1 代码示例
#include <stdio.h>
#include <stdarg.h>// 定义变参函数
void my_printf(const char* format, ...) {va_list args; // 定义一个 va_list 类型的变量,用于访问变长参数va_start(args, format); // 初始化 args,使其指向第一个可变参数while (*format) { // 遍历格式化字符串if (*format == '%' && *(format + 1)) { // 如果遇到 '%' 符号,并且下一个字符不是 '\0'format++;switch (*format) { // 判断格式符号case 'd': {int i = va_arg(args, int); // 获取 int 类型的参数printf("%d", i);break;}case 'c': {char c = (char)va_arg(args, int); // 获取 char 类型的参数,注意 char 类型通过 int 获取printf("%c", c);break;}case 'f': {double d = va_arg(args, double); // 获取 double 类型的参数printf("%f", d);break;}case 's': {char* s = va_arg(args, char*); // 获取字符串类型的参数printf("%s", s);break;}default:printf("Unknown format specifier: %%%c\n", *format); // 处理未知格式符号}} else {putchar(*format); // 输出普通字符}format++;}va_end(args); // 清理工作
}int main() {// 测试变参函数my_printf("Hello %s, your score is %d and your average is %f\n", "John", 85, 87.5);my_printf("Character: %c\n", 'A');return 0;
}

代码说明

  1. 定义 my_printf 函数

    • 使用 va_list 定义一个变长参数列表 args
    • 使用 va_start(args, format) 初始化 args,并将其指向第一个变长参数。
    • 遍历格式化字符串 format,遇到格式符号(如 %d%c)时,使用 va_arg 获取相应类型的参数并输出。
    • 使用 va_end(args) 结束变长参数处理。
  2. 调用 my_printf 函数

    • my_printf("Hello %s, your score is %d and your average is %f\n", "John", 85, 87.5); 输出 Hello John, your score is 85 and your average is 87.500000
    • my_printf("Character: %c\n", 'A'); 输出 Character: A

总结

  1. 函数指针:定义和使用指向函数的指针。
  2. 指针函数:返回指针的函数。
  3. 回调函数:通过函数指针实现的,由接口提供方调用的函数。
  4. 内联函数:通过 inline 关键字避免函数调用的开销,提高效率。
  5. 变参函数:通过 stdarg.h 中的宏实现接受可变数量参数的函数。

这篇关于14-特殊函数——静态函数、递归函数、函数指针、回调函数、内联函数、变参函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1050259

相关文章

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

MySQL高级查询之JOIN、子查询、窗口函数实际案例

《MySQL高级查询之JOIN、子查询、窗口函数实际案例》:本文主要介绍MySQL高级查询之JOIN、子查询、窗口函数实际案例的相关资料,JOIN用于多表关联查询,子查询用于数据筛选和过滤,窗口函... 目录前言1. JOIN(连接查询)1.1 内连接(INNER JOIN)1.2 左连接(LEFT JOI

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

C语言函数递归实际应用举例详解

《C语言函数递归实际应用举例详解》程序调用自身的编程技巧称为递归,递归做为一种算法在程序设计语言中广泛应用,:本文主要介绍C语言函数递归实际应用举例的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录前言一、递归的概念与思想二、递归的限制条件 三、递归的实际应用举例(一)求 n 的阶乘(二)顺序打印

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注