囘調函數進階一:瞭解囘調函數
調用函數:
如果參數是一個函數指針,調用者可以傳遞一個函數的地址給實現者,即調用者提供一個函數但自己不去調用,而是讓實現者去調用它,這稱之為囘調函數。
囘調函數示例:
void func(void (*f)(void *),void *p);
實現過程:
調用者提供一個囘調函數,再提供一個準備傳遞給囘調函數的參數;
把囘調函數傳給參數f,把準備傳給囘調函數的參數按照void * 類型傳給參數p。
實現者呢,在適當的時候根據調用者傳來的函數指針f調用囘調函數,
將調用者傳來的參數p轉交給囘調函數,即調用f(p)。記住,此時f是一個函數指針,
真正的函數地址則是看調用者的傳遞了。
應用實例:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
typedef void (*callback_t)(void *);
void my_func(callback_t,void *);
/*
* 當然,正規的定義應該是這樣的:
* void my_func(void (*callback_t)(void *),void *p);
* 使用 typedef 會更清晰一些。
*/
void my_func(callback_t f,void *para)
{
f(para);
}
static void say_hello(void *str)
{
printf("%s\n",(const char *)str);
}
static void count_num(void *num)
{
printf("%d\n",(int)num);
}
int main(int argc, char *argv[])
{
my_func(say_hello,"Hello World!");
my_func(count_num,2);
return 0;
}
通俗的講,囘調函數實現了程序處理的自由性,靈活性。同樣的都是void *p 參數,
但是另一個函數,函數指針的地址(也就是囘調函數)不同,
因此可以通過對同一個參數傳遞不同的函數來處理不同的操作。
再比如下面這個示例:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
typedef int (*cmp_t)(const void *,const void *);
void *max(const void *base,size_t nmemb,size_t size,cmp_t cmp);
void *max(const void *base,size_t nmemb,size_t size,cmp_t cmp)
{
size_t i;
const char *_base = base;
const char *temp = _base;
for(i=0;i<nmemb;i++){
if(cmp(temp,_base+size*i)<0)
temp = _base + size*i;
}
return (void *)temp;
}
typedef struct {
const char *name;
int score;
}student_t;
static int cmp_student(const void *a,const void *b);
{
if(((student_t *)a)->score > ((student_t *)b)->score)
return 1;
else if(((student_t)a)->score == ((student_t *)b)->score)
return 0;
else
return -1;
}
int main(int argc,char **argv)
{
student_t list[4] = {{"Tom",68},{"Jerry",72},{"Moby",60},{"Kirby",89}};
student_t *pmax = max(list,sizeof(list)/sizeof(student_t),sizeof(student_t),cmp_student);
printf("%s gets the highest score %d\n",pmax->name,pmax->score);
exit(0);
}
囘調函數進階二:深入囘調函數
同步囘調:
在上面的例子中,囘調函數是被同步調用的。my_func()調用say_hello(),相當於調用者直接調用了自己提供的囘調函數。
比如,將main函數中的第一行替換成:sya_hello("Hello World!");也是完全可以的。
異步調用:
比起同步調用,異步調用也是很典型的。調用者首先將囘調函數傳給實現者,實現者記住這個函數,這稱為“註冊”一個囘調函數,然後當某個事件發生時實現者再調用先前註冊的函數。
比如信號處理函數sigaction(),首先註冊一個信號處理函數,當此信號發生的時候由操作系統調用該函數進行處理。
再比如創建線程函數pthread_create()註冊一個線程函數,當發生調度時操作系統切換到新註冊的線程函數中運行。
“從這個角度來看,凡是系統api參數中有函數指針,大多(or全部?)是這種囘調函數的形式”。
囘調函數進階三:提升囘調函數
總結:簡單來說,在層次化程序設計中,一般都是上層函數調用下層函數,逐級調用。
而如果把函數的參數變為函數地址(也就是函數指針),那麼就可以實現函數的囘調,
也就是不必一直向下層調用。這樣,我們就稱之為囘調函數,這樣實現了層次化編程
中的函數自由、靈活的調用。
如果自己提供了一個囘調函數并實現了囘調函數,將之編譯成lib庫或是其他的形式。
那麼在應用中,一旦需要這個囘調函數,系統就會在lib庫中按照名字尋找。
1.可以實現動態綁定。通過運行時傳遞不同的函數地址,執行不同的操作。可以理解為多态。
2.可以實現消息通知和事件驅動。比如程序中設置一個計時器,到達指定時間則通知程序。
3.提供了一種代碼動態嵌入的方式,實現函數內部中斷的一種方式。比如程序出錯,程序需要額外處理,
可以嵌入一個囘調函數,啟動一個單獨線程去執行任務。而程序則繼續執行自己的後續代碼。
實際上,創建線程函數就是一個典型應用。
pthread_create(),其中有一個參數就是函數執行的地址,還有一個參數是參數。
ps:囘調函數都是全局函數、或者是靜態函數。想想這是爲什麽?
http://hi.baidu.com/gtfcugb/blog/item/57b836dd6e11f5e976c638f1.html