回调函数到底是怎么一回事呢
今天看到回調函數,有點迷糊,找了好多搜索引擎的資料,都不是讓我很能理解,看了《c和指針》我才明白了。
簡單描述一下什么是回調函數:
用戶把一個函數指針作為參數傳遞給其他函數,后者將“回調”用戶的函數。如果函數可以再不同的時間執行不同類型的工作或者執行只能由函數調用者定義的工作,都可以使用回調函數。?回調函數無法知道比較的值的類型,所以參數的類型被聲明為void*。表示一個指向未知類型的指針。?可以通過函數指針來實現回調函數。一個指向回調函數的指針作為參數傳遞給另一個函數,后者使用這個指針調用回調函數。?
可能說了太多定義也不會很是明白,來幾個事例說說。
當我們在在鏈表中查找一個數時,我們一般會這樣寫:
1 Node *search_list( Node *node, int const value )2 {3 while ( NULL != node ){4 if ( node->value == value ){5 break;6 }7 node = node->link;8 }9 10 return node; 11 }這樣就限制我們只能在查找的數必須是int類型,當變為其他類型時我們就無法用這個函數,但是重新寫一個函數,他們重復代碼又太多。那我們看看用回調函數如何辦到?! ?/p>
回調函數查找:
?
1 int compare_int( void const *a, void const *b ) 2 { 3 if ( *( int * )a == *( int * )b ){ 4 return 0; 5 } 6 7 return 1; 8 }?
1 Node *search_list(Node *node, void const *value, 2 int (*compare)(void const *, void const *)) //函數指針3 {4 while(node != NULL){5 if(compare(&node->value, value) == 0) //相等6 break;7 node = node->link;8 }9 return node; 10 }?
這樣利用回調函數就可以解決如上問題。我們把一個函數指針(?int (*compare)(void const *, void const *)?)作為參數傳遞給查找函數,查找函數將“回調”比較函數。當我們需要執行不同類型的比較時我們合理調用該函數。例如:當我們整形查找時:?search_list( root, &desired_value, compare_int );?,使用字符查找時:?search_list( root, &desired_value, compare_char );?。這就是回調函數簡單的應用,當然回調函數不僅僅只是用于這些簡單的例子,比如庫函數qsort就是利用回調函數實現。
函數原型如下:
void qsort(void *base, //字符串首地址size_t num, //排序總個數size_t width, //排序元素的大小int (__cdecl *compare )(const void *, const void *) //函數指針 );庫函數實現:
void qsort(void *base, //字符串首地址size_t num, //排序總個數size_t width, //排序元素的大小int (__cdecl *compare )(const void *, const void *) //函數指針 ); {char *lo, *hi; /* ends of sub-array currently sorting */char *mid; /* points to middle of subarray */char *loguy, *higuy; /* traveling pointers for partition step */size_t size; /* size of the sub-array */char *lostk[STKSIZ], *histk[STKSIZ];int stkptr; /* stack for saving sub-array to be processed *//* validation section */_VALIDATE_RETURN_VOID(base != NULL || num == 0, EINVAL);_VALIDATE_RETURN_VOID(width > 0, EINVAL);_VALIDATE_RETURN_VOID(comp != NULL, EINVAL);if (num < 2)return; /* nothing to do */stkptr = 0; /* initialize stack */lo = (char *)base;hi = (char *)base + width * (num-1); /* initialize limits *//* this entry point is for pseudo-recursion calling: settinglo and hi and jumping to here is like recursion, but stkptr ispreserved, locals aren't, so we preserve stuff on the stack */ recurse:size = (hi - lo) / width + 1; /* number of el's to sort *//* below a certain size, it is faster to use a O(n^2) sorting method */if (size <= CUTOFF) {__SHORTSORT(lo, hi, width, comp, context);}else {/* First we pick a partitioning element. The efficiency of thealgorithm demands that we find one that is approximately the medianof the values, but also that we select one fast. We choose themedian of the first, middle, and last elements, to avoid badperformance in the face of already sorted data, or data that is madeup of multiple sorted runs appended together. Testing shows that amedian-of-three algorithm provides better performance than simplypicking the middle element for the latter case. */mid = lo + (size / 2) * width; /* find middle element *//* Sort the first, middle, last elements into order */if (__COMPARE(context, lo, mid) > 0) {swap(lo, mid, width);}if (__COMPARE(context, lo, hi) > 0) {swap(lo, hi, width);}if (__COMPARE(context, mid, hi) > 0) {swap(mid, hi, width);}/* We now wish to partition the array into three pieces, one consistingof elements <= partition element, one of elements equal to thepartition element, and one of elements > than it. This is donebelow; comments indicate conditions established at every step. */loguy = lo;higuy = hi;/* Note that higuy decreases and loguy increases on every iteration,so loop must terminate. */for (;;) {/* lo <= loguy < hi, lo < higuy <= hi,A[i] <= A[mid] for lo <= i <= loguy,A[i] > A[mid] for higuy <= i < hi,A[hi] >= A[mid] *//* The doubled loop is to avoid calling comp(mid,mid), since someexisting comparison funcs don't work when passed the samevalue for both pointers. */if (mid > loguy) {do {loguy += width;} while (loguy < mid && __COMPARE(context, loguy, mid) <= 0);}if (mid <= loguy) {do {loguy += width;} while (loguy <= hi && __COMPARE(context, loguy, mid) <= 0);}/* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,either loguy > hi or A[loguy] > A[mid] */do {higuy -= width;} while (higuy > mid && __COMPARE(context, higuy, mid) > 0);/* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,either higuy == lo or A[higuy] <= A[mid] */if (higuy < loguy)break;/* if loguy > hi or higuy == lo, then we would have exited, soA[loguy] > A[mid], A[higuy] <= A[mid],loguy <= hi, higuy > lo */swap(loguy, higuy, width);/* If the partition element was moved, follow it. Only needto check for mid == higuy, since before the swap,A[loguy] > A[mid] implies loguy != mid. */if (mid == higuy)mid = loguy;/* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at topof loop is re-established */}/* A[i] <= A[mid] for lo <= i < loguy,A[i] > A[mid] for higuy < i < hi,A[hi] >= A[mid]higuy < loguyimplying:higuy == loguy-1or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] *//* Find adjacent elements equal to the partition element. Thedoubled loop is to avoid calling comp(mid,mid), since someexisting comparison funcs don't work when passed the same valuefor both pointers. */higuy += width;if (mid < higuy) {do {higuy -= width;} while (higuy > mid && __COMPARE(context, higuy, mid) == 0);}if (mid >= higuy) {do {higuy -= width;} while (higuy > lo && __COMPARE(context, higuy, mid) == 0);}/* OK, now we have the following:higuy < loguylo <= higuy <= hiA[i] <= A[mid] for lo <= i <= higuyA[i] == A[mid] for higuy < i < loguyA[i] > A[mid] for loguy <= i < hiA[hi] >= A[mid] *//* We've finished the partition, now we want to sort the subarrays[lo, higuy] and [loguy, hi].We do the smaller one first to minimize stack usage.We only sort arrays of length 2 or more.*/if ( higuy - lo >= hi - loguy ) {if (lo < higuy) {lostk[stkptr] = lo;histk[stkptr] = higuy;++stkptr;} /* save big recursion for later */if (loguy < hi) {lo = loguy;goto recurse; /* do small recursion */}}else {if (loguy < hi) {lostk[stkptr] = loguy;histk[stkptr] = hi;++stkptr; /* save big recursion for later */}if (lo < higuy) {hi = higuy;goto recurse; /* do small recursion */}}}/* We have sorted the array, except for any pending sorts on the stack.Check if there are any, and do them. */--stkptr;if (stkptr >= 0) {lo = lostk[stkptr];hi = histk[stkptr];goto recurse; /* pop subarray from stack */}elsereturn; /* all subarrays done */ }為了更好地理解回調函數,接下來我們來寫一個自己的qsort函數(利用冒泡排序)
?
int char_compare(void const * c1,void const* c2) //比較函數 {int a = *((int*)c1);int b = *((int*)c2);return a>b ? 1 : a<b ? -1 : 0; }void Swap(char *str1,char *str2,int size) {while (size--){char tmp = *str1;*str1 = *str2;*str2 = tmp;str1++;str2++;} } void MyQsort(void *str,int len,int elen,int(*compare)(void const*,void const*)) //基于回調函數寫的排序算法 {int i = 0;int j = 0;int flag = 1;for (i=0; i<len-1; i++){for (j=0; j<len-1-i; j++){if (compare((char*)str+j*elen,(char*)str+(j+1)*elen)>0){flag = 0;Swap((char*)str+j*elen,(char*)str+(j+1)*elen,elen);}}if (flag)return;} }?
看了例題在來說說原理:
簡而言之,回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用為調用它所指向的函數時,我 們就說這是回調函數?;卣{函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。
回調函數實現的機制是:
(1)定義一個回調函數;
(2)提供函數實現的一方在初始化的時候,將回調函數的函數指針注冊給調用者;
(3)當特定的事件或條件發生的時候,調用者使用函數指針調用回調函數對事件進行處理。
?
看了兩個例子大家應該能理解回調函數了,如果還有什么問題可以私信我,建議把指針這節理解透徹,這是指針的
參考文獻:
Kenneth?A.Reek?著? 徐波 譯.c和指針.人民郵電出版社.2008
總結
以上是生活随笔為你收集整理的回调函数到底是怎么一回事呢的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言中函数可变参数解析
- 下一篇: C语言的函数调用过程(栈帧的创建与销毁)