1.什麼是回調函數。
在FreeRDP的項目中看到,幾乎所有的繪制工作都是通過回調函數完成的。究竟什麼是回調函數呢?要了解回調函數,還得中函數指針說起。那函數指針又是什麼呢?它跟指針函數有什麼關系呢?請看下面:
一般我們是這樣聲明一個函數的,
int func(int params, ...);
相信大家對函數最熟悉不過了,這是個返回值為int的函數。稍微變形一下就成了指針函數了,請看:
int* func(int params, ...);
一個函數不僅可以帶回一個整型數據的值,字符類型值和實型類型的值,還可以帶回指針類型的數據,使其指向某個地址單元。當一個函數的返回值是一個指針時,該函數就是一個指針函數。那跟指針函數有什麼關系呢?先看看指針函數的定義吧。
在程序運行中,函數代碼是程序的算法指令部分,它們和數組一樣也占用存儲空間,都有相應的地址。可以使用指針變量指向數組的首地址,也可以使用指針變量指向函數代碼的首地址,指向函數代碼首地址的指針變量稱為函數指針。
int func(int x); /* 聲明一個函數 */
int (*f) (int x); /* 聲明一個函數指針 */
f=func; /* 將func函數的首地址賦給指針f */
回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用為調用它所指向的函數時,我們就說這是回調函數。
2.為什麼要使用回調函數
因為可以把調用者與被調用者分開。調用者不關心誰是被調用者,所有它需知道的,只是存在一個具有某種特定原型、某些限制條件(如返回值為int)的被調用函數。
如果想知道回調函數在實際中有什麼作用,先假設有這樣一種情況,我們要編寫一個庫,它提供了某些排序算法的實現,如冒泡排序、快速排序、shell排序、shake排序等等,但為使庫更加通用,不想在函數中嵌入排序邏輯,而讓使用者來實現相應的邏輯;或者,想讓庫可用於多種數據類型(int、float、string),此時,該怎麼辦呢?可以使用函數指針,並進行回調。
回調可用於通知機制,例如,有時要在程序中設置一個計時器,每到一定時間,程序會得到相應的通知,但通知機制的實現者對我們的程序一無所知。而此時,就需有一個特定原型的函數指針,用這個指針來進行回調,來通知我們的程序事件已經發生。實際上,SetTimer() API使用了一個回調函數來通知計時器,而且,萬一沒有提供回調函數,它還會把一個消息發往程序的消息隊列。
不管怎麼說,回調函數是繼續自C語言的,因而,在C++中,應只在與C代碼建立接口,或與已有的回調接口打交道時,才使用回調函數。除了上述情況,在C++中應使用虛擬方法或函數符(functor),而不是回調函數。
3.回調函數的實現
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <malloc.h>
/*
File: darray.c:回調函數冒泡排序函數實現
Author: ecorefeng
Created on 2010年8月
*/
/*
*定義一個用於自動測試的宏(關於自動測試請閱讀我的博客文章《自動測試的優劣》)
*/
#define return_val_if_fail(p, val)\
if(!(p)){printf("%s:%d"#p" failed", func, LINE); return val; }
/*
*定義一個回調函數原型,用於回調
*/
typedef int (*CompFunc)(void *ctx, void *data);
/*
*功能:實現冒泡排序
*參數:array:要排序的數組 compfunc:回調的用於比較的函數 array_len:數組長度
*返回:
*/
int darray_b_sort(void **array,CompFunc compfunc, int array_len)
{
return_val_if_fail(array !=NULL&&compfunc !=NULL, 1);
int len = 0;
int max = 0;
int index = 0;
for(len = array_len - 1; len > 0; len--)
{
for(index = 1, max = 0; index < len; index++)
{
if(compfunc(array[index], array[max]) > 0)
{
max = index;
}
}
if(compfunc(array[max], array[len]) > 0)
{
void *data = array[max];
array[max] = array[len];
array[len] = data;
}
//int i = 0;
//for(i = 0; i < 6; i++)
//{
//printf("%d\n",array[i]);
// assert(array[i] >= array[i-1]);
//}
//printf(".......................\n");
}
return 0;
}
/*
*功能:隨機生成一個數組
*參數:num:數組長度
*返回:數組指針
*/
static void **int_array_create(int num)
{
int i = 0;
int *array = (int *)malloc(sizeof(int) * num);
for(i = 0; i < num; i++)
{
array[i] = rand()%100;
printf("%d\t",array[i]);
}
printf("***********************\n");
return(void **)array;
}
/*
*功能:實現比較
*參數:compfunc:回調的用於比較的函數 num:數組長度
*返回:數組指針
*/
static void sort_test_asc_or_desc(CompFunc compfunc, int num)
{
void **array = int_array_create(num);
darray_b_sort(array, compfunc, num);
int i = 0;
for(i = 0; i < num; i++)
{
printf("%d\t",(int)array[i]);
// assert(array[i] >= array[i-1]);
}
free(array);
}
/*
*功能:實現比較函數(升序)
*參數:指針
*返回:比較結果:大於0、小於0;
*/
int int_sort_compfunc_asc(void *num1, void *num2)
{
return (int)num1 - (int)num2;
}
/*
*功能:實現比較函數(降序)
*參數:指針
*返回:比較結果:大於0、小於0;
*/
int int_sort_compfunc_desc(void *num1, void *num2)
{
return (int)num2 - (int)num1;
}
int main(int argc, char *argv[])
{
sort_test_asc_or_desc(int_sort_compfunc_asc, 20);
printf("................\n");
sort_test_asc_or_desc(int_sort_compfunc_desc, 10);
return 0;
}
回調函數的形式實現的升降序的冒泡排序算法例子。
感謝ecorefeng提供的程序例子。
回頭看看FreeRDP的項目,正是由於回調函數的使用,使得FreeRDP的移植變得簡單多了。例如我是用skia庫實現rdp的繪制函數時,不需要了解整個的調用過程,我只需要實現回調函數調用的那些函數,然後注冊這些回調函數就可以了。
由於知識有限,難免有錯,歡迎大家指正,謝謝。
摘自 hopetribe