簡體   English   中英

指向函數的指針

[英]Pointers to Functions in C

我覺得我對C中的指針有很好的處理。大多數情況下,我使用它們將數組傳遞給函數。

但是我注意到在查看許多不同的代碼示例時,看到指向函數的指針並不罕見。 我一點也不清楚為什么這會有用呢? 是否存在一些經典實例,其中指向函數模型的指針是否易於實現或必不可少?

典型的例子是使用qsort函數對元素數組進行排序。 它的最后一個參數是一個函數指針,它指向比較兩個元素的例程,作為void指針傳遞。

void qsort(void *base,
           size_t nmemb,
           size_t size,
           int(*compar)(const void *, const void *));

在這里使用函數指針是必不可少的,因為排序例程不能足夠通用以了解你想要如何比較元素,所以函數指針本質上是一個“回調”,sort函數用它來擴展它。

[編輯]例如,假設您定義了以下結構:

struct president {
    char * fname;
    char * lname;
    unsigned int number;
};

如果你想通過它們的“數字”字段對它們的數組進行排序,你可以實現一個“比較”(比較)函數,如下所示:

int compare_presidents(const void * v1, const void * v2) {
    struct president p1 = * ((struct president *) v1);
    struct president p2 = * ((struct president *) v2);
    return (p1.number - p2.number); // Compare by number ascending.
}

你可以對這樣的結構進行排序:

qsort(presidents, num_presidents, sizeof(struct president), compare_presidents);

把功能指針想象為實現(好,描述)我們現在優雅地稱為回調的“石刀和熊皮”方式。 這個比喻並不完美,但這就是想法 - 通過指針引用提供一種快速的,相當危險的方式來調用一個完全不同的函數。

一個明顯的例子是qsort 你傳遞一個指向它將用於比較項目的函數的指針。 因為它不知道你正在排序什么類型的數據,所以它本身並不能真正做到這一點。

另一個常見的例子是進行數值積分的函數。 您將指向要集成的函數的指針以及下限和上限傳遞給它,並調用函數來查找這些邊界之間的各個點的值以獲得答案。

示例1:想象有5種不同的排序算法。 你只想在各種各樣的地方進行排序,但你不想在你所做的每個地方中選擇5個。 相反,你有一個指向排序算法的指針(它們都采用相同的參數,類型等)。 只需選擇一次算法(設置指針)並使用指針進行排序。

示例2:回調...函數在循環中執行某些操作但每次迭代都需要應用程序反饋。 對於該函數,回調是一個作為參數傳遞的函數指針。

一個非常常見的用途是用於事件處理程序的回調函數。 例如,想一下按鈕按下處理程序。 你的button_update()可能看起來像:

void button_update(struct button* button, void* data) {
    if (button_pressed(button)) {
        button.callback(data);
    }
}

在這種情況下,button.callback將是一個函數指針。

啟動線程是另一個例子。 與POSIX類似,C11具有啟動線程的接口:

typedef int (*thrd_start_t)(void*);
int thrd_create(thrd_t *, thrd_start_t, void *);

這里thrd_start_t函數指針指向將由新線程執行的函數,而void*參數是傳遞給該函數的參數。

函數指針的另一個常見用例是插件。 插件可以實現為可動態加載的庫。 當應用程序加載一個插件時,它可以調用一個預定義的函數,傳入一個帶有應用程序函數指針的結構,然后返回一個帶有插件函數指針的結構。 現在插件和應用程序可以調用彼此的功能。

更多:函數指針也有函數簽名給出的類型。 您也可以[仔細]轉換為特定的函數指針類型:

在qsort示例中,預期的比較器在簽名中具有const void *:

int (*comparator)(const void *, const void *)

但是,如果您具有特定 (非void *)ptr類型的比較函數,則可以將其簡單地轉換為qsort預期簽名:

(int (*)(const void *, const void *))cmpr_specific

類似地,typedef可用於定義函數指針的簽名,從而節省計算parens的痛苦:

typedef int (*MYFUNC_T)(int arg); MYFUNC_T callptr = myfunc; callptr(10);

,其中myfunc是具體的int myfunc(int arg)

現在想想它定義為什么: int (*fptr_arr[10])(int (*)(int));

好吧,typedef使它變得清晰: typedef int (*FUNC_T)(int arg); typedef int (*FUNC2_T)(FUNC_T func); typedef int (*FUNC_T)(int arg); typedef int (*FUNC2_T)(FUNC_T func); - 一個帶有函數指針參數的函數。

FUNC2_T fptr_arr[10]; fptr_arr[0]=myfunc;

另一個例子是一些重型系統編程或實時嵌入式編程。 處理器中斷向量表可以實現為函數指針數組。 它們以匯編程序的方式實現,如果您有(非C標准)方式指定函數表的內存位置,它也可以在C中完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM