函数指针与回调函数详解

函数指针回调函数详解

1.什么是函数指针?

函数(的)指针就是指针。这个指针存放一个函数的地址,而函数的名称就该函数的入口,即地址。这类似于数组名就是数组的首地址。我们可以通过反汇编直观的查看到函数名和函数地址的关系。

080483c4 <rfun>:
 80483c4:       55                      push   %ebp
 80483c5:       89 e5                   mov    %esp,%ebp
 80483c7:       83 ec 28                sub    $0x28,%esp
 ······
 80483f3:       e8 cc ff ff ff          call   80483c4 <rfun>
 ······

第一行080483c4就是函数在内存中的地址,后面的 rfun 就是函数名,还可以看出第二行和第一行的地址相同,所以可以知道函数名就是该函数的入口地址。 
第6行的call 80483c4 rfun就是再次调用这个函数,回到这个函数的入口出执行,可以看出该函数是一个递归函数。

2.函数指针的使用

2.1调用函数

我们就用函数指针调用刚才的rfun函数。

int rfun(unsigned x);//函数的声明

通过函数的声明,可以得到rfun函数的返回值类型,参数类型和参数个数。这些都是定义指针函数的必要条件!既然函数名就是函数地址,我们就可以用一个指针指向它,函数指针如下:

int (*pfun)(unsigned);//函数指针的定义
pfun = rfun;//函数指针的初始化

pfun就是指针变量,可以直接用函数名rfun赋值给pfun这个指针。函数指针在调用时和函数调用一致,只是用指针代替了函数名。

pfun(x);//函数指针的调用

函数指针不能自增和自减操作,否则程序会崩溃。

2.2作为函数的参数

函数指针作为A函数的参数,A函数称为回调函数。A函数的定义为:

int rfun_call(int (*pfun)(unsigned), unsigned x)
{
    return pfun(x);
}

这个函数的第一个参数是一个返回值为int类型,参数为一个且是unsigned类型函数的指针,第二个参数是 unsigned类型。

3.什么是回调函数?

回调函数(Callback Functions)就是一个通过函数指针调用的函数。

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。刚才的rfun_call就是一个回调函数。这个函数是自己写的,下面就用系统感受一下的回调函数。

先说明两个Linux系统函数,一个是alarm函数,一个是signal函数

①alarm函数,也称为闹钟函数,它可以在进程中设置一个定时器,当定时器指定的时间到时,它向进程发送SIGALRM信号。

#include <unistd.h>//alarm的头文件
unsigned int alarm(unsigned int seconds);//alarm的函数原型

②signal函数,执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。

#include <signal.h>//signal函数的头文件

typedef void (*sighandler_t)(int);//sighandler_t是一个指向返回值为void,参数为int的函数指针
sighandler_t signal(int signum, sighandler_t handler);//signal函数的原型

描述一个场景: 
你要睡觉,只睡2秒,所以你定了一个2秒的闹钟,到第3秒时,闹钟给你发信号,说时间到了

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void fun(int signum)
{
     printf("时间到了,起床!\n");
}

int main()
{
    int i;

    alarm(3);//定2秒闹钟,第3秒会响铃,也就是发信号
    signal(SIGALRM, fun);//接收闹铃的信号,去执行fun()
    for(i = 0; i < 3; i++){
            sleep(1);//描述时间的流逝
            printf("%d秒过去了\n", i+1);
    }
    printf("睡觉结束");

    return 0;
}

执行结果如下:

[root@menwen-linux test]# ./callback
1秒过去了
2秒过去了
时间到了,起床!
3秒过去了
睡觉结束

在时间到第三秒的时候,signal函数执行了fun函数,执行完fun函数,就会继续执行“睡觉结束”。 
如果不设置闹铃(注释alarm(3)),程序就会一直按顺序执行,直到程序结束,永远不会执行回调函数。 
signal函数就是一个回调函数,接收一个函数指针,和一个信号量SIGALRM,在Linux内核代码中,SIGALRM等信号其实是一堆宏定义,都对应一个数值

#define SIGHUP           1
#define SIGINT           2
#define SIGQUIT          3
#define SIGILL           4
......
#define SIGPIPE         13
#define SIGALRM         14
#define SIGTERM         15
#define SIGSTKFLT       16
#define SIGCHLD         17
等等.....

或者命令行输入kill -l 也会对应出现这个信号量。

[menwen@menwen-linux 6th_day]$ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
......
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX    

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。我们实现了fun函数,而我们不调用该函数,而是去睡觉(或者干其他事),等到时间到,回调函数就会去响应,这是一种系统异步处理的机制。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页