摘錄自 《C和指標》
1.回呼函數
這裡有一個簡單的函數,它用於在一個單鏈表中尋找一個值,它的參數是一個指向鏈表第一個節點的指標以及那個需要尋找的值.
Node* search_list(Node* node,int const value)
{
while(node!=NULL)
{
if(node->value==value) break;
node=node->link;
}
return node;
}
這個函數看上去相當簡單,但它只適用於值為整數的鏈表,如果你需要在一個字串鏈表中尋找,你不得不另外編寫一個函數,這個函數和上面那個函數的絕大部分代碼相同,只是第二個參數的類型以及節點值的比較方法不同.一種更為通用的方法是尋找函數與類型無關,這樣它就能用於任何類型的值的鏈表,我們必須對函數的兩個方面進行修改,使它與類型無關.
首先我們必須改變比較的執行方式,這樣函數就可以對任何類型的值進行比較.這個目標聽上去好象不可能,如果你編寫語句用於比較整型值,它怎麼還可能用於其他類型如字串的比較呢?解決方案就是使用函數指標,調用者編寫一個函數,用於比較兩個值,然後把一個指向這個函數的指標作為參數傳遞給尋找函數.然後尋找函數調用這個函數來執行值的比較,使用這種方法,任何類型的值都可以進行比較.我們必須修改的第二個方面是向函數傳遞一個指向值的指標而不是本身.函數由一個void *形參,用於接收這個參數,然後指向這個值的指標便傳遞給比較函數,這個修改使字串和數組對象也可以被使用,字串和數組無法作為參數傳遞給函數,但指向它們的指標可以.
使用這種技巧的函數叫"回呼函數"(callback function);因為使用者把一個函數指標作為參數傳遞給其他函數,後者將"回調"使用者的函數.任何時候,如果你所編寫的函數必須能夠在不同的時刻執行不同類型的工作或執行只能由函數調用者定義的工作,你都可以使用這個技巧.許多視窗系統使用回呼函數串連多個動作,如拖拽滑鼠和點擊按鈕來指定使用者程式中的某個特定函數.我們無法在這個上下文環境中為回呼函數編寫一個準確的原型,因為我們並不知道進行比較的值的類型.事實上,我們需要尋找函數能作用於任何類型的值,解決這個難題的方法是把參數型別宣告為"void
*",表示"一個指向未知類型的指標".
/***在一個單鏈表中尋找一個指定值的函數,它的參數是一個指向鏈表第一個節點
**的指標,一個指向我們需要尋找的值的指標和一個函數指標,它所指向的函數
**用於比較儲存於此鏈表中的類型的值.
*/
#include "node.h"
Node* search_list(Node *node,void const *value, int(*compare)(void const*,void const*)) //函式宣告;
{
while (node!=NULL)
{
if(compare(&node->value,value)==0) break;
node=node->link;
}
return node;
}
同時注意雖然函數不會修改參數node所指向的任何節點,但node並未聲明為const。如果node被聲明為const,函數不得不返回一個const結果,這將限制調用程式,它便無法修改尋找函數所找到的節點。
在一個特定的鏈表中進行尋找時,使用者需要編寫一個適當的比較函數,並把指向該函數的指標和指向需要尋找的值的指標傳遞給尋找函數。
例如,下面是一個比較函數,它用於在一個整數鏈表中進行尋找。
int compare_ints(void const* a,void const* b)
{
if(*(int*)a==*(int*)b) return 0;
else return 1;
}
這個函數將像下面這樣使用:
desired_node=search_list(root,&desired_value,compare_ints);
2.轉換表(jump table)
跳躍表最好用個例子來解釋。下面的程式碼片段取自一個程式,它用於實現一個袖珍式計算機。程式的其他部分已經讀入兩個數(op1和op2)和一個操作符(oper)。下面的代碼對操作符進行測試,最後決定調用哪個函數。
switch(oper)
{
case ADD: result=add(op1,op2);break;
case SUB: result=sub(op1,op2);break;
case MUL: result=mul(op1,op2);break;
case DIV: result=div(op1,op2);break;
......
}
對於一個新奇的具有上百個操作符的計算機,這條switch語句將會非常之長。為什麼要調用函數來執行這些操作呢?把具體操作和選擇操作的代碼分開是一種良好的設計方案。更為複雜的操作將肯定以獨立的函數來實現,因為它們的長度可能很長。但即使是簡單的操作也可能具有副作用,例如儲存一個常量值用於以後的操作。
為了使用switch語句,表示操作符的代碼必須是整數。如果它們是從零開始連續的整數,我們可以使用轉換表來實現相同的任務。轉換表就是一個函數指標數組。
建立一個轉換表需要兩個步驟。首先,聲明並初始化一個函數指標數組。唯一需要留心之處就是確保這些函數的原型出現在這個數組的聲明之前。
double add(double,double);
double sub(double,double);
double mul(double,double);
double div(double,double);
double (*oper_func[])(double,double)={add,sub,mul,div,...};
初始化列表中各個函數名的正確順序取決於程式中用於表示每個操作符的整型代碼。這個例子假定ADD是0,SUB是1,MUL是2,接下去以此類推。
第二個步驟是用下面這條語句替換前面整條switch語句!
result=oper_func[oper](op1,op2);
oper從數組中選擇正確的函數指標,而函數叫用作業符將執行這個函數。