C语言:深入了解指针3

server/2025/2/6 7:17:30/

1.回调函数是什么?

基本概念

回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数
时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。

使用场景

  • 事件驱动编程:在图形用户界面(GUI)编程中,当用户点击按钮、输入文本等操作发生时,系统会调用预先注册的回调函数来处理这些事件。
  • 排序算法:标准库中的qsort函数允许用户传入一个比较函数作为回调函数,以实现自定义的排序规则。
  • 异步操作:在多线程或异步编程中,当一个异步任务完成时,可以通过回调函数通知主线程进行后续处理。

实现方式

在 C 语言中,实现回调函数主要涉及函数指针的使用。函数指针是指向函数的指针变量,它可以存储函数的地址,并且可以通过该指针调用相应的函数。

示例代码

示例 1:简单的回调函数示例
#include <stdio.h>// 定义回调函数类型
typedef int (*Callback)(int, int);//typedef是自定义类型名字// 回调函数1:加法
int add(int a, int b) 
{return a + b;
}// 回调函数2:减法
int subtract(int a, int b) 
{return a - b;
}// 主调函数,接受一个回调函数作为参数
int operate(int a, int b, Callback func) //Callback func ==int(*func)(int,int)
{return func(a, b);
}int main() 
{int x = 10, y = 5;// 使用加法回调函数int result1 = operate(x, y, add);//函数k名就是地址printf("加法结果: %d\n", result1);// 使用减法回调函数int result2 = operate(x, y, subtract);printf("减法结果: %d\n", result2);return 0;
}

代码解释

1. 定义回调函数类型

typedef int (*Callback)(int, int);

  • typedef 是 C 语言中的一个关键字,用于为已有的数据类型定义一个新的类型名,目的是让代码更具可读性和可维护性。
  • int (*Callback)(int, int) 定义了一个函数指针类型。具体来说,Callback 是一个新的类型名,它代表的是一个指向函数的指针,这个函数接收两个 int 类型的参数,并且返回一个 int 类型的值。

2. 定义回调函数

加法函数 add

 int add(int a, int b)

{

      return a + b;

}

  • 这是一个普通的函数,它接受两个 int 类型的参数 a 和 b,并返回它们的和。
  • 该函数的参数和返回值类型与前面定义的 Callback 类型相匹配,所以它可以作为 Callback 类型的回调函数使用。
减法函数 subtract

 int subtract(int a, int b) 

{

       return a - b; 

}

  • 同样是一个普通函数,接受两个 int 类型的参数 a 和 b,返回它们的差。
  • 它的参数和返回值类型也与 Callback 类型匹配,也能作为回调函数使用。

3. 定义主调函数

 int operate(int a, int b, Callback func) 

        return func(a, b); 

}

  • operate 是主调函数,它接受三个参数:两个 int 类型的参数 a 和 b,以及一个 Callback 类型的参数 func
  • Callback func 等价于 int(*func)(int, int),即 func 是一个函数指针,它指向一个符合 Callback 类型定义的函数。
  • 在函数体中,通过 func(a, b) 调用 func 所指向的函数,并将 a 和 b 作为参数传递给该函数,最后返回该函数的返回值。
4. main 函数

 int main() 

    int x = 10, y = 5

     // 使用加法回调函数 

     int result1 = operate(x, y, add ); 

      printf("加法结果: %d\n", result1 ); 

        // 使用减法回调函数

        int result2 = operate(x, y, subtract ); 

         printf("减法结果: %d\n", result2 ); 

         return 0

}

  • 首先,定义并初始化两个 int 类型的变量 x 和 y,分别赋值为 10 和 5。
  • 调用 operate 函数进行加法运算:
    • operate(x, y, add) 中,将 x 和 y 作为前两个参数传递给 operate 函数,将 add 函数名作为回调函数传递给 operate 函数。在 C 语言中,函数名可以隐式转换为函数的地址,所以 add 实际上传递的是 add 函数的地址。
    • operate 函数内部会通过函数指针 func 调用 add 函数,计算 x 和 y 的和,并将结果返回给 result1
    • 使用 printf 函数输出加法结果。
  • 调用 operate 函数进行减法运算:
    • 类似地,operate(x, y, subtract) 将 subtract 函数的地址作为回调函数传递给 operate 函数。
    • operate 函数内部通过函数指针 func 调用 subtract 函数,计算 x 和 y 的差,并将结果返回给 result2
    • 使用 printf 函数输出减法结果。

2. qsort 使⽤举例

函数原型

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

参数解释

  • base:指向要排序的数组的第一个元素的指针。由于使用 void * 类型,所以可以处理任意类型的数组。
  • nmemb:数组中元素的个数。
  • size:数组中每个元素的大小(以字节为单位)。
  • compar:一个指向比较函数的指针。该比较函数用于确定元素之间的顺序关系,它接受两个 const void * 类型的参数,并返回一个整数值,表示两个元素的相对顺序。

比较函数规则

比较函数 int_comp 的返回值规则如下:

  • 如果返回值小于 0,表示第一个参数小于第二个参数。
  • 如果返回值等于 0,表示两个参数相等。
  • 如果返回值大于 0,表示第一个参数大于第二个参数。
#include <stdio.h>
#include <stdlib.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数int int_cmp(const void * p1, const void * p2)
{return (*( int *)p1 - *(int *) p2);//小于返回一个负数,大于返回一个正数,等于返回0.
}int main()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{printf( "%d ", arr[i]);}printf("\n");return 0;
}

代码解释:

1. 比较函数 int_cmp

  • 参数qsort 函数要求使用者提供一个比较函数,这个比较函数必须接受两个 const void * 类型的参数。const void * 是一种通用的指针类型,可以指向任意类型的数据,使用它是为了让 qsort 函数能够处理不同类型的数组。这里的 p1 和 p2 就是指向要比较的两个元素的指针。
  • 类型转换:在比较函数内部,由于 p1 和 p2 是 const void * 类型,不能直接进行解引用操作,所以需要将它们转换为 int * 类型(因为这里要比较的是整数数组),然后再进行解引用,得到具体的整数值。
  • 返回值:比较函数的返回值决定了两个元素的相对顺序。如果 *(int *)p1 - *(int *) p2 的结果小于 0,说明 p1 指向的元素小于 p2 指向的元素;如果结果等于 0,说明两个元素相等;如果结果大于 0,说明 p1 指向的元素大于 p2 指向的元素。

2. main 函数

2.1 数组定义与变量声明
  • 定义了一个包含 10 个整数的数组 arr,初始化为 { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 }
  • 声明一个整型变量 i 用于后续的循环操作。
2.2 调用 qsort 函数进行排序
  • qsort 函数的原型为 void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
    • base:指向要排序的数组的第一个元素的指针,这里传入 arr,即数组的首地址。
    • nmemb:数组中元素的个数,通过 sizeof(arr) / sizeof(arr[0]) 计算得到,即数组的总字节数除以单个元素的字节数。
    • size:数组中每个元素的大小(以字节为单位),这里是 sizeof(int),表示每个整数元素占用的字节数。
    • compar:指向比较函数的指针,这里传入 int_cmp,即前面定义的比较函数。
2.3 输出排序后的数组
  • 使用 for 循环遍历排序后的数组 arr,并使用 printf 函数将每个元素输出,元素之间用空格分隔。
  • 最后使用 printf("\n"); 输出一个换行符,使输出结果更美观。
  • main 函数返回 0 表示程序正常结束。

    综上所述,这段代码的主要功能是使用 qsort 函数对一个整数数组进行排序,并将排序后的数组元素输出。通过自定义比较函数 int_cmp,可以灵活地控制排序的顺序。

3. qsort函数的模拟实现

    下面就是模拟qsort函数的模拟实现的

#include<stdio.h>int cmp_int(const void* p1, const void* p2)//整型排序
{return (*(int*)p1 - *(int*)p2);//可以做差返回}
void print_arr(int arr[], int sz)//打印排序过的数组
{int  i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}void Swap(char* buf1, char* buf2, size_t width)//比较之后交换的函数
{int i = 0;char tmp = 0;for (i = 0; i < width; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))//总函数
{   // 趟数int i = 0;for (i = 0; i < sz - 1; i++){   //一趟内部的两两比较int j = 0;for (j = 0; j < sz - 1 - i; j++){  //比较两个元素if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){ //  交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test1()//需要比较的
{int arr[] = { 9,8,7,4,6,2,10,1,3,5 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);//cmp_int == &cmp_int 地址需要指针接收。print_arr(arr, sz);
}
int main()
{test1();return 0;
}

1. 比较函数

1.1 cmp_int 函数

int cmp_int(const void* p1, const void* p2)

      return (*(int*)p1 - *(int*)p2); 

}

  • 功能:用于比较两个整型数据的大小。
  • 参数
    • p1 和 p2const void* 类型的指针,这是为了使函数具有通用性,可以接收任意类型的指针。
  • 实现细节
    • (int*)p1 和 (int*)p2:将 void* 类型的指针强制转换为 int* 类型,以便可以解引用获取整型值。
    • *(int*)p1 和 *(int*)p2:解引用指针,得到对应的整型值。
    • *(int*)p1 - *(int*)p2:计算两个整型值的差。如果结果大于 0,表示 p1 指向的值大于 p2 指向的值;如果结果小于 0,表示 p1 指向的值小于 p2 指向的值;如果结果等于 0,表示两个值相等。

2. 打印函数

2.1 print_arr 函数

void print_arr(int arr[], int sz)

{

    int i = 0;

    for (i = 0; i < sz; i++) 

  { 

   printf("%d ", arr[i]);

  } 

}

  • 功能:用于打印整型数组的元素。
  • 参数
    • arr:整型数组。
    • sz:数组的元素个数。
  • 实现细节
    • 使用 for 循环遍历数组的每个元素。
    • 使用 printf 函数以整数格式打印每个元素,并在元素之间添加一个空格。

3. 交换函数 Swap

void Swap(char* buf1, char* buf2, size_t width)

                        int i = 0

                       char tmp = 0;

                 for (i = 0; i < width; i++) 

      { 

                          tmp = *buf1;

                        *buf1 = *buf2;

                        *buf2 = tmp;

                         buf1++;

                         buf2++; 

      } 

}

  • 功能:用于交换两个内存块的内容。
  • 参数
    • buf1 和 buf2char* 类型的指针,指向要交换的两个内存块。
    • width:每个内存块的字节数。
  • 实现细节
    • 使用 for 循环逐字节交换两个内存块的内容。
    • 通过 char 类型的临时变量 tmp 来存储中间值,实现交换。
    • 每次交换一个字节后,将指针 buf1 和 buf2 向后移动一个字节,直到交换完整个内存块。

4. 冒泡排序函数 bubble_sort

void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2)) 
{int i = 0;for (i = 0; i < sz - 1; i++) {int j = 0;for (j = 0; j < sz - 1 - i; j++) {if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
  • 功能:实现通用的冒泡排序算法。
  • 参数
    • basevoid* 类型的指针,指向待排序数组的起始地址,使用 void* 类型可以接收任意类型的数组。
    • sz:数组的元素个数。
    • width:每个元素的字节数。
    • cmp:函数指针,指向一个比较函数,用于确定元素的顺序。
  • 实现细节
    • 外层 for 循环控制排序的趟数,共进行 sz - 1 趟。
    • 内层 for 循环进行每一趟的两两比较,比较相邻的两个元素。
    • cmp((char*)base + j * width, (char*)base + (j + 1) * width):调用比较函数 cmp 比较相邻两个元素的大小。(char*)base + j * width 和 (char*)base + (j + 1) * width 分别计算相邻两个元素的地址。
    • 如果比较结果大于 0,表示顺序不正确,调用 Swap 函数交换这两个元素的位置。

5. 测试函数

5.1 test1 函数

void test1()

                 int arr[] = { 9,8,7,4,6,2,10,1,3,5 };

                 int sz = sizeof(arr) / sizeof(arr[0]);

                 bubble_sort(arr, sz, sizeof(arr[0]), cmp_int); 

                 print_arr(arr, sz); 

}

 

  • 功能:创建一个整型数组,调用 bubble_sort 函数对数组进行排序,然后调用 print_arr 函数打印排序后的数组。
  • 实现细节
    • int arr[] = { 9,8,7,4,6,2,10,1,3,5 };:定义一个包含 10 个整数的数组。
    • int sz = sizeof(arr) / sizeof(arr[0]);:计算数组的元素个数。
    • bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);:调用 bubble_sort 函数对数组进行排序,使用 cmp_int 函数作为比较规则。
    • print_arr(arr, sz);:调用 print_arr 函数打印排序后的数组。

6. 主函数

    int main()

{

      test1();

      return 0;

}

  • 功能:程序的入口点,调用 test1 函数进行测试。
  • 返回值:返回 0 表示程序正常结束。

下面是我自己画的图(画的不是特别好)方便大家理解理解:


http://www.ppmy.cn/server/165349.html

相关文章

GaussDB安全配置建议

安全性是华为云与您的共同责任。华为云负责云服务自身的安全&#xff0c;提供安全的云。作为租户&#xff0c;您需要合理使用云服务提供的安全能力对数据进行保护&#xff0c;安全地使用云。详情请参见责任共担。 本文提供了GaussDB使用过程中的安全配置建议&#xff0c;旨在为…

开源 OA 办公系统

目前尚未有关于勾股 OA 5.6.8 新春版发布的相关确切信息&#xff0c;但勾股 OA v5.6 已于 2025 年 1 月 19 日发布2。勾股 OA 是一款基于 ThinkPHP6 Layui MySql 打造的实用的开源的企业办公系统2。勾股 OA v5.6 的相关信息如下2&#xff1a; 系统特点 系统各功能模块一目了…

部署Hadoop高可用集群

注&#xff1a;下述步骤仅供参考&#xff0c;具体指令和操作截图的word版本可见上方本博文免费资源绑定。 1.为虚拟机Hadoop1&#xff0c;2&#xff0c;3拍摄快照以免后续错误操作 2.创建hadoop-HA目录区分之前的集群并将Hadoop安装到该目录下 3.进入/etc目录修改系统环境变量…

《迪拜AI展:探寻中东人工智能发展的璀璨新篇》

迪拜&#xff1a;AI 浪潮下的闪耀明珠 迪拜&#xff0c;这座位于阿拉伯半岛东部、波斯湾东南岸的城市&#xff0c;犹如一颗璀璨的明珠&#xff0c;在中东地区散发着独特的魅力。它是阿拉伯联合酋长国的第二大城市&#xff0c;也是迪拜酋长国的首府 &#xff0c;凭借优越的地理位…

为AI聊天工具添加一个知识系统 之86 详细设计之27 数据处理:ETL

本文要点 ETL 数据提取 作为 数据项目的起点。数据的整个三部曲--里程碑式的发展进程&#xff1a; ETL : 1分形 Type()-层次Broker / 2完形 Method() - 维度Delegate /3 整形 Class() - 容器 Agent 1变象。变象 脸谱Extractor - 缠度&#xff08;物理 皮肤缠度&#xf…

蓄电池巡检仪介绍以及功能特点---安科瑞 吴雅芳

一、引言 在电力、通信、交通等众多领域中&#xff0c;蓄电池作为重要的备用电源&#xff0c;其性能的稳定与否直接关系到整个系统的可靠性和安全性。为了实时地掌握蓄电池的运行状态&#xff0c;及时发现并解决潜在问题&#xff0c;蓄电池巡检仪应运而生。它是一种智能化的设…

Linux环境部署DeepSeek大模型

一、背景 【DeepSeek 深度求索】这个春节给了世界一个重磅炸弹&#xff0c;弄得美国都睡不好觉。这次与以往不同&#xff0c;之前我们都是跟随着美国的AI人工智能&#xff0c;现在DeepSeek通过算法上的优化&#xff0c;大大降低了训练模型所需的成本以及时间&#xff0c;短期造…

98.2 AI量化开发:基于DeepSeek打造个人专属金融消息面-AI量化分析师(理论+全套Python代码)

目录 0. 承前1. 金融工程结构图2. Why is DeepSeek3. 项目实现代码3.1 导入python库3.2 参数设置3.3 获取数据3.4 数据处理3.5 AI人设提示词3.6 Messages构建3.7 AI Agent3.8 response格式处理3.9 汇总函数3.10 运行案例 4. 总结4.1 系统优点4.2 系统缺点4.3 可提升方向 0. 承前…