C 语言中如何进行函数指针的回调?

news2024/9/9 1:06:48

C语言

🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!
📙C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】通俗易懂,深入浅出,匠心打磨,死磕细节,6年迭代,看过的人都说好。

分割线

文章目录

  • C 语言中函数指针的回调
  • 一、函数指针的概念
  • 二、回调函数的概念
  • 三、函数指针回调的优势
  • 四、函数指针回调的实现步骤
  • 五、示例
  • 六、更复杂的示例:排序算法中的回调
  • 七、回调函数中的错误处理
  • 八、回调函数与多线程
  • 九、回调函数与异步操作
  • 十、总结

分割线


C 语言中函数指针的回调

在 C 语言中,函数指针的回调是一种强大的编程技术,它允许我们在特定的事件发生或特定的条件满足时,调用由用户定义的函数。这种机制增加了程序的灵活性和可扩展性,使得代码更具通用性和可重用性。

一、函数指针的概念

函数指针是一个指向函数的指针变量。它存储了函数的地址,通过这个地址可以调用该函数。

函数指针的声明形式如下:

返回值类型 (*指针变量名)(参数列表);

例如,定义一个指向返回值为 int 类型,有两个 int 类型参数的函数指针:

int (*func_ptr)(int, int);

二、回调函数的概念

回调函数是由调用者提供给被调用者的函数指针,当特定的事件发生时,被调用者会调用这个回调函数来通知调用者。

三、函数指针回调的优势

  1. 解耦代码
    通过使用函数指针回调,可以将主逻辑与具体的处理逻辑分离,使得代码更易于理解和维护。

  2. 增加灵活性
    可以在运行时动态地决定调用哪个具体的函数,而不需要在编译时就确定。

  3. 提高代码复用性
    可以将通用的框架与特定的功能实现分开,相同的框架可以使用不同的回调函数来实现不同的行为。

四、函数指针回调的实现步骤

  1. 定义回调函数
    首先,需要定义一个符合特定签名的函数,作为回调函数。

  2. 声明函数指针
    声明一个指向回调函数类型的指针。

  3. 将函数指针传递给调用函数
    在需要进行回调的地方,将函数指针作为参数传递给相应的函数。

  4. 在被调用函数中调用回调函数
    在接收到函数指针后,在合适的时机通过函数指针调用回调函数。

五、示例

以下是一个简单的示例,展示了如何在 C 语言中实现函数指针的回调。

#include <stdio.h>

// 定义回调函数类型
typedef void (*CallbackFunction)(int);

// 回调函数实现
void myCallback(int value) {
    printf("Callback received value: %d\n", value);
}

// 执行回调的函数
void performCallback(CallbackFunction callback, int data) {
    callback(data);
}

int main() {
    // 声明函数指针并指向回调函数
    CallbackFunction ptr = myCallback;

    // 调用执行回调的函数,并传递函数指针和数据
    performCallback(ptr, 42);

    return 0;
}

在上述示例中:

  • 首先,定义了一个 CallbackFunction 类型的函数指针,它指向一个接受一个 int 类型参数且无返回值的函数。
  • myCallback 函数是具体的回调函数实现,它会打印接收到的值。
  • performCallback 函数接受一个 CallbackFunction 类型的函数指针和一个 int 类型的数据,在函数内部调用传递进来的回调函数,并将数据作为参数传递给它。
  • main 函数中,声明了一个函数指针 ptr 并将其指向 myCallback 函数,然后调用 performCallback 函数,并传递 ptr42 作为参数,从而实现了回调。

六、更复杂的示例:排序算法中的回调

下面是一个更复杂的示例,展示在排序算法中如何使用函数指针回调来实现不同的比较策略。

#include <stdio.h>
#include <stdlib.h>

// 交换两个元素的位置
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 冒泡排序函数,使用回调函数进行比较
void bubbleSort(int arr[], int n, int (*compare)(int, int)) {
    int i, j;
    for (i = 0; i < n - 1; i++) {
        for (j = 0; j < n - i - 1; j++) {
            if (compare(arr[j], arr[j + 1])) {
                swap(&arr[j], &arr[j + 1]);
            }
        }
    }
}

// 升序比较函数
int ascendingCompare(int a, int b) {
    return a > b;
}

// 降序比较函数
int descendingCompare(int a, int b) {
    return a < b;
}

// 打印数组函数
void printArray(int arr[], int size) {
    int i;
    for (i = 0; i < size; i++)
        printf("%d ", arr[i]);
    printf("\n");
}

int main() {
    int arr1[] = {64, 34, 25, 12, 22, 11, 90};
    int arr2[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr1) / sizeof(arr1[0]);

    printf("Original array: ");
    printArray(arr1, n);

    // 按升序排序
    bubbleSort(arr1, n, ascendingCompare);
    printf("Sorted in ascending order: ");
    printArray(arr1, n);

    printf("Original array: ");
    printArray(arr2, n);

    // 按降序排序
    bubbleSort(arr2, n, descendingCompare);
    printf("Sorted in descending order: ");
    printArray(arr2, n);

    return 0;
}

在这个示例中:

  • swap 函数用于交换两个元素的位置。
  • bubbleSort 函数是冒泡排序的实现,它接受一个整数数组、数组的大小和一个函数指针 compare ,用于比较两个元素的大小。
  • ascendingComparedescendingCompare 分别是升序和降序的比较函数。
  • main 函数中,首先定义了两个待排序的数组,然后分别使用升序和降序的比较回调函数对数组进行排序,并打印排序前后的数组。

通过这种方式,我们可以通过传递不同的比较函数来实现不同的排序顺序,而不需要修改排序算法的主体逻辑,体现了函数指针回调的灵活性和可扩展性。

七、回调函数中的错误处理

在回调函数中,进行错误处理是非常重要的。因为回调函数通常是在其他函数的上下文中被调用,错误信息的传递和处理需要特别注意。

#include <stdio.h>

// 定义错误码枚举
typedef enum {
    ERROR_NONE = 0,
    ERROR_INVALID_INPUT,
    ERROR_OUT_OF_MEMORY
} ErrorCode;

// 定义回调函数类型,包含错误码返回值
typedef ErrorCode (*CallbackFunctionWithError)(int, int, ErrorCode*);

// 回调函数实现,处理错误
ErrorCode myCallbackWithError(int value1, int value2, ErrorCode* error) {
    if (value1 < 0 || value2 < 0) {
        *error = ERROR_INVALID_INPUT;
        return ERROR_INVALID_INPUT;
    }
    // 正常处理逻辑
    printf("Callback received values: %d and %d\n", value1, value2);
    *error = ERROR_NONE;
    return ERROR_NONE;
}

// 执行回调的函数,处理错误返回
void performCallbackWithError(CallbackFunctionWithError callback, int data1, int data2) {
    ErrorCode error;
    ErrorCode result = callback(data1, data2, &error);
    if (result!= ERROR_NONE) {
        printf("Error occurred: ");
        switch (error) {
            case ERROR_INVALID_INPUT:
                printf("Invalid input\n");
                break;
            case ERROR_OUT_OF_MEMORY:
                printf("Out of memory\n");
                break;
            default:
                printf("Unknown error\n");
        }
    }
}

int main() {
    // 声明函数指针并指向回调函数
    CallbackFunctionWithError ptr = myCallbackWithError;

    // 正常调用
    performCallbackWithError(ptr, 5, 10);

    // 错误调用
    performCallbackWithError(ptr, -5, 10);

    return 0;
}

在上述示例中:

  • 定义了一个包含错误码返回值的回调函数类型 CallbackFunctionWithError
  • myCallbackWithError 回调函数在输入值无效时设置错误码并返回错误。
  • performCallbackWithError 函数在调用回调函数后,检查返回的错误码,并进行相应的错误处理。

这样可以在回调函数中有效地处理各种错误情况,并将错误信息传递回调用者进行适当的处理。

八、回调函数与多线程

在多线程环境中,使用函数指针回调需要注意线程安全问题。

#include <stdio.h>
#include <pthread.h>

// 共享数据
int sharedData = 0;

// 互斥锁
pthread_mutex_t mutex;

// 回调函数
void* myCallbackInThread(void* arg) {
    pthread_mutex_lock(&mutex);
    sharedData++;
    printf("In callback: Shared data is now %d\n", sharedData);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

// 线程函数
void* threadFunction(void* arg) {
    // 调用回调函数
    myCallbackInThread(NULL);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 创建线程
    pthread_create(&thread1, NULL, threadFunction, NULL);
    pthread_create(&thread2, NULL, threadFunction, NULL);

    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);

    return 0;
}

在这个示例中:

  • 有一个共享的整数 sharedData 和一个互斥锁 mutex
  • myCallbackInThread 是回调函数,它在操作共享数据前加锁,操作完成后解锁,以保证线程安全。
  • threadFunction 是线程函数,它调用回调函数。
  • main 函数中创建了两个线程来执行 threadFunction

通过使用互斥锁来保护共享数据,确保在多线程环境中回调函数对共享资源的访问是安全的。

九、回调函数与异步操作

函数指针回调在处理异步操作时也非常有用。

#include <stdio.h>
#include <unistd.h>

// 异步操作完成的回调函数
void asyncOperationComplete(int result, void (*callback)(int)) {
    printf("Async operation completed with result: %d\n", result);
    callback(result);
}

// 异步操作完成后的处理回调函数
void handleAsyncResult(int result) {
    printf("Handling async result: %d\n", result);
}

int main() {
    // 模拟异步操作
    asyncOperationComplete(42, handleAsyncResult);

    return 0;
}

在上述示例中:

  • asyncOperationComplete 函数模拟异步操作完成,并调用传递进来的回调函数 callback 来通知操作的结果。
  • handleAsyncResult 函数是处理异步操作结果的回调函数。

通过这种方式,可以在异步操作完成后及时进行相应的处理,而不需要阻塞等待操作完成。

十、总结

函数指针的回调是 C 语言中一种强大而灵活的编程技术,它能够实现代码的解耦、提高灵活性和可扩展性、增强代码的复用性。通过合理地设计回调函数和使用函数指针,可以编写出更优雅、更高效、更易于维护的 C 语言程序。无论是在简单的程序结构中,还是在复杂的多线程、异步操作和排序算法等场景中,函数指针回调都能发挥重要的作用。但在使用过程中,需要注意错误处理、线程安全等问题,以确保程序的正确性和稳定性。


分割线

🎉相关推荐

  • 📙C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】 通俗易懂,深入浅出,匠心打磨,死磕细节,6年迭代,看过的人都说好。
  • 🍅博客首页-关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!
  • 📙CSDN专栏-C语言修炼
  • 📙技术社区-墨松科技

分割线



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1917247.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系统架构设计师 - 数学与经济管理

数学与经济管理 数学与经济管理&#xff08;1 - 2分&#xff09;图论应用最小生成树最短路径网络与最大流量 ★ 运筹方法关键路径法 ★ ★ ★线性规划 ★动态规划 ★ ★ ★排队论预测与决策 ★预测 - 博弈论决策 数学建模 ★ ★ 大家好呀&#xff01;我是小笙&#xff0c;本章我…

Java版Flink使用指南——将消息写入到RabbitMQ的队列中

大纲 新建工程新增依赖 编码自动产生数据写入RabbitMQ 测试工程代码 在 《Java版Flink使用指南——从RabbitMQ中队列中接入消息流》一文中&#xff0c;我们介绍了如何使用Java在Flink中读取RabbitMQ中的数据&#xff0c;并将其写入日志中。本文将通过代码产生一些数据&#xf…

OTA与OTA升级

目录 一、OTA简介 二、OTA升级 三、操作方式 一、OTA简介 在嵌入式领域当中&#xff0c;OTA&#xff08;Over-The-Air&#xff09;指的是通过无线通信技术对嵌入式设备的软件进行远程更新和管理。这种技术广泛应用于物联网设备、智能家电、汽车电子、智能手机等领域。通过OTA…

基于 PyTorch 的迁移学习介绍 (图像分类实战演示)

1. 介绍 迁移学习&#xff08;Transfer Learning&#xff09;允许我们采用另一个模型从另一个问题中学到的模式&#xff08;也称为权重&#xff09;并将它们用于我们自己的问题。 例如&#xff0c;我们可以采用计算机视觉模型从 ImageNet&#xff08;包含数百万张不同对象的图…

51单片机嵌入式开发:8、 STC89C52RC 操作LCD1602原理

STC89C52RC 操作LCD1602原理 1 LCD1602概述1.1 LCD1602介绍1.2 LCD1602引脚说明1.3 LCD1602指令介绍 2 LCD1602外围电路2.1 LCD1602接线方法2.2 LCD1602电路原理 3 LCD1602软件操作3.1 LCD1602显示3.2 LCD1602 protues仿真 4 总结 1 LCD1602概述 1.1 LCD1602介绍 LCD1602是一种…

java如何判断某个数在区间是否存在?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

数字园区新视界:智慧管理的全景体验

通过图扑可视化技术&#xff0c;智慧园区管理实现实时监控与数据分析&#xff0c;优化各类资源的配置与使用&#xff0c;提高整体运营效率与智能决策能力。

Java中关于File类的详解

File类 File类是文件和目录路径名称的抽象表示&#xff0c;主要用于文件和目录的创建、查找和删除等操作。在创建File对象的时候&#xff0c;需要传递一个路径&#xff0c;这个路径定位到哪个文件或者文件夹上&#xff0c;File就代表哪个对象。 File file new File("D:…

记一次Ueditor上传Bypss

前言 前一段时间和小伙伴在某内网进行渗透测试&#xff0c;目标不给加白&#xff0c;只能进行硬刚了&#xff0c;队友fscan一把梭发现某资产疑似存在Ueditor组件&#xff0c;但初步测试是存在waf和杀软的&#xff0c;无法进行getshell&#xff0c;经过一番折腾最终getshell&am…

揭秘”大模型加速器”如何助力大模型应用

文章目录 一、大模型发展面临的问题二、“大模型加速器”助力突破困难2.1 现场效果展示2.1.1 大模型加速器——文档解析引擎2.2.2 图表数据提取 三、TextIn智能文档处理平台3.1 在线免费体验3.1.1 数学公式提取3.1.2 表格数据提取 四、acge文本向量化模型4.1 介绍4.2 技术创新4…

Python基础语法:运算符详解(算术运算符、比较运算符、逻辑运算符、赋值运算符)②

文章目录 Python中的运算符详解一、算术运算符二、比较运算符三、逻辑运算符四、赋值运算符五、综合示例结论 Python中的运算符详解 在Python编程中&#xff0c;运算符用于执行各种操作&#xff0c;例如算术计算、比较、逻辑判断和赋值。了解并掌握这些运算符的使用方法是编写…

CTF php RCE (四)

0x08 取反以及异或、或 这两个东西呢相当的好玩&#xff0c;也能够达到一下小极限的操作 <?php error_reporting(0); if(isset($_GET[code])){$code$_GET[code];if(strlen($code)>40){die("This is too Long.");}if(preg_match("/[A-Za-z0-9]/",$…

Firealpaca 解锁版下载及安装教程 (火焰羊驼绘画软件)

前言 FireAlpaca是一款简单易用的电脑绘画软件&#xff0c;采用了类似于Photoshop的图层绘画方式。对于喜欢手绘和创作漫画的朋友来说&#xff0c;FireAlpaca的多图层功能使得绘画过程更加便捷和简单。作为一个小型图像编辑软件&#xff0c;它能够轻松处理多个图层或手绘图&am…

拥抱UniHttp,规范Http接口对接之旅

前言 如果你项目里还在用传统的编程式Http客户端比如HttpClient、Okhttp去直接对接第三方Http接口&#xff0c; 那么你项目一定充斥着大量的对接逻辑和代码&#xff0c; 并且针对不同的对接渠道方需要每次封装一次调用的简化&#xff0c; 一旦封装不好系统将会变得难以维护&am…

策略模式(大话设计模式)C/C++版本

策略模式 商场收银软件 根据客户所购买商品的单价和数量来收费 需求分析&#xff1a; 1. 输入单价数量 > 界面逻辑 2. 计算&#xff08;可能打折或者促销&#xff09; > 业务逻辑 3. 输出结果 > 界面逻辑感觉和计算器的逻辑流程差不多&#xff0c;可以用简单工厂模式…

浪潮天启防火墙TQ2000远程配置方法SSL-xxx、L2xx 配置方法

前言 本次设置只针对配置VXX&#xff0c;其他防火墙配置不涉及。建议把防火墙内外网都调通后再进行Vxx配置。 其他配置可参考&#xff1a;浪潮天启防火墙配置手册 配置SSLVxx 在外网端口开启SSLVxx信息 开启SSLVxx功能 1、勾选 “启用SSL-Vxx” 2、设置登录端口号&#xff0…

Unity3D 太空大战射击游戏

一、前言 本案例是初级案例&#xff0c;意在帮助想使用unity的初级开发者能较快的入门&#xff0c;体验unity开发的方便性和简易性能。 本次我们将使用团结引擎进行开发&#xff0c;帮助想体验团结引擎的入门开发者进行较快的环境熟悉。 本游戏案例以太空作战为背景&#xff0c…

如何分析软件测试中发现的Bug!

假如你是一名软件测试工程师&#xff0c;每天面对的就是那些“刁钻”的Bug&#xff0c;它们像是隐藏在黑暗中的敌人&#xff0c;时不时跳出来给你一个“惊喜”。那么&#xff0c;如何才能有效地分析和处理这些Bug&#xff0c;让你的测试工作变得高效且有趣呢&#xff1f;今天我…

Threadlocal使用获取最后更新人信息

Threadlocal 的作用范围是一个线程&#xff0c;tomcat启动默认开启一个线程 首先点击登录&#xff0c;登录方法会返回token 拿到token后放在请求头中发送商品的插入请求&#xff0c;在插入是设置拿到token中的nickName&#xff08;花名&#xff09;放入&#xff08;lastUpdate…

C 语言中如何实现字符串的拼接?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&…