一、c言语回调函数的完成
1. 什么是回调函数
简而言之,回调函数便是一个经过函数指针调用的函数。假如你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,咱们就说这是回调函数。
2. 为什么要运用回调函数
由于能够把调用者与被调用者分隔。调用者不关心谁是被调用者,一切它需知道的,仅仅存在一个具有某种特定原型、某些约束条件(如回来值为int)的被调用函数。
程序员常常需求完成回调。本文将评论函数指针的基本原则并阐明怎么运用函数指针完成回调。留意这儿针对的是一般的函数,不包括彻底依赖于不同语法和语义规矩的类成员函数(类成员指针将在另文中评论)。
3. 声明函数指针
回调函数是一个程序员不能显式调用的函数;经过将回调函数的地址传给调用者然后完成调用。要完成回调,有必要首要界说函数指针。虽然界说的语法有点难以想象,但假如你了解函数声明的一般办法,便会发现函数指针的声明与函数声明十分相似。请看下面的比如:
void f();// 函数原型
上面的句子声明晰一个函数,没有输入参数并回来void。那么函数指针的声明办法如下:
void (*) ();
让咱们来剖析一下,左面圆括弧中的星号是函数指针声明的要害。别的两个元素是函数的回来类型(void)和由边圆括弧中的进口参数(本例中参数是空)。留意本例中还没有创立指针变量-仅仅声明晰变量类型。现在能够用这个变量类型来创立类型界说名及用sizeof表达式取得函数指针的巨细:
// 取得函数指针的巨细
unsigned psize = sizeof (void (*) ());
// 为函数指针声明类型界说
typedef void (*pfv) ();
pfv是一个函数指针,它指向的函数没有输入参数,回来类行为void。运用这个类型界说名能够躲藏杂乱的函数指针语法。
指针变量应该有一个变量名:
void (*p) (); //p是指向某函数的指针
p是指向某函数的指针,该函数无输入参数,回来值的类型为void。左面圆括弧里星号后的便是指针变量名。有了指针变量便能够赋值,值的内容是署名匹配的函数名和回来类型。例如:
void func()
{
/* do something */
}
p = func;
p的赋值能够不同,但必定要是函数的地址,而且署名和回来类型相同。
4. 传递回调函数的地址给调用者
现在能够将p传递给另一个函数(调用者)- caller(),它将调用p指向的函数,而此函数名是不知道的:
void caller(void(*ptr)())
{
ptr(); /* 调用ptr指向的函数 */
}
void func();
int main()
{
p = func;
caller(p); /* 传递函数地址到调用者 */
}
假如赋了不同的值给p(不同函数地址),那么调用者将调用不同地址的函数。赋值能够发生在运行时,这样使你能完成动态绑定。
5. 调用标准
到现在为止,咱们只评论了函数指针及回调而没有去留意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,虽然两者有相同的回来值和参数列。
二、在STM32中的回调函数的界说
上层程序调用基层函数是惯例性的操作,而基层函数(usb_init相对于usb_prop是输入底层操作文件)调用上层文件函数咱们称之为回调。回调函数的含义在于同一种操作形式、供给不同的回调函数则能够完成不同的功用。Windows中处理音讯,如同也用到了这种形式。回调函数的完成办法是函数指针数组,这是指针的高档使用