`

函数指针和指针函数

 
阅读更多

本文转自:http://blog.csdn.net/porscheyin/article/details/3461632

1.指向函数的指针(函数指针)

来分析这样一个声明,void (*f) ( );虽然()的优先级高于*,但由于有括号存在,首先执行的是解引用,所以f是一个指针;接下来执行( ),表明f指向一个函数,这个函数不返回任何值。现在得出结论:f是一个指向不接受参数且不返回任何值的函数的指针,简称函数指针(pointer to function)

对比一下int(*p) [100]p是一个指向含有100个整型元素的数组的指针,它们有一个共同的特点:指针声明符(*)和标识符(fp)都被限制在一个括号中,由于括号的优先级是最高的,所以我们从标识符开始由内向外分析,即可得到以上结果。

<1>.初始化

注意指向函数的指针(函数指针)指向的是函数而非普通的变量,它所指向的函数也是有特定类型的,函数的类型由它的返回值类型以及形参列表确定,和函数名无关。对函数指针初始化时可以采用相同类型函数的函数名或函数指针(当然还有零指针常量)。假如有函数void test ( )int wrong_match (int)和函数指针void (*ptf) ( )

下面的初始化是错误的,因为函数指针的类型与函数的类型不匹配:

f = wrong_match;

f = & wrong_match;

ptf = wrong_match;

ptf = & wrong_match;

以下初始化及赋值是合法的:

f = test;

f = &test;

ptf = test;

ptf = &test;

f = pf;

要做出解释的是test&test都可以用来初始化函数指针。C语言规定函数名会被转换为指向这个函数的指针,除非这个函数名作为操作符或sizeof操作符的操作数(注意:函数名用于sizeof的操作数是非法的)也就是说f = test;test被自动转换为&test,而f= &test;中已经显示使用了&test,所以test就不会再发生转换了。因此直接引用函数名等效于在函数名上应用 & 运算符,两种方法都会得到指向该函数的指针。

<2>.通过函数指针调用函数

通过函数指针调用函数可以有两种方法,直接使用函数指针或在函数指针前使用解引用运算符,如下所示:

f = test;

ptf = test;

f ( );

(*f) ( );  //指针两侧的括号非常重要,表示先对f解引用,然后再调用相应的函数

ptf ( );

(*ptf) ( ); //括号同样不能少

以上语句都能达到调用test函数的作用。ANSI C标准将f ( )认为是(*f)( )的简写形式,并且推荐使用f ( )形式,因为它更符合函数调用的逻辑。要注意的是:如果指向函数的指针没有初始化,或者具有0(零指针常量),那么该指针不能在函数调用中使用。只有当指针已经初始化,或被赋值后指向某个函数才能安全地用来调用函数。

<3>.探究函数名

现在有如下程序:

#include<stdio.h>

 

void test( )

{

    printf("test called!\n");

}

 

int main()

{

   void (*f) ( );

    f = test;

   f ( );

    (*f)( );

    //test++;             // error,标准禁止对指向函数的指针进行自增运算

          //test = test + 2;        //error,不能对函数名赋值,函数名也不能用于进行算术运算

          printf("%p\n", test);

          printf("%p\n", &test);

          printf("%p\n", *test);

  return 0;

}

在我机器上的运行结果为:

test called!

test called!

004013EE

004013EE

004013EE

这个程序中较难理解的是3个输出语句都可以得到函数的入口地址。首先来看函数名test,它与数组名类似(注意:只是类似),是一个符号用来标识一个函数的入口地址,在使用中函数名会被转换为指向这个函数的指针,指针的值就是函数的入口地址,&test在前面已经说了:显示获取函数的地址。对于*test,可以认为由于test已经被转换成了函数指针,指向这个函数,所以*test就是取这个指针所指向的函数名,而又根据函数名会被转换指向该函数的指针的规则,这个函数也转变成了一个指针,所以*test最终也是一个指向函数test的指针。对它们采用%p格式项输出,都会得到以16进制数表示的函数test的入口地址。注意函数的地址在编译期是未知的,而是在链接时确定的。

2.返回指针的函数(指针函数)

类比指针数组(还记得吗),理解指针函数将会更加轻松。所谓指针函数,就是返回指针的函数,函数可以不返回任何值,也可以返回整型值,实型值,字符型值,当然也可以返回指针值。一个指针函数的声明:int *f(int i, int j); 回想一下指针数组的声明:char *cars[10];同样的把它写成好理解的形式(非业界惯例)int* f(int i, int j);这样一来已经十分明了了,由于( )的优先级高于*,因此f先与()结合,所以f是一个具有两个int型参数,返回一个指向int型指针的函数。

C语言的库函数中有很多都是指针函数,比如字符串处理函数,下面给出一些函数原型:

char *strcat( char *dest, const char *src );

char *strcpy( char *dest, const char *src );

char *strchr( const char *s, int c );

char *strstr( const char *src, const char*sub );

注意函数的返回值不仅仅局限于指向变量的指针,也可以是指向函数的指针。初遇这种函数的声明可能会痛苦一点儿,但练习两三次应该是可以理解并掌握的。首先来看这个声明:int (*function(int)) (double*,char);要了解此声明的含义,首先来看function(int),将function声明为一个函数,它带有一个int型的形式参数,这个函数的返回值为一个指针,正是我们本将开头讲过的函数指针int (*) (double*, char);这个指针指向一个函数,此函数返回int型并带有两个分别是double*型和char型的形参。如果使用typedef可以将这个声明简化:

typedef int (*ptf) (double*, char);

ptf function(int );

要说明一下,对于typedef int (*ptf) (double*,char); 注意不要用#define的思维来看待typedef,如果用#define的思维来看的话会以为(*ptf)(double*, char)int的别名,但这样的别名看起来好像又不是合法的名字,于是会处于迷茫状态。实际上,上面的语句把ptf定义为一种函数指针类型的别名,它和函数指针类型int (*) (double*, char);等价,也就是说ptf现在也是一种类型。

3.函数指针和指针函数的混合使用

函数指针不仅可以作为返回值类型,还可以作为函数的形式参数,如果一个函数的形参和返回值都是函数指针,这个声明看起来会更加复杂,例如:

void (*signal (int sig, void (*func) (intsiga)) ) ( int siga );看上去确实有些恼人,我们来一步一步的分析。现在要分析的是signal,因为紧邻signal的是优先级最高的括号,首先与括号结合,所以signal为一个函数,括号内为signal的两个形参,一个为int型,一个为指向函数的指针。接下来从向左看,*表示指向某对象的指针,它所处的位置表明它是signal的返回值类型,现在可以把已经分析过的signal整体去掉,得到void (*) ( int siga ),很清晰了吧。又是一个函数指针,这个指针与signal形参表中的第二个参数类型一样,都是指向接受一个int型形参且不返回任何值的函数的指针。同样地,用typedef可以将这个声明简化:

typedef void (*p_sig) (int);

p_sig signal(int sig, p_sig func);

这个signal函数是C语言的库函数,在signal.h中定义,用来处理系统中产生的信号,是UNIX/Linux编程中经常用到的一个函数,所以在此单独拿出来讲解一下。

4.函数指针数组

还有一种较为常用的关于函数指针的用法——函数指针数组。假设现在有一个文件处理程序,通过一个菜单按钮来选择相应的操作(打开文件,读文件,写文件,关闭文件)。这些操作都实现为函数且类型相同,分别为:

void open( );

void read( );

void write( );

void close( );

现在定义一个函数指针类型的别名PFtypedefvoid (*PF) ( );把以上4种操作取地址放入一个数组中,得到:

PF file_options[ ] = {

                &open,

                &read,

                &write,

                &close

};

这个数组中的元素都是指向不接受参数且不返回任何值的函数的指针,因此这是一个函数指针数组。接下来,定义一个函数指针类型的指针action并初始化为函数指针数组的第一个元素:PF* action = file_options;,如果不好理解,可以类比一下int ia[4] = {0, 1, 2, 3}; int *ip = ia;,这里PF相当于int,这样应该比较好懂了。通过对指针action进行下标操作可以调用数组中的任一操作,如:action[2]( )会调用write操作,以此类推。在实际中,指针action可以和鼠标或者其他GUI对象相关联,以达到相应的目的。

5.关于指针的复杂声明

4点中的函数指针数组采用了typedef来声明,这是应该提倡的方法,因为它可读性更高。如果不使用typedef,那么分析起来就会比较复杂,结果是void (*file_options[ ]) ( );对于C语言的复杂声明我不想讲太多,因为在实际中用到的机会并不多,并且推荐大家多用typedef来简化声明的复杂度。对于分析复杂声明有一个极为有效的方法——右左法则。右左法则的大致描述为:从未定义的变量名开始阅读声明,先向右看,然后向左看。当遇到括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号。这样一直继续下去,直到整个声明都被分析完毕。来分析一个的例子:int * (* (*fp) (int) ) [10]; 

阅读步骤:

1.从未定义的变量名开始阅读 --------------------------------------------fp

2.往右看,什么也没有,遇到了),因此往左看,遇到一个* ------一个指向某对象的指针

3.跳出括号,遇到了(int) -----------------------------------一个带一个int参数的函数

4.向左看,发现一个* ---------------------------------------(函数)返回一个指向某对象的指针

5.跳出括号,向右看,遇到[10] ------------------------------一个10元素的数组

6.向左看,发现一个* ---------------------------------------一个指向某对象指针

7.向左看,发现int -----------------------------------------int类型

所以fp是指向函数的指针,该函数返回一个指向数组的指针,此数组有10int*型的元素。

对此我不再多举例了,下面给出一些声明,有兴趣的朋友可以试着分析一下,答案我会在下一讲中给出:

1. int *( *( *a[5]) ( ) ) ( );

2. void * (*b) ( char, int (*) ( ) );

3. float ( *(*c[10]) (int*) ) [5]; 

4. int ( *(*d)[2][3] ) [4][5];

5. int (*(*(*e) ( int* ))[15]) (int*);

6. int ( *(*f[4][5][6]) (int*) ) [10];

7. int *(*(*(*g)( ))[10]) ( );

分享到:
评论

相关推荐

    函数指针和指针函数的区别

    在C/C++编程语言中,函数指针和指针函数是两个不同的概念,虽然它们都涉及到指针和函数,但其用法和含义各有特点。理解这两者的差异对于编写高效、灵活的代码至关重要。 首先,我们来解释"函数指针"。函数指针是一...

    详解函数指针和指针函数

    总之,函数指针和指针函数是C语言中高级的编程工具,它们增加了代码的灵活性和可扩展性,常用于回调函数、动态调度、数据结构(如链表)的实现等场景。理解和熟练掌握这些概念对于深入理解C语言及其在复杂系统中的...

    函数指针和指针函数的理解

    在C语言中,函数指针和指针函数是两个重要的概念,它们经常出现在较为复杂的程序设计中,尤其在回调函数、动态绑定等场景中发挥着重要作用。 **函数指针**指的是指向函数的指针变量,它存储的是函数的入口地址。当...

    函数指针和指针函数,const的用法,指针常量,常量指针的用法

    ### 函数指针和指针函数的区分及应用 #### 函数指针的理解与使用 **函数指针**是一种特殊的指针类型,它可以用来存储函数的地址,进而通过该指针来调用函数。理解函数指针的关键在于认识到函数也是一种具有特定...

    c语言函数指针和指针函数.pdf

    "函数指针和指针函数" 函数指针是 C 语言中的一种特殊的指针,它可以指向函数的地址,而不是变量的地址。函数指针的定义格式为:`返回类型 (*指针变量名)(参数列表)`,其中返回类型是函数的返回类型,指针变量名是...

    函数指针和指针函数的说明

    ### 函数指针和指针函数的说明 #### 引言 C语言作为一种广泛应用的编程语言,其灵活性和强大功能很大程度上得益于指针的应用。指针不仅能够直接操作内存地址,还能通过指针来实现对函数的操作,这包括函数指针和...

    55_函数指针和指针函数的区别1

    在C++编程语言中,函数指针和指针函数是两个不同的概念,它们在定义、使用和表达方式上有显著的差异。理解这些差异对于编写高效和灵活的代码至关重要。 首先,我们来详细解释一下这两个概念: 1. **指针函数**: ...

    学习c语言函数指针和指针函数.pdf

    C语言中的函数指针和指针函数是两个关键概念,它们允许我们以更灵活的方式处理函数,实现回调机制、动态绑定以及在运行时决定调用哪个函数等功能。 1. **函数指针**: - 函数指针是一种特殊的指针变量,它存储的是...

    C语言再学习之函数指针和指针函数[文].pdf

    在C语言中,函数指针和指针函数是高级特性,它们在软件开发中有着广泛的应用,尤其是在系统编程和库函数的设计中。本篇再学习的内容主要围绕函数指针和指针函数的概念、定义、赋值以及如何通过它们来调用函数。 1. ...

    函数指针和指针函数[借鉴].pdf

    在编程语言中,函数指针和指针函数是两种不同的概念,它们在C语言和C++等编程语言中尤其常见。这些概念对于理解和编写高级的、动态的代码至关重要。 首先,我们来澄清这两个术语的区别: 1. **函数指针**:函数...

    函数指针和指针函数区别[参考].pdf

    在编程领域,函数指针和指针函数...总之,函数指针和指针函数都是C语言中非常重要的特性,它们允许我们以灵活的方式处理函数和数据,增强了程序的可扩展性和模块化。正确理解和使用这些概念对于提升编程能力至关重要。

    C语言再学习之函数指针和指针函数.pdf

    在C语言中,函数指针和指针函数是高级特性,它们允许我们以更灵活的方式操作函数和数据。本文将详细解释这两个概念及其应用。 **函数指针** 函数指针是一种特殊的指针变量,它可以存储函数的地址,使得我们可以...

    学习C语言再学习之函数指针和指针函数.pdf

    在C语言中,函数指针和指针函数是高级特性,它们允许我们以更灵活的方式处理函数和数据。本文将详细解释这两个概念以及如何在实际编程中应用它们。 首先,让我们了解一下函数指针。函数在内存中占据一定的空间,就...

    函数指针与指针函数应用程序

    在编程世界中,函数指针和指针函数是C/C++等语言中非常重要的概念,它们为程序提供了灵活和高效的方式处理数据和控制流程。在这个“函数指针与指针函数应用程序”中,我们将深入探讨这两个核心主题。 首先,我们要...

    c语言函数指针和指针函数[收集].pdf

    c语言函数指针和指针函数[收集].pdf

    指针函数和函数指针变量

    在C语言中,指针和函数是两个非常重要的概念,而将它们结合在一起,就产生了指针函数和函数指针变量。这两个概念是C语言高级特性的体现,它们在编程中有着广泛的应用,如回调函数、动态加载库、内存管理等。 首先,...

    函数指针和函数对象

    函数指针和函数对象 函数指针(全局函数/类成员函数)和函数对象是C++语言中两个重要的概念,经常被混淆和误解。本文将详细介绍函数指针和函数对象的定义、类型、使用方法和区别。 一、函数指针 函数指针是指向...

    函数指针与指针函数

    ### 函数指针与指针函数详解 在深入探讨函数指针与指针函数之前,我们首先需要理解...通过理解和运用函数指针与指针函数,开发者可以实现诸如回调函数、动态加载库、策略模式等功能,从而提升代码的可扩展性和复用性。

Global site tag (gtag.js) - Google Analytics