程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 關於回調函數

關於回調函數

編輯:關於C

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
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved