天天看點

回調函數

如果參數是一個函數指針,調用者可以傳遞一個函數的位址給實作者,讓實作者去調用它,這稱為回調函數(callback function)。例如<code>qsort(3)</code>和<code>bsearch(3)</code>。

<b>表 24.7. 回調函數示例:<code>void func(void (*f)(void *), void *p);</code></b>

<col>

調用者

實作者

提供一個回調函數,再提供一個準備傳給回調函數的參數。

把回調函數傳給參數<code>f</code>,把準備傳給回調函數的參數按<code>void *</code>類型傳給參數<code>p</code>

在适當的時候根據調用者傳來的函數指針<code>f</code>調用回調函數,将調用者傳來的參數<code>p</code>轉交給回調函數,即調用<code>f(p);</code>

以下是一個簡單的例子。實作了一個<code>repeat_three_times</code>函數,可以把調用者傳來的任何回調函數連續執行三次。

<b>例 24.7. 回調函數</b>

回顧一下前面幾節的例子,參數類型都是由實作者規定的。而本例中回調函數的參數按什麼類型解釋由調用者規定,對于實作者來說就是一個<code>void *</code>指針,實作者隻負責将這個指針轉交給回調函數,而不關心它到底指向什麼資料類型。調用者知道自己傳的參數是<code>char *</code>型的,那麼在自己提供的回調函數中就應該知道參數要轉換成<code>char *</code>型來解釋。

回調函數的一個典型應用就是實作類似c++的泛型算法(generics algorithm)。下面實作的<code>max</code>函數可以在任意一組對象中找出最大值,可以是一組<code>int</code>、一組<code>char</code>或者一組結構體,但是實作者并不知道怎樣去比較兩個對象的大小,調用者需要提供一個做比較操作的回調函數。

<b>例 24.8. 泛型算法</b>

<code>max</code>函數之是以能對一組任意類型的對象進行操作,關鍵在于傳給<code>max</code>的是指向對象的指針所構成的數組,而不是對象本身所構成的數組,這樣<code>max</code>不必關心對象到底是什麼類型,隻需轉給比較函數<code>cmp</code>,然後根據比較結果做相應操作即可,<code>cmp</code>是調用者提供的回調函數,調用者當然知道對象是什麼類型以及如何比較。

以上舉例的回調函數是被同步調用的,調用者調用<code>max</code>函數,<code>max</code>函數則調用<code>cmp</code>函數,相當于調用者間接調了自己提供的回調函數。在實際系統中,異步調用也是回調函數的一種典型用法,調用者首先将回調函數傳給實作者,實作者記住這個函數,這稱為注冊一個回調函數,然後當某個事件發生時實作者再調用先前注冊的函數,比如<code>sigaction(2)</code>注冊一個信号處理函數,當信号産生時由系統調用該函數進行處理,再比如<code>pthread_create(3)</code>注冊一個線程函數,當發生排程時系統切換到新注冊的線程函數中運作,在gui程式設計中異步回調函數更是有普遍的應用,例如為某個按鈕注冊一個回調函數,當使用者點選按鈕時調用它。

以下是一個代碼架構。

既然參數可以是函數指針,傳回值同樣也可以是函數指針,是以可以有<code>func()();</code>這樣的調用。傳回函數的函數在c語言中很少見,在一些函數式程式設計語言(例如lisp)中則很常見,基本思想是把函數也當作一種資料來操作,輸入、輸出和參與運算,操作函數的函數稱為高階函數(high-order function)。

refer to : http://learn.akae.cn/media/ch24s05.html

繼續閱讀