今天看到回調函數,有點迷糊,找了好多搜索引擎的資料,都不是讓我很能理解,看了《c和指針》我才明白了。node
簡單描述一下什麼是回調函數:算法
用戶把一個函數指針做爲參數傳遞給其餘函數,後者將「回調」用戶的函數。若是函數能夠再不一樣的時間執行不一樣類型的工做或者執行只能由函數調用者定義的工做,均可以使用回調函數。 回調函數沒法知道比較的值的類型,因此參數的類型被聲明爲void*。表示一個指向未知類型的指針。 能夠經過函數指針來實現回調函數。一個指向回調函數的指針做爲參數傳遞給另外一個函數,後者使用這個指針調用回調函數。 app
可能說了太多定義也不會非常明白,來幾個事例說說。ide
當咱們在在鏈表中查找一個數時,咱們通常會這樣寫:函數
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類型,當變爲其餘類型時咱們就沒法用這個函數,可是從新寫一個函數,他們重複代碼又太多。那咱們看看用回調函數如何辦到。 oop
回調函數查找:this
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就是利用回調函數實現。搜索引擎
函數原型以下:spa
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: setting lo and hi and jumping to here is like recursion, but stkptr is preserved, 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 the algorithm demands that we find one that is approximately the median of the values, but also that we select one fast. We choose the median of the first, middle, and last elements, to avoid bad performance in the face of already sorted data, or data that is made up of multiple sorted runs appended together. Testing shows that a median-of-three algorithm provides better performance than simply picking 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 consisting of elements <= partition element, one of elements equal to the partition element, and one of elements > than it. This is done below; 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 some existing comparison funcs don't work when passed the same value 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, so A[loguy] > A[mid], A[higuy] <= A[mid], loguy <= hi, higuy > lo */ swap(loguy, higuy, width); /* If the partition element was moved, follow it. Only need to 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 top of 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 < loguy implying: higuy == loguy-1 or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */ /* Find adjacent elements equal to the partition element. The doubled loop is to avoid calling comp(mid,mid), since some existing comparison funcs don't work when passed the same value for 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 < loguy lo <= higuy <= hi A[i] <= A[mid] for lo <= i <= higuy A[i] == A[mid] for higuy < i < loguy A[i] > A[mid] for loguy <= i < hi A[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 */ } else return; /* 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