玩转函数指针

题外话
在c99标准(ISO/IEC 9899):TC2,6.7.6(Type names)节中说,类型名在语义上是一个省略了标识符的函数或对象声明
例如:
指向int的指针这样一个类型: int *
相当于:
int * pInt
中省掉了标识符pInt

要点在分析指针相关的表达式时,把自己当成编译器,将表达式执行一遍,你就可以分析出到底是在声明什么了

1.函数指针

int (*pfunc)(int op1, int op2)

分析:
1).按照优先级规则,先执行”(*pfunc)”,*对pfunc进行间接访问,编译器期待pfunc是一个指针,所以pfunc是一个指向某种类型的指针
2).执行表达式(int op1, int op2),这是个函数调用的样子,可知(*pfunc)的结果是一个函数,从而确定pfunc是函数指针
3).进一步分析,可知指针指向的该函数接受两个int参数,返回int

所以:pfunc是一个函数指针,其指向的函数接受两个int参数,返回int

上面*pfunc外面的括号是必须的,若写成这样:

int *pfunc(int op1,int op2)

根据优先级规则,函数调用的优先级高,编译器期待小括号前面的pfunc是个函数,所以pfunc就是一个函数,它接受两个int参数,返回指向int的指针

2.函数指针数组

比如,如下声明

int (*pfunc_arr[3])(int op1,int op2)

分析:
1)按照优先级规则,先执行下标运算[3],可知pfunc_arr是一个长度为3的数组,
2)然后执行间接访问操作*,可知数组元素的类型是指针
3)之后是函数调用操作(int op1,int op2),可知指针是个函数指针,其指向的函数接受两个int参数
4)得到3)中的信息之后,往最开头看,可知那个指针指向的函数返回int

所以,pfunc_arr是一个数组,其中每一个元素是一个函数指针,指向的函数接受两个int参数,返回int
完整的例子:

#include 

int add(int a, int b)
{
    printf("a + b = ");
    return a + b;
}

int sub(int a, int b)
{
    printf("a - b = ");
    return a - b;
}

int multiply(int a, int b)
{
    printf("a * b = ");
    return a * b;
}

int main(void){
    //test function pointer array
    int(*pfarr[3])(int,int);
    pfarr[0] = add;
    pfarr[1] = sub;
    pfarr[2] = multiply;

    int i,result;
    int a=1,b=99;
    printf("a=%d,b=%d\n",a,b);
    for( i = 0; i < 3; i++){
        result = pfarr[i](a, b);
        printf("%d\n",result);
    }
    return 0;
}

3.函数指针作为函数的参数

这个比较简单了,在需要放参数的地方,放一个函数指针声明就可以

比如,如下声明:

int do_it(in (*operation)(int, int),int op1, int op2)

分析:
in (*operation)(int, int),这整个是第一个参数,它是一个函数指针,使用typedef替换下,上面的函数相当于

typedef in (*fp)(int, int);
int do_it(fp operation, int op1, int op2);

我们声明的do_it可以如下使用:

//...
int add(int a, int b)
{
    printf("a + b = ");
    return a + b;
}
//...

int result;
result = do_it(add,1,2);

4.函数指针作为函数返回值 (返回函数指针的函数)

比如,我们想要些一个函数,func_maker,接受一个字符op,如果op是’a’,那么返回一个加法函数指针,
如果op是’s’,那么返回一个减法函数指针。加减法函数指针的类型为:

int (*)(int,int)

我们可以这样声明这个函数:

int (*func_maker(char op))(int,int)

分析:
1)按照优先级规则,先执行func_maker(char op),这是个函数调用的样子,所以func_maker是个函数名,这是个函数声明
2)前面有个间接访问*,说明函数返回某种类型的指针
3)最后面有(int,int),这又是个函数调用的样子,说明前面提到的指针是函数指针,接受两个int参数
4)得到3中的信息之后,往最开头看,可知那个指针指向的函数返回int

所以上面声明了一个叫做func_maker的函数,这个函数接受一个叫做op的char型参数,返回一个函数指针,
该指针指向的函数接受两个int参数,返回一个int

完整例子:

#include 
#include 

int add(int a, int b)
{
    printf("a + b = ");
    return a + b;
}

int sub(int a, int b)
{
    printf("a - b = ");
    return a - b;
}

int (*func_maker(char op))(int,int)
{
    switch(op)
    {
        case 'a':
            return add;
        case 's':
            return sub;
        default:
            return NULL;
    }
}

int main(void){
    int (*fp)(int,int);
    int a=100,b=1;
    int result;
    printf("a = %d, b = %d\n",a,b);
    //do add
    fp = func_maker('a');
    result = fp(a,b);
    printf("%d\n",result);
    // do sub
    fp = func_maker('s');
    result = fp(a,b);
    printf("%d\n",result);

    return 0;
}

signal函数分析

标准c中的signal函数,其原型如下:

void (*signal(int sig, void (*func)(int)))(int);

我们分析下:
1)按照优先级规则,最先执行

void (*func)(int)

这里声明了一个函数指针func,其指向的函数接受一个int参数,返回void
我们可以用一个typedef来简化

typedef void (*sighandler_t)(int)

然后可以用sighandler_t来表示该指针类型
于是,上面的这个表达式可以写成

sighandler_t func

代入回去,得到:

void (*signal(int sig, sighandler_t func))(int);

这样看上去简单了些

2)对于我们简化后的表达式

void (*signal(int sig, sighandler_t func))(int);

按照优先级规则,先执行

signal(int sig, sighandler_t func)

这是函数调用的样子,可知signal是个函数名,这就是所声明的函数名
它接受两个参数,一个是sig,一个是func,返回……,返回值,继续分析

3)接着执行signal前面哪个间接访问*,可知返回值是一个指针,什么样的指针呢,继续分析

4)接着是最后面的(int),这是个函数调用的样子,可知指针是个函数指针,其指向的函数
接受一个int参数,返回……,往最前看,void。
我们不难发现,这个函数指针其实和前面的func是一样类型。
于是我们可以再做一次typedef的代入,结果:

sighandler_t signal(int sig, sighandler_t func);

现在明白了:
1)signal函数接受两个参数,第一个参数为int,第二个参数为一个函数指针,返回值为一个函数指针。
2)作为第二个参数和作为返回值的函数指针指向的函数有这样的特征:
接受一个int参数,返回void
实际上,signal函数将func安装为信号sig新的处理程序,然后返回之前安装的处理程序。

This entry was posted in Programming and tagged , . Bookmark the permalink.

One Response to 玩转函数指针

  1. Curu Wong says:

    Thanks, I will keep it going for sure. ^_^

Leave a Reply