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

相关文章

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Python Counter 函数使用案例

《PythonCounter函数使用案例》Counter是collections模块中的一个类,专门用于对可迭代对象中的元素进行计数,接下来通过本文给大家介绍PythonCounter函数使用案例... 目录一、Counter函数概述二、基本使用案例(一)列表元素计数(二)字符串字符计数(三)元组计数三、C

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N

MySQL中REPLACE函数与语句举例详解

《MySQL中REPLACE函数与语句举例详解》在MySQL中REPLACE函数是一个用于处理字符串的强大工具,它的主要功能是替换字符串中的某些子字符串,:本文主要介绍MySQL中REPLACE函... 目录一、REPLACE()函数语法:参数说明:功能说明:示例:二、REPLACE INTO语句语法:参数

python中update()函数的用法和一些例子

《python中update()函数的用法和一些例子》update()方法是字典对象的方法,用于将一个字典中的键值对更新到另一个字典中,:本文主要介绍python中update()函数的用法和一些... 目录前言用法注意事项示例示例 1: 使用另一个字典来更新示例 2: 使用可迭代对象来更新示例 3: 使用

通过配置nginx访问服务器静态资源的过程

《通过配置nginx访问服务器静态资源的过程》文章介绍了图片存储路径设置、Nginx服务器配置及通过http://192.168.206.170:8007/a.png访问图片的方法,涵盖图片管理与服务... 目录1.图片存储路径2.nginx配置3.访问图片方式总结1.图片存储路径2.nginx配置

Python lambda函数(匿名函数)、参数类型与递归全解析

《Pythonlambda函数(匿名函数)、参数类型与递归全解析》本文详解Python中lambda匿名函数、灵活参数类型和递归函数三大进阶特性,分别介绍其定义、应用场景及注意事项,助力编写简洁高效... 目录一、lambda 匿名函数:简洁的单行函数1. lambda 的定义与基本用法2. lambda

Python 函数详解:从基础语法到高级使用技巧

《Python函数详解:从基础语法到高级使用技巧》本文基于实例代码,全面讲解Python函数的定义、参数传递、变量作用域及类型标注等知识点,帮助初学者快速掌握函数的使用技巧,感兴趣的朋友跟随小编一起... 目录一、函数的基本概念与作用二、函数的定义与调用1. 无参函数2. 带参函数3. 带返回值的函数4.

MySQL中DATE_FORMAT时间函数的使用小结

《MySQL中DATE_FORMAT时间函数的使用小结》本文主要介绍了MySQL中DATE_FORMAT时间函数的使用小结,用于格式化日期/时间字段,可提取年月、统计月份数据、精确到天,对大家的学习或... 目录前言DATE_FORMAT时间函数总结前言mysql可以使用DATE_FORMAT获取日期字段

Django中的函数视图和类视图以及路由的定义方式

《Django中的函数视图和类视图以及路由的定义方式》Django视图分函数视图和类视图,前者用函数处理请求,后者继承View类定义方法,路由使用path()、re_path()或url(),通过in... 目录函数视图类视图路由总路由函数视图的路由类视图定义路由总结Django允许接收的请求方法http