本文旨在记录对回调函数及可变参函数的一些理解,不恰当的地方欢迎指正
简介
文章内容分为回调函数和可变参函数的原理分析和基础实现
回调函数
首先所谓回调函数,是用户将自己的函数实现交给第三方接口,拿常用的定时器举个例子:
void sys_timer_add(void *_para, void (*callback)(void *para), u32 time)
{
while (time--) {
do something;
}
if (_para && callback) {
callback(_para);
}
}
typedef struct {
char *str;
short num;
char type;
} CALLBACK;
void CallBackFunc(void *_para)
{
CALLBACK *callback = (CALLBACK *callback)_para;
do something;
}
CALLBACK callback;
u32 time;
//给callback、time赋值后,作为参数输入
sys_timer_add((void *)&callback, CallBackFunc, time)
以上sys_timer_add函数是我们调用的库函数,函数的内部有一个callback分支,当我们有自己的回调函数时会在sys_timer_add内部触发
另外回调函数在传参时最好使用无类型(void)数据,也就是上面代码中CallBackFunc传到sys_timer_add的callback参数会通过(void *)强转,最后在回调函数CallBackFunc中转回实际类型,这样的作用是让sys_timer_add有更强兼容性;上述sys_timer_add函数只是用来举例,实际代码并非如此
按我的理解,回调函数的一个重要的作用就是让用户在不接触库代码的情况下,可以让原本库里面执行流程多出一个可变化的代码,用户通过自定义回调函数让其在库里面的某个位置执行
可变参函数
最常见的可变参函数是printf和scanf,该功能实现的基础是va_start、va_arg、va_end三个宏,定义在标准库stdarg.h中,参考下图中的注释了解这三个宏的作用
这里给出一个简单的例子:
typedef void (*FUNC)(int i, char c, float f, double d, char *str);
void VarArgFunc(char *arg, ...)
{
va_list pArgs;
va_start(pArgs, arg);
FUNC func = va_arg(pArgs, FUNC);
int i = va_arg(pArgs, int);
char c = va_arg(pArgs, int);
float f = va_arg(pArgs, double);
double d = va_arg(pArgs, double);
char *str = va_arg(pArgs, char *);
/* (*func)(i, c, f, d, str); */
func(i, c, f, d, str);
printf(">>>>> 0x%x 0x%x 0x%x\n", func, (*func), &(*func));
/* void *v = va_arg(pArgs, (char *)); */
va_end(pArgs);
}
void custem_func(int i, char c, float f, double d, char *str)
{
printf("int i:%d\n", i);
printf("char c:%c\n", c);
printf("float f:%f\n", f);
printf("double d:%f\n", d);
printf("char *str:%s\n", str);
}
int main()
{
printf("start Callback_VarArgFunc!\n");
int i = 10;
char c = 'y';
double f = 12.0123;
double d = 23.56;
char *str = "this is a test!";
VarArgFunc("ifdcv\n", custem_func, i, c, f, d, str);
printf("finish Callback_VarArgFunc!\n");
return 0;
}
这里VarArgFunc是可变函数名,填入参数包括回调函数地址、若干不同类型变量,最后通过回调函数将不同变量打印出来
这里有几点需要注意的是:va_arg宏虽然可以将传入的char、short、float类型解析出来,但类型需要用int、double,具体原因可以看看文章开头引用的文档;此外要精确地解析出变量个数,需要类似printf那种在固定参数里去判断,va_arg好像并不能判断当前是否是最后一个参数
这里我有一个疑惑:在VarArgFunc函数实体里,为什么得到的func函数,func、 (*func)、 &(*func)打印出的三个值是一样的?
以下贴出可变参函数用到的代码实例:
Callback_VarArgFunc.c
#include "Callback_VarArgFunc.h"
#include "stdarg.h"
#include "stddef.h"
typedef void (*FUNC)(int i, char c, float f, double d, char *str);
void VarArgFunc(char *arg, ...)
{
va_list pArgs;
va_start(pArgs, arg);
FUNC func = va_arg(pArgs, FUNC);
int i = va_arg(pArgs, int);
char c = va_arg(pArgs, int);
float f = va_arg(pArgs, double);
double d = va_arg(pArgs, double);
char *str = va_arg(pArgs, char *);
/* (*func)(i, c, f, d, str); */
func(i, c, f, d, str);
printf(">>>>> 0x%x 0x%x 0x%x\n", func, (*func), &(*func));
/* void *v = va_arg(pArgs, (char *)); */
va_end(pArgs);
}
Callback_VarArgFunc.h
#ifndef __CALLBACK_VARARGFUNC__
#define __CALLBACK_VARARGFUNC__
#include "string.h"
typedef unsigned short u16;
typedef unsigned char u8;
void VarArgFunc(char *arg, ...);
#endif
main.c
#include "string.h"
#include "stdarg.h"
#include "Callback_VarArgFunc.h"
void custem_func(int i, char c, float f, double d, char *str)
{
printf("int i:%d\n", i);
printf("char c:%c\n", c);
printf("float f:%f\n", f);
printf("double d:%f\n", d);
printf("char *str:%s\n", str);
}
int main()
{
printf("start Callback_VarArgFunc!\n");
int i = 10;
char c = 'y';
double f = 12.0123;
double d = 23.56;
char *str = "this is a test!";
VarArgFunc("ifdcv\n", custem_func, i, c, f, d, str);
printf("finish Callback_VarArgFunc!\n");
return 0;
}
Makefile
exe: Callback_VarArgFunc.c main.c
gcc Callback_VarArgFunc.c main.c -o exe