函数指针是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/