匿名使用者
匿名使用者 發問時間: 電腦與網際網路程式設計 · 1 0 年前

對於call back function存在一些疑問

小弟最近在看一些程式時,

對於call back function有一些疑問,

不知網上的先進們能否幫忙解惑...

1. call back function和一般的call function在應用時究竟有何差別?

2. 在什麼情況下,必須(抑或者通常)會需要用到call back function?為什麼?

3. 如果因為我自己不熟悉call back function的做法,因而選擇其它的方式實作時,同樣的動作,有何方式以其它的寫法實踐?

已更新項目:

感謝 mark 和 1234321 的解答

就如同 mark 所說的, 小弟所認知的 call back function 的確來自於 interrupt service routine (ISR).

二位的回答解決了我不少的疑問,

另外, 針對上述兩位的回應, 在這裡仍有一些疑問想請教網上的先進們.

2 個已更新項目:

1. 如 mark 所述, call function 必須有一個主體 (來源) 程式 (如 main function) 呼叫方可動作, 而 call back function 則長駐在執行程式中 "等待" 被呼叫 (如ISR). 所以, 在應用中, 除了像 interrupt 由執行程式外的硬體觸發來動作外, 一般的 main function是不需要使用到 call back function這種程式應用. 這樣的說法, 正確嗎?

3 個已更新項目:

2. 像 ARM cpu, 在 Exception Vector中, 所謂的 Software Interrupt (ex: http://pdf1.alldatasheet.com/datasheet-pdf/view/37... 指的是程式執行錯誤時所觸發的 interrupt, 並不是程式執行的過程中發引發的動作? 那如果我想以程式執行的過程來模擬一個 interrupt source, 可以嗎?

感激不盡

3 個解答

評分
  • 1 0 年前
    最佳解答

    以下是淡藍色在 Google 上查詢到的定義:

    Callback Function: A function that is passed (by reference) to another function. The other function calls the callback function under defined conditions (for example, upon completion).

    (請見 http://www.ssuet.edu.pk/taimoor/books/0%2D7897%2D1...

    這裡有兩個重點,一個是函數本體(Function),另一個是傳遞(Passed by Reference)。叫用函數(Calling Function)與回應函數(Callback Function)其實是一體兩面的事。當 A 去呼叫 B 時,從 A 的觀點, B 被視為叫用函數,而 B 自然而然為 A 的回應函數。

    (Callback Function 依慣例翻譯為「回呼函數」,但是淡藍色認為「回應函數」較為洽當。)

    一、函數本體:

      從計算機實際運作的角度來看,函數本體不會像咱們在撰寫中高階程式時,有那麼明確界定,對處理器而言,它所在乎的是當前的機械碼該從何處提取,並且加以解碼與執行。函數本體(進入點),對處理器而言,其實只是一個簡單的記憶體位址的表示而已。

    二、傳遞:

      假設有一種機制,可以將一群函數本體(進入點)當作某一個函數的傳遞參數,那麼便可於這個函數的執行時期決定這群函數的叫用時機,而不用非得在編譯時期前決定。很幸運的,這個假設是可以被實現的,就第一、函數本體結論,「函數的表示,其實就只是單純的記憶體位址表示」,所以把函數本體當成一個數值當作參數傳遞是可行的。

    淡藍色在此以 C 語言舉一個排序例子,來解釋 Callback Function 實際的作法。

    假設您以限定方式製作了一個 Library 提供排序數字的函數:

    #define ASCEND 1 /*遞增方式*/

    #define DESCEND 2 /*遞減方式*/

    void sort(int* array, int size, int order) /*以簡單的泡沫排序法當例子*/

    {

    int i,j,t,f;

    for(i=0;i<size-1;i++)

    {

    for(j=i+1;j<size;j++)

    {

    f=0; /*f 用來決定是否要交換數字 */

    switch(order)

    {

    case ASCEND:{if (array[j]<array[i]) f=1;}break;

    case DESCEND:{if (array[i]<array[j]) f=1;} break;

    }

    if(f)

    {

    t=array[i]; array[i]=array[j]; array[j]=t;

    }

    }

    }

    }

    以下是主程式,並且使用上述的Library:

    #include <stdio.h>

    #include "mylib.h" /*mylib.h 提供上述 sort() 函數所需要的宣告與定義*/

    int main(int argc, char* argv[])

    {

    int i,a[8]={1,5,4,7,2,6,8,3};

    sort(a,8,ASCEND); for(i=0;i<8;i++) printf("%d ",a[i]); printf("\n"); /*以遞增方式排序*/

    sort(a,8,DESCEND); for(i=0;i<8;i++) printf("%d ",a[i]); printf("\n"); /*以遞減方式排序*/

    return 0;

    }

    這個 sort 比對的方式只有遞增與遞減兩種方式,無從變更;但是如果需要以數字所含 bits 為 1 的數量做排序,雖然 sort 的演算法依舊可以應付,但是排序依據方式已經被寫死,故因此變得很沒有彈性。很幸運地,在 C 當中可以實作 Callback Function,一切就改觀了,將 sort 以如下方式實作:

    2007-05-23 12:18:45 補充:

    typedef int (*Compare) (int,int); /*Compare 是一個指標型態,所宣告的變數為一個指向函數的指標,這個函數接受兩個 int 型態的參數,並回傳 int 型態的值*/

    2007-05-23 12:19:39 補充:

    int order_by_ascend(int front, int rear) /*遞增排序依據*/

    {

    if(front>rear) return 1; /*前面的數字大於後面的數字就必須交換*/

    return 0;

    }

    int order_by_descend(int front, int rear) /*遞減排序依據*/

    {

    if(front<rear) return 1; /*前面的數字小於後面的數字就必須交換*/

    return 0;

    }

    2007-05-23 12:20:10 補充:

    void sort(int* array, int size, Compare func) /*使用相同的泡沫排序演算法*/

    {

    int i,j,t,f;

    for(i=0;i<size-1;i++)

    {

    for(j=i+1;j<size;j++)

    {

    if(func(array[i],array[j])){t=array[i]; array[i]=array[j]; array[j]=t;} /*使用一個 Callback function 來當作排序依據*/

    }

    }

    }

    2007-05-23 12:20:39 補充:

    接下來是主程式,並且使用上述的Library:

    #include <stdio.h>

    #include "mylib.h"

    /*自訂排序依據*/

    int bits_count(int n) /*檢查 n 所含 bits 為 1 的數量*/

    {

    int i,c=0;

    for(i=0;i<32;i++){if(n&(1<<i)) c++;}

    return c;

    }

    int order_by_bits(int front, int rear)

    {

    if(bits_count(front)>bits_count(rear)) return 1; /*由小到大排序 bits 為 1 的數量*/

    return 0;

    }

    2007-05-23 12:23:38 補充:

    int main(int argc, char* argv[])

    {

    int i,a[8]={1,5,4,7,2,6,8,3};

    sort(a,8,order_by_ascend); /*遞增方式*/

    for(i=0;i<8;i++) printf("%d ",a[i]); printf("\n");

    sort(a,8,order_by_descend); /*遞減方式*/

    for(i=0;i<8;i++) printf("%d ",a[i]); printf("\n");

    2007-05-23 12:24:01 補充:

    sort(a,8,order_by_bits); /*自訂方式*/

    for(i=0;i<8;i++) printf("%d ",a[i]); printf("\n");

    return 0;

    }

    2007-05-23 12:24:18 補充:

    /*sort() 函數已具備較大的彈性,因為使用 Callback Function 而不再限定排序依據方式,不僅可以使用 mylib 所提供的排序依據,也可以自己製作想要的排序依據*/

    2007-05-23 12:24:47 補充:

    由上述的小例子可以稍微窺探,當您需要延長軟體生命週期,亦或者是對軟體規劃具更高的彈性,在執行時期決定程式流程有時候是很必要的。說實在的,如果您的程式是依附在作業系統下執行的程序,即便是 main() 函數,也被作業系統視為 Callback Function。您可能會質疑淡藍色的說法,依據 C 語言及其工具的規範,main() 函數的標準宣告方式為 int main(int argc, char* argv[]); 怎麼 int main(void); 方式宣告的程式,仍舊被作業系統所接受?別忘了轉型的威力:

    2007-05-23 12:25:10 補充:

    typedef int (*Process) (int,char**);

    雖然您的 main() 函數宣告為 int main(void); 但是 main 的參考位址是不變的,作業系統在執行您的程式前,仍舊可以將 main() 函數的參考位置轉型成上述 Process 型態,而不會變更 main() 的參考位置。所以,事實上您無時無刻在製作 Callback Function,因為至少您實作了 main() 函數,當作作業系統的 Callback Function。

    2007-05-23 12:25:31 補充:

    只要您製作了「函數本體」,並且透過語言提供的任何方式可以將函數的參考位址當作參數「傳遞」,並且在執行時期才決定函數本體的執行時機,那就是使用了 Callback Function 技術。

    當代有許多在設計時期方便開發設計與執行時期抽換程式碼的技術都是靠 Callback Function 才能實現,例如:事件導向程序(Event Driven)、DLL(Dynamic Link Library)、SO(Shared Object)、COM/COM+、…等。

    2007-05-23 12:25:47 補充:

    至於您提到不熟悉 Callback Function 作法,淡藍色建議您多多嘗試指標指向 Function 的方式來實作您的程式,Callback Function 是一種獨特的手段,將程式分成主從兩種形式(Master-Slave),當做 Slave 的 Callback Function 的設計心態必須是被動的,並且無須探究 Master 的運作方式,只需要配合 Master 所規範的合作方式,等待 Master 呼叫,專注於對 Master 的回應即可。

    2007-05-23 12:39:38 補充:

    (貼完長篇論述,才發現您的補充問題)

    1. Callback Function 非限定與硬體設備(Interrupt 配合),只要您有設計函數本體,並且有傳遞函數參考值,並且以函數參考值叫用(非直接具名叫用)都可以視為使用 Callback Function 技術。淡藍色必須強調,「函數本體」與「傳遞」這兩個概念。語言若有提供函數參考值,那麼就能實現 Callback Function 技術。

    2007-05-23 12:48:04 補充:

    2. 依上述論述,Callback Function 技術與 Hardware/Software Interrupt 無關,Interrupt 技術僅在協調週邊軟硬體設備,並盡可能避免處理器無謂的忙碌。當代的事件驅動導向的程式方法(Event Driven)使用 Interrupt 技術,配合 Callback Function,讓軟韌體開發較有彈性,對於處理器的運作效率也會有程度上的提升。

    2007-05-23 17:29:29 補充:

    勘誤更正(撰文時,淡藍色老想著 DLL 動態載入程式庫的機制,敘述有所謬誤,敬請見諒。)

    更正文意:「Callback Function 於執行時期才決定叫用時機。」,這種陳述並非絕對的。為了在軟體架構的規劃上有較高的彈性,在研發設計階段就已經可以突顯 Callback Function 的好處了。

    參考資料: Callback Function 定義來自 http://www.ssuet.edu.pk/taimoor/books/0%2D7897%2D1... 無, 無, 無, 無, 無, 無, 無, 無, 無, 無, 無, 無, 無, 無
  • SiYu
    Lv 5
    1 0 年前

    個人覺得兩位的討論把call back functin太狹隘化了.

    call back function. 在個人的看法是. 某設計師A寫了一個function(or 某種功能)但是他寫的funciton 可能有一部份他沒辦法全部完成. 因此他要求使用他的function 的設計師B 提供一個call back function 讓他呼叫. 以便完成所有的工作.

    2007-05-23 11:30:01 補充:

    舉例說

    C 的qsort() 需要妳提供一個function 來做比較大小

    Direct X 中有個api DirectDrawEnumerate() 也需要一個call back function

    但跟任何事件發生一點也沒關係. 只是透過此call back function 告訴呼叫者. 系統中有那些顯示設備. 並不會把call back function 保存在windows 系統中. DirectDrawEnumerate() 直行結束. 給他用的call back function 不會在被呼叫.

  • Mark
    Lv 5
    1 0 年前

    1. 一般的dos program可能就從main開始一直執行下去

    中間也可能去呼叫一些其它的函式, 就是你講的call function

    callback function顧名思義就是等著被呼叫的函式

    並不是由你的main()下去呼叫的

    而是你寫好一個函式

    然後可能是在你的主程式去做註冊這個函式的動作

    當特定的事件發生時, 你註冊的callback function就會被呼叫

    2. callback function在windows program常會看到

    Interrupt service routine也算是callback function

    通常是在某些特定事件發生後才會執行的動作

    比方說, 你寫一個程式是要等使用者按滑鼠右鍵才會嗶一聲

    如果不用callback的話, 你要一直去polling有沒有滑鼠右鍵按下

    這對CPU效能就會有所影響, 而且還會讓別的程式都不能做事

    這時我們就可以去註冊一個callback for滑鼠按鍵

    當滑鼠右鍵被按下時, 你註冊的callback才會被呼叫到

    這樣你就不用polling的方式, 也可以把CPU讓出來做其它事

    3. 那應該就是用polling的方式, 用一個 for 或 while 迴圈

    不斷地去測試事件是否發生

    直到事件發生再去呼叫你的function

    2007-05-24 22:58:57 補充:

    1. 應該這麼說, callback function只是一個名稱定義

    它和一般的function都是一樣的

    只是callback function通常是給特定的系統元件

    為了某些特定目的呼叫的

    系統元件會把它所需要呼叫的函式prototype定好

    你就是按照那個function prototype寫好function

    然後把function pointer提供給它

    2007-05-24 22:59:05 補充:

    當系統元件需要呼叫函式時就會透過你提供的function pointer呼叫

    所以你實在不需要去執著探討main會不會使用callback function

    正常來說, 你寫的callback function是要給別人為了特定目的呼叫的

    你的main應該不會去呼叫自己寫給別人的callback function

    但是我說過, callback function其實也都是一般的function

    所以你的main真的要去呼叫也行啦

    只是很少看到這樣子在用的

    2007-05-24 23:02:46 補充:

    2. software interrupt就是在你的程式裏呼叫某個特定的API

    它會讓CPU產生exception, 然後跳到某段exception handler

    exception handler也可以說是一段callback function

    在系統initialize階段會把exception handler的funtion pointer設好

    當有software interrupt產生時, exception handler就會被執行到

    2007-05-24 23:13:03 補充:

    這通常是用在程式發生異常狀況的處理方法

    或是在某個時間點你想要看某些暫存器或堆疊內容等

    比如說, 你可以在你的程式碼裏加上這樣的code

    if (異常狀況or特定狀況發生)

    {

    generate_soft_int(); // 觸發software interrupt, 這邊應該是OS會寫好

    }

    generate_soft_int()

    {

    暫存register內容以及相關動作

    呼叫exception_handler()

    }

    exception_handler()

    {

    看你需求做相關動作

    比如說print all register contents for debugging

    }

還有問題?馬上發問,尋求解答。