函數指針是C語言中功能最強大的工具之一,可是在學習的初始階段會有些痛苦。這篇文章介紹了函數指針的基礎知識,以及如何使用它們在C中實現函數回調。C++爲回調採起了一條略有不一樣的途徑,這是另外一條路。編程
指針是一種特殊的變量,用於保存另外一個變量的地址。相同的概念適用於函數指針,只是它們指向函數而不是指向變量。例如,若是聲明一個數組,int a[10];則該數組名稱a將在大多數上下文中(在表達式中或做爲函數參數傳遞)「退化」到指向其第一個元素的不可修改的指針(即便指針和數組在聲明/定義它們,或用做運算sizeof符的操做數)。以相同的方式,對於int func();,func退化爲指向函數的不可修改的指針。您能夠暫時將其func視爲const指針。數組
可是咱們能夠聲明一個指向函數的非恆定指針嗎?是的,咱們能夠,就像咱們聲明一個指向變量的非恆定指針同樣:異步
int (*ptrFunc) ();
此處ptrFunc是指向不帶參數且返回整數的函數的指針。不要忘記加括號,不然編譯器將假定它ptrFunc是一個普通的函數名,它不作任何事情並返回一個指向整數的指針。ide
來看看如下的程序:函數
#include <stdio.h> /* function prototype */ int func(int, int); int main(void) { int result; /* calling a function named func */ result = func(10, 20); printf("result = %d\n", result); return 0; } /* func definition goes here */ int func(int x, int y) { return x + y; }
不出所料,當咱們使用進行編譯gcc -g -o example1 example1.c和調用時./example1,輸出以下:工具
result = 30學習
上面的程序調用func()了簡單的方法。讓咱們修改程序以使用指向函數的指針進行調用。這是更改的main()功能:ui
#include <stdio.h> int func(int, int); int main(void) { int result1,result2; /* declaring a pointer to a function which takes two int arguments and returns an integer as result */ int (*ptrFunc)(int, int); /* assigning ptrFunc to func's address */ ptrFunc=func; /* calling func() through explicit dereference */ result1 = (*ptrFunc)(10, 20); /* calling func() through implicit dereference */ result2 = ptrFunc(10, 20); printf("result1 = %d result2 = %d\n", result1, result2); return 0; } int func(int x, int y) { return x + y; }
輸出結果:result1 = 30 result2 = 30spa
在這一階段,咱們有足夠的知識來處理函數回調。根據Wikipedia的說法,「在計算機編程中,回調是對可執行代碼或一段可執行代碼的引用,該代碼做爲參數傳遞給其餘代碼。這使較低層的軟件層能夠調用較高層中定義的子例程(或函數)。」prototype
讓咱們來看一個簡單的程序來解釋這個定義,完整的方案有三個文件:callback.c,reg_callback.h和reg_callback.c
/* callback.c */ #include <stdio.h> #include "reg_callback.h" /* callback function definition goes here */ void my_callback(void) { printf("inside my_callback\n"); } int main(void) { /* initialize function pointer to my_callback */ callback ptr_my_callback=my_callback; printf("This is a program demonstrating function callback\n"); /* register our callback function */ register_callback(ptr_my_callback); printf("back inside main program\n"); return 0; } /* reg_callback.h */ typedef void (*callback)(void); void register_callback(callback ptr_reg_callback); /* reg_callback.c */ #include <stdio.h> #include "reg_callback.h" /* registration goes here */ void register_callback(callback ptr_reg_callback) { printf("inside register_callback\n"); /* calling our callback function my_callback */ (*ptr_reg_callback)(); }
使用gcc -Wall -o callback callback.c reg_callback.c和編譯,連接和運行程序./callback:
This is a program demonstrating function callback inside register_callback inside my_callback back inside main program
該代碼須要一些解釋。假設在程序的另外一部分發生事件以後,咱們必須調用一個回調函數來執行一些有用的工做(錯誤處理,退出前的最後一刻清理等)。第一步是註冊回調函數,這只是將函數指針做爲參數傳遞給須要調用該回調函數的其餘函數(例如register_callback)。
咱們能夠將上面的代碼寫在一個文件中,可是將回調函數的定義放在一個單獨的文件中以模擬實際狀況,其中回調函數位於頂層,而調用它的函數位於不一樣的文件層。所以,程序流程如圖1所示。
較高層的函數將較低層的函數做爲普通調用來調用,而且回調機制容許較低層的函數經過指向回調函數的指針來調用較高層的函數,這正是Wikipedia定義所陳述的。
使用回調函數
在這裏能夠看到回調機制的一種用法:
/ * This code catches the alarm signal generated from the kernel Asynchronously */ #include <stdio.h> #include <signal.h> #include <unistd.h> struct sigaction act; /* signal handler definition goes here */ void sig_handler(int signo, siginfo_t *si, void *ucontext) { printf("Got alarm signal %d\n",signo); /* do the required stuff here */ } int main(void) { act.sa_sigaction = sig_handler; act.sa_flags = SA_SIGINFO; /* register signal handler */ sigaction(SIGALRM, &act, NULL); /* set the alarm for 10 sec */ alarm(10); /* wait for any signal from kernel */ pause(); /* after signal handler execution */ printf("back to main\n"); return 0; }
信號是從內核生成的中斷類型,對於處理異步事件很是有用。信號處理功能已在內核中註冊,而且在將信號傳遞到用戶進程時能夠從程序的其他部分異步調用,圖2表示此流程。
回調函數還能夠用於建立將從上層程序調用的庫,而後該庫將在發生某些事件時調用用戶定義的代碼。 如下源代碼(insertion_main.c,insert_sort.c和insert_sort.h)顯示了用於實現普通插入排序庫的機制。 能夠靈活地使用戶能夠調用他們想要的任何比較功能。
/* insertion_sort.h */ typedef int (*callback)(int, int); void insertion_sort(int *array, int n, callback comparison); /* insertion_main.c */ #include <stdio.h> #include <stdlib.h> #include "insertion_sort.h" int ascending(int a, int b) { return a > b; } int descending(int a, int b) { return a < b; } int even_first(int a, int b) { /* code goes here */ } int odd_first(int a, int b) { /* code goes here */ } int main(void) { int i; int choice; int array[10] = {22,66,55,11,99,33,44,77,88,0}; printf("ascending 1: descending 2: even_first 3: odd_first 4: quit 5\n"); printf("enter your choice = "); scanf("%d", &choice); switch(choice) { case 1: insertion_sort(array,10, ascending); break; case 2: insertion_sort(array,10, descending); case 3: insertion_sort(array,10, even_first); break; case 4: insertion_sort(array,10, odd_first); break; case 5: exit(0); default: printf("no such option\n"); } printf("after insertion_sort\n"); for(i = 0; i < 10; i++) printf("%d\t", array[i]); printf("\n"); return 0; } /* insertion_sort.c */ #include"insertion_sort.h" void insertion_sort(int *array, int n, callback comparison) { int i, j, key; for(j=1; j<=n-1;j++) { key=array[j]; i=j-1; while(i >=0 && comparison(array[i], key)) { array[i+1]=array[i]; i=i-1; } array[i+1]=key; } }
參考:
https://opensourceforu.com/2012/02/function-pointers-and-callbacks-in-c-an-odyssey/
https://www.sanfoundry.com/c-tutorials-callback-functions/