详细理解C指针指针函数与函数指针和回调函数.docx

上传人:b****2 文档编号:23157768 上传时间:2023-05-08 格式:DOCX 页数:11 大小:19.83KB
下载 相关 举报
详细理解C指针指针函数与函数指针和回调函数.docx_第1页
第1页 / 共11页
详细理解C指针指针函数与函数指针和回调函数.docx_第2页
第2页 / 共11页
详细理解C指针指针函数与函数指针和回调函数.docx_第3页
第3页 / 共11页
详细理解C指针指针函数与函数指针和回调函数.docx_第4页
第4页 / 共11页
详细理解C指针指针函数与函数指针和回调函数.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

详细理解C指针指针函数与函数指针和回调函数.docx

《详细理解C指针指针函数与函数指针和回调函数.docx》由会员分享,可在线阅读,更多相关《详细理解C指针指针函数与函数指针和回调函数.docx(11页珍藏版)》请在冰豆网上搜索。

详细理解C指针指针函数与函数指针和回调函数.docx

详细理解C指针指针函数与函数指针和回调函数

1、函数指针:

指针函数是指带指针的函数,即本质是一个函数。

我们知道函数都又返回类型(如果不返回值,则为无值型),只不过指针函数返回类型是某一类型的指针。

其定义格式如下所示:

返回类型标识符*返回名称(形式参数表)

{函数体}

返回类型可以是任何基本类型和复合类型。

返回指针的函数的用途十分广泛。

事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。

比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个“变量”。

例如下面一个返回指针函数的例子:

float*find(float(*pionter)[4],intn)/*定义指针函数*/

{

    float*pt;

    pt=*(pionter+n);

    return(pt);

}

main()

{

    staticfloatscore[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}};

    float*p;

    inti,m;

    printf("Enterthenumbertobefound:

");

    scanf("%d",&m);

    printf("thescoreofNO.%dare:

\n",m);

    p=find(score,m);

    for(i=0;i<4;i++)

        printf("%5.2f\t",*(p+i));

}

学生学号从0号算起,函数find()被定义为指针函数,起形参pointer是指针指向包含4个元素的一维数组的指针变量。

pointer+1指向score的第一行。

*(pointer+1)指向第一行的第0个元素。

pt是一个指针变量,它指向浮点型变量。

main()函数中调用find()函数,将score数组的首地址传给pointer.

 将字符串1(str1)连接字符串2(str2),并输出字符串1.

#include"stdio.h"

 char*mystrcpy(char*str1,char*str2)

{

   char*p;

   p=str1;

   while(*str1)

   str1++;

   while(*str1++=*str2++);

   returnp;

}

intmain(void)

{

 charstr1[]="ILOVESHY";

 charstr2[]="  chj!

";

 char*p;

 p=mystrcpy(str1,str2);

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

}

例3:

int*GetDate(intwk,intdy)

       {

           staticintcalendar[5][7]=

           {

              {1,2,3,4,5,6,7},

              {8,9,10,11,12,13,14},

              {15,16,17,18,19,20,21},

              {22,23,24,25,26,27,28},

              {29,30,31,-1,0}

           };

           return(&calendar[wk-1][dy-1]);

       }

   int main(void)

       {

           intwk,dy;

           do

           {

               printf("Enterweek(1-5)day(1-7)\n");

               scanf("%d%d",&wk,&dy);

           }

           while(wk<1||wk>5||dy<1||dy>7);

           printf("%d",*GetDate(wk,dy));

       }

2,函数指针:

“函数指针”是指向函数的指针变量,因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。

这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。

如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。

有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上一致的。

函数指针有两个用途:

调用函数和做函数的参数。

函数指针的说明方法为:

函数类型(*指针变量名)(形参列表);

“函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。

例如:

int(*f)(intx);

double(*ptr)(doublex);

在定义函数指针时请注意:

   

函数指针和它指向的函数的参数个数和类型都应该是—致的;

函数指针的类型和函数的返回值类型也必须是一致的。

函数指针的赋值

函数名和数组名一样代表了函数代码的首地址,因此在赋值时,直接将函数指针指向函数名就行了。

例如,

intfunc(intx);  /*声明一个函数*/

int(*f)(intx);   /*声明一个函数指针*/

f=func;           /*将func函数的首地址赋给指针f*/

赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。

与其他指针变量相类似,如果指针变量pi是指向某整型变量i的指针,则*p等于它所指的变量i;如果pf是指向某浮点型变量f的指针,则*pf就等价于它所指的变量f。

同样地,*f是指向函数func(x)的指针,则*f就代表它所指向的函数func。

所以在执行了f=func;之后,(*f)和func代表同一函数。

由于函数指针指向存储区中的某个函数,因此可以通过函数指针调用相应的函数。

现在我们就讨论如何用函数指针调用函数,它应执行下面三步:

首先,要说明函数指针变量。

例如:

int(*f)(intx);

其次,要对函数指针变量赋值。

例如:

f=func;   (func(x)必须先要有定义)

最后,要用(*指针变量)(参数表);调用函数。

例如:

   (*f)(x);(x必须先赋值)

例1:

 intmax(intx,inty)

{return(x>y?

x:

y);

}

int main(void)

{

   inta,b,c;

    int(*ptr)(int,int);

   scanf("%d,%d",&a,&b);

    ptr=max;

   c=(*ptr)(a,b);

   printf("a=%d,b=%d,max=%d",a,b,c);

   return0;

}

例2:

      voidFileFunc()

       {

           printf("FileFunc\n");

       }

       voidEditFunc()

       {

           printf("EditFunc\n");

       }

       intmain(void)

       {

           void(*funcp)();

           funcp=FileFunc;

           (*funcp)();

           funcp=EditFunc;

           (*funcp)();

       }

例3:

ptr是指向函数的指针变量,所以可把函数max()赋给ptr作为ptr的值,即把max()的入口地址赋给ptr,以后就可以用ptr来调用该函数,实际上ptr和max都指向同一个入口地址,不同就是ptr是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你像怎么做了。

在程序中把哪个函数的地址赋给它,它就指向哪个函数。

而后用指针变量调用它,因此可以先后指向不同的函数,不过注意,指向函数的指针变量没有++和--运算,用时要小心。

3、函数指针数组

关于函数指针数组的定义

关于函数指针数组的定义方法,有两种:

一种是标准的方法;一种是蒙骗法。

第一种,标准方法:

{

分析:

函数指针数组是一个其元素是函数指针的数组。

那么也就是说,此数据结构是是一个数组,且其元素是一个指向函数入口地址的指针。

根据分析:

首先说明是一个数组:

数组名[]

其次,要说明其元素的数据类型指针:

*数组名[].

再次,要明确这每一个数组元素是指向函数入口地址的指针:

函数返回值类型(*数组名[])().请注意,这里为什么要把“*数组名[]”用括号扩起来呢?

因为圆括号和数组说明符的优先级是等同的,如果不用圆括号把指针数组说明表达式扩起来,根据圆括号和方括号的结合方向,那么*数组名[]()说明的是什么呢?

是元素返回值类型为指针的函数数组。

有这样的函数数祖吗?

不知道。

所以必须括起来,以保证数组的每一个元素是指针。

}

第二种,蒙骗法:

尽管函数不是变量,但它在内存中仍有其物理地址,该地址能够赋给指针变量。

获取函数方法是:

用不带有括号和参数的函数名得到。

函数名相当于一个指向其函数入口指针常量。

那么既然函数名是一个指针常量,那么就可以对其进行一些相应的处理,如强制类型转换。

那么我们就可以把这个地址放在一个整形指针数组中,然后作为函数指针调用即可。

完整例子:

#include"stdio.h"

intadd1(inta1,intb1);

intadd2(inta2,intb2);

intmain(intargc,char*argv[])

{

intnuma1=1,numb1=2;

intnuma2=2,numb2=3;

int(*op[2])(inta,intb);

op[0]=add1;

op[1]=add2;

printf("%d%d\n",op[0](numa1,numb1),op[1](numa2,numb2));

getch();

}

intadd1(inta1,intb1)

{

returna1+b1;

}

intadd2(inta2,intb2)

{

returna2+b2;

}

再给出常用的C变量的定义方式:

a)一个整型数(Aninteger)

b)一个指向整型数的指针(Apointertoaninteger)

c)一个指向指针的的指针,它指向的指针是指向一个整型数(Apointertoapointertoaninteger)

d)一个有10个整型数的数组(Anarrayof10integers)

e)一个有10个指针的数组,该指针是指向一个整型数的(Anarrayof10pointerstointegers)

f)一个指向有10个整型数数组的指针(Apointertoanarrayof10integers)

g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(Apointertoafunctionthattakesanintegerasanargumentandreturnsaninteger)

h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(Anarrayoftenpointerstofunctionsthattakeanintegerargumentandreturnan

integer)

答案是:

a)inta;//Aninteger

b)int*a;//Apointertoaninteger

c)int**a;//Apointertoapointertoaninteger

d)inta[10];//Anarrayof10integers

e)int*a[10];//Anarrayof10pointerstointegers

f)int(*a)[10];//Apointertoanarrayof10integers

g)int(*a)(int);//Apointertoafunctionathattakesanintegerargumentandreturnsaninteger

h)int(*a[10])(int);//Anarrayof10pointerstofunctionsthattakeanintegerargumentandreturnaninteger

####################################################################

#####################################################

程序员常常需要实现回调。

本文将讨论函数指针的基本原则并说明如何使用函数指针实现回调。

注意这里针对的是普通的函数,不包括完全依赖于不同语法和语义规则的类成员函数(类成员指针将在另文中讨论)。

声明函数指针

   回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。

要实现回调,必须首先定义函数指针。

尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。

请看下面的例子:

voidf();//函数原型

上面的语句声明了一个函数,没有输入参数并返回void。

那么函数指针的声明方法如下:

void(*)();

   让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。

另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。

注意本例中还没有创建指针变量-只是声明了变量类型。

目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:

//获得函数指针的大小

unsignedpsize=sizeof(void(*)()); 

//为函数指针声明类型定义

typedefvoid(*pfv)();

pfv是一个函数指针,它指向的函数没有输入参数,返回类行为void。

使用这个类型定义名可以隐藏复杂的函数指针语法。

指针变量应该有一个变量名:

void(*p)();//p是指向某函数的指针

   p是指向某函数的指针,该函数无输入参数,返回值的类型为void。

左边圆括弧里星号后的就是指针变量名。

有了指针变量便可以赋值,值的内容是署名匹配的函数名和返回类型。

例如:

voidfunc() 

{

/*dosomething*/

p=func; 

p的赋值可以不同,但一定要是函数的地址,并且署名和返回类型相同。

传递回调函数的地址给调用者

   现在可以将p传递给另一个函数(调用者)-caller(),它将调用p指向的函数,而此函数名是未知的:

voidcaller(void(*ptr)())

{

ptr();/*调用ptr指向的函数*/ 

}

voidfunc();

intmain()

{

p=func; 

caller(p);/*传递函数地址到调用者*/

}

   如果赋了不同的值给p(不同函数地址),那么调用者将调用不同地址的函数。

赋值可以发生在运行时,这样使你能实现动态绑定。

调用规范

   到目前为止,我们只讨论了函数指针及回调而没有去注意ANSIC/C++的编译器规范。

许多编译器有几种调用规范。

如在VisualC++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。

C++Builder也支持_fastcall调用规范。

调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。

   将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。

例如:

//被调用函数是以int为参数,以int为返回值

__stdcallintcallee(int); 

//调用函数以函数指针为参数

voidcaller(__cdeclint(*ptr)(int)); 

//在p中企图存储被调用函数地址的非法操作

__cdeclint(*p)(int)=callee;//出错

   指针p和callee()的类型不兼容,因为它们有不同的调用规范。

因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1