C语言回调函数的使用



// Console_test.cpp : 定义控制台应用程序的入口点。
// C语言实现的回调函数

#include "stdafx.h"
#include <stdio.h>
typedef int (*CallBackFun) (char *p);

int A_fun(char *p) {  // 方法 Afun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun
        printf("A_fun 回调打印出字符%s!\n", p);
        return 0;
}

int C_fun(char *p) {    // 方法 Bfun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun
    printf("C_fun 回调打印:%s, Nice to meet you!\n", p);
    return 0;
}

int callback(CallBackFun pCallBack, char *p) {      // 执行回调函数,方式一:通过命名方式
    printf("call 直接打印出字符%s!\n", p);
    pCallBack(p);
    return 0;
}

int callback_1( char *p, int (*ptr) (char *) ) {       // 执行回调函数,方式二:直接通过方法指针
     printf("==============\n", p);
     (*ptr)(p);
  return 0;
}


int callback_2(char *p, CallBackFun pCallBack){ // 执行回调函数,方式一:通过命名方式
    printf("--------------\n", p);
    pCallBack(p);
 return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
 
    char *p = "hello wrold";
    callback(A_fun, p);
    callback(C_fun, p);

    callback_1(p, A_fun);
    callback_2(p, C_fun);

    callback_2(p, A_fun);
    callback_2(p, C_fun);
    getchar();
    return 0; 
}


1 什么是回调
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,因此,下面我们着重讨论回调机制在不同软件架构中的实现。

对于不同类型的语言(如结构化语言和对象语言)、平台(Win32、JDK)或构架(CORBA、DCOM、WebService),客户和服务的交互除了同步方式以外,都需要具备一定的异步通知机制,让服务方(或接口提供方)在某些情况下能够主动通知客户,而回调是实现异步的一个最简捷的途径。

对于一般的结构化语言,可以通过回调函数来实现回调。回调函数也是一个函数或过程,不过它是一个由调用方自己实现,供被调用方使用的特殊函数。

在面向对象的语言中,回调则是通过接口或抽象类来实现的,我们把实现这种接口的类成为回调类,回调类的对象成为回调对象。对于象C++或Object Pascal这些兼容了过程特性的对象语言,不仅提供了回调对象、回调方法等特性,也能兼容过程语言的回调函数机制。

Windows平台的消息机制也可以看作是回调的一种应用,我们通过系统提供的接口注册消息处理函数(即回调函数),从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的,我们可以认为它也是回调函数的一个特例。

对于分布式组件代理体系CORBA,异步处理有多种方式,如回调、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务,他们主要负责消息的处理、派发、维护等工作。对一些简单的异步处理过程,我们可以通过回调机制来实现。

下面我们集中比较具有代表性的语言(C、Object Pascal)和架构(CORBA)来分析回调的实现方式、具体作用等。


2  C语言中的回调

2.1 函数指针

回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此,要实现回调,必须首先定义函数指针,请看下面的例子:

void Func(char *s);
// 函数原型
void (*pFunc) (char *); 
//函数指针可以看出,函数的定义和函数指针的定义非常类似。
// 一般的化,为了简化函数指针类型的变量定义,提高程序的可读性,我们需要把函数指针类型自定义一下。 typedef void(*pcb)(char *);


回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数。

被调函数的例子:

void GetCallBack(pcb callback)
  {      /*do something*/}
用户在调用上面的函数时,需要自己实现一个pcb类型的回调函数:
void fCallback(char *s) {    /* do something */}
然后,就可以直接把fCallback当作一个变量传递给GetCallBack,GetCallBack(fCallback);
如果赋了不同的值给该参数,那么调用者将调用不同地址的函数。
赋值可以发生在运行时,这样使你能实现动态绑定。
2.2 参数传递规则到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。
如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。
C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。
将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:
// 被调用函数是以int为参数,以int为返回值__stdcall int callee(int);
// 调用函数以函数指针为参数void caller( __cdecl int(*ptr)(int));
// 在p中企图存储被调用函数地址的非法操作__cdecl int(*p)(int) = callee;
// 出错指针p和callee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列表。

2.3 应用举例

C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。

快速排序函数原型:void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));


二分搜索函数原型:void *bsearch(const void *key, const void *base, size_t nelem,
     size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
其中fcmp就是一个回调函数的变量。
 下面给出一个具体的例子:
#include <stdio.h>
#include <stdlib.h>
int sort_function( const void *a, const void *b);
int list[5] = { 54, 21, 11, 67, 22 };
int main(void)

  int  x;
  qsort( (void *) list, 5, sizeof( list[0]), sort_function );//API函数
    for (x = 0; x < 5; x++) 
     printf("%i\n", list[x]);
    return 0;

}
int sort_function( const void *a, const void *b)
{   
 return *(int*)a-*(int*)b;
}

<think>好的,我现在需要回答用户关于C语言回调函数使用示例的问题。首先,用户希望看到具体的代码示例,所以我得先确保提供的代码正确且易于理解。根据引用中的内容,用户已经有一个简单的示例,但可能需要更详细的解释或更多的例子。 首先,回顾用户提供的引用内容。引用[1]给出了一个基本的回调函数示例,包括函数原型、调用函数和实际回调函数。引用[4]提到回调函数的作用是解耦,这点需要强调。用户可能想知道为什么使用回调函数而不是普通函数调用,所以解释解耦的重要性是关键。 接下来,我应该构建一个清晰的回答结构。首先,解释回调函数的概念和作用,引用[3]提到回调函数作为参数传递,在特定事件被调用,这可以用来开头。然后,分步骤解析示例代码,说明每个部分的作用,如定义回调类型、执行操作的函数、回调的实现以及主函数中的调用。这样用户能逐步理解代码流程。 此外,用户可能希望看到不同的应用场景,比如事件处理或排序算法。引用[2]提到在C++中应优先使用其他方法,但在C语言回调函数很常见。可以举一个排序时使用回调函数比较不同数据类型的例子,展示其灵活性。这样能体现解耦的优势,如引用[4]所述。 需要注意代码的格式,确保使用正确的语法,比如函数指针的typedef,以及调用时的参数传递。同时,检查代码是否有错误,比如函数签名是否匹配,避免指针错误。例如,在比较函数中,确保参数类型正确,并返回适当的整数值。 可能用户还会对回调函数的实际应用有疑问,比如在库函数或异步操作中的使用。可以提到如GUI事件处理或网络编程中的回调,但保持示例简单,避免复杂化。重点在于通过代码示例展示机制,而不是深入复杂场景。 最后,生成相关问题,帮助用户进一步学习。问题可能涉及回调函数的优缺点、与普通函数的区别、在项目中的实际应用等,这些都能引导用户深化理解。确保问题与回答内容相关,并覆盖用户可能感兴趣的方面。 总结步骤:解释概念、分步代码解析、应用场景示例、总结优点,最后生成相关问题。需要确保语言简洁,代码正确,引用适当,并符合用户的中文要求。</think>以下是一个详细且规范的C语言回调函数使用示例及解析: ### C语言回调函数使用示例 回调函数是通过函数指针实现的编程技术,主要用于实现模块解耦和事件驱动编程[^3][^4] #### 示例代码分步解析 ```c #include <stdio.h> // 步骤1:定义回调函数类型 typedef void (*DataProcessor)(int); // 步骤2:创建接收回调的函数 void process_data(int arr[], int size, DataProcessor processor) { for(int i=0; i<size; i++) { // 处理过程中触发回调 processor(arr[i]); // 对每个元素执行回调[^1] } } // 步骤3:实现具体回调函数 void print_square(int num) { printf("%d²=%d\n", num, num*num); } void print_cube(int num) { printf("%d³=%d\n", num, num*num*num); } // 步骤4:主函数调用 int main() { int dataset[] = {2,3,5,7}; const int size = sizeof(dataset)/sizeof(int); printf("平方计算:\n"); process_data(dataset, size, print_square); printf("\n立方计算:\n"); process_data(dataset, size, print_cube); return 0; } ``` #### 代码执行结果 ``` 平方计算: 2²=4 3²=9 5²=25 7²=49 立方计算: 2³=8 3³=27 5³=125 7³=343 ``` ### 技术要点解析 1. **类型定义**:`typedef void (*DataProcessor)(int)` 明确定义回调函数签名[^1] 2. **解耦实现**:`process_data`函数不关心具体处理逻辑,通过回调参数实现功能扩展[^4] 3. **动态绑定**:主程序运行时决定使用平方还是立方计算,无需修改处理函数 4. **通用架构**:这种模式常见于事件处理、算法策略等场景[^3] ### 典型应用场景 1. 事件驱动编程(如GUI按钮点击事件) 2. 排序算法中的比较函数(qsort函数实现) 3. 异步IO操作完成通知 4. 插件系统开发
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值