文档库 最新最全的文档下载
当前位置:文档库 › 指向类的成员函数的指针如何使用(详解)

指向类的成员函数的指针如何使用(详解)

指向类的成员函数的指针如何使用(详解)
指向类的成员函数的指针如何使用(详解)

我们首先复习一下"指向函数的指针"如何使用?

void print()

{

}

void (*pfun)(); //声明一个指向函数的指针,函数的参数是void,函数的返回值是void pfun = print; //赋值一个指向函数的指针

(*pfun)(); //使用一个指向函数的指针

比较简单,不是吗?为什么*pfun 需要用()扩起来呢?因为*的运算符优先级比()低,如果不用()就成了*(pfun()).

指向类的成员函数的指针不过多了一个类的限定而已!

class A

{

void speak(char *, const char *);

};

void main()

{

A a;

void (A::*pmf)(char *, const char *);//指针的声明

pmf = &A::speak; //指针的赋值

}

一个指向类 A 成员函数的指针声明为:

void (A::*pmf)(char *, const char *);

声明的解释是:pmf 是一个指向A 成员函数的指针,返回无类型值,函数带有二个参数,参数的类型分别是char *和const char *。除了在星号前增加A::,与声明外部函数指针的方法一样。一种更加高明的方法是使用类型定义:

例如,下面的语句定义了PMA 是一个指向 A 成员函数的指针,函数返回无类型值,函数参数类型为char *和const char *:

typedef void(A::*PMA)(char *, const char *);

PMA pmf= &A::strcat; // pmf 是PMF 类型(类A 成员指针)的变量

下面请看关于类的使用的示例程序!

#include

using namespace std;

class Person

{

public:

/*这里稍稍注意一下,我将speak()函数设置为普通的成员函数,而hello()函数设置为虚函数*/

int value;

void speak()

{

cout<<"I am a person!"<

printf("%d\n",&Person::speak);/*在这里验证一下,输出一下地址就知道了!*/

}

virtual void hello()

{

cout<<"Person say \"Hello\""<

}

Person()

{

value=1;

}

};

class Baizhantang:public Person

{

public:

void speak()

{

cout<<"I am 白展堂!"<

}

virtual void hello()

{

cout<<"白展堂 say \"hello!\""<

}

Baizhantang()

{

value=2;

}

};

typedef void(Person::*p)();//定义指向Person类无参数无返回值的成员函数的指针

typedef void(Baizhantang::*q)();//定义指向Baizhantang类的无参数无返回值的指针

int main()

{

Person pe;

int i=1;

p ip;

ip=&Person::speak;//ip指向Person类speak函数

(pe.*ip)();//这个是正确的写法!

//--------------------------------------------

// result : I am a Person!

// XXXXXXXXXX(表示一段地址)

//--------------------------------------------

/*

*下面是几种错误的写法,要注意!

* pe.*ip();

* pe.(*ip)();

* (pe.(*ip))();

*/

Baizhantang bzt;

q iq=(void(Baizhantang::*)())ip;//强制转换

(bzt.*iq)();

//--------------------------------------------

// result : I am a Person!

// XXXXXXXXXX(表示一段地址)

//--------------------------------------------

/* 有人可能会问了:ip明明被强制转换成了Baizhantang类的成员函数的指针,为什么输出结果还是:

* I am a Person!在C++里面,类的非虚函数都是采用静态绑定,

也就是说类的非虚函数在编译前就已经

*确定了函数地址!ip之前就是指向Person::speak函数的地址,强制转换之后,只是指针类型变了,里面

*的值并没有改变,所以调用的还是Person.speak函数,细心的家伙会发现,输出的地址都是一致的.

*这里要强调一下:对于类的非静态成员函数,c++编译器会给每个函数的参数添加上一个该类的指针this,这也

*就是为什么我们在非静态类成员函数里面可以使用this指针的原因,当然,这个过程你看不见!而对于静态成员

*函数,编译器不会添加这样一个this。

*/

iq=&Baizhantang::speak;/*iq指向了Baizhantang类的speak函数*/

ip=(void(Person::*)())iq;/*ip接收强制转换之后的

iq指针*/

(bzt.*ip)();

//--------------------------------------------

// result : I am 白展堂!

//--------------------------------------------

(bzt.*iq)();//这里我强调一下,使用了动态联编,也就是说函数在运行是才确定函数地址!

//--------------------------------------------

// result : I am 白展堂!

//--------------------------------------------

/*这一部分就没有什么好讲的了,很明白了!由于speak函数是普

通的成员函数,在编译时就知道

*到了Baizhantang::speak的地址,因此(bzt.*ip)()会输出

“I am 白展堂!”,即使iq被强制转换

*成(void (Person::*)())类型的ip,但是其值亦未改变,

(bzt.*iq)()依然调用iq指向处的函数

*即Baizhantang::speak.

*/

/*好了,上面讲完了普通成员函数,我们现在来玩一点好玩的,现

在来聊虚函数*/

ip=&Person::hello;/*让ip指向Person::hello函数*/ (pe.*ip)();

//--------------------------------------------

// result : Person say "Hello"

//--------------------------------------------

(bzt.*ip)();

//--------------------------------------------

// result : 白展堂 say "Hello"

//--------------------------------------------

/*咦,这就奇怪了,为何与上面的调用结果不类似?为什么两个调

用结果不一致?伙伴们注意了:

*speak函数是一个虚函数,前面说过虚函数并不是采用静态绑定

的,而是采用动态绑定,所谓动态

*绑定,就是函数地址得等到运行的时候才确定,对于有虚函数的类,编译器会给我们添加一个指针

*vptr,指向一个虚函数表vptl,vptl里面存放着虚函数的地址,子类继承父类的时候,也会继承这样

*一个指针,如果子类复写了虚函数,那么该表中该虚函数地址将会由父类的虚函数地址替换成子类虚

*函数地址,编译器会把(pe.*ip)()转化成为

(pe.vptr[1])(pe),加上动态绑定,结果会输出:

* Person say "Hello"

*(bzt.*ip)()会被转换成(bzt.vptr[1])(pe),自然会输出: * 白展堂 say "Hello"

*ps:这里我没法讲得更详细,因为解释起来肯定是很长很长的,感兴趣的话,我推荐两本书你去看一看:

* 第一本是侯捷老师的<深入浅出MFC>,里面关于c++的虚函数特性讲的比较清楚;

* 第二本是侯捷老师翻译的<深度探索C++对象模型>,一听名字就知道,讲这个就更详细了;

*当然,不感兴趣的同学这段解释可以省略,对与使用没有影响!

*/

iq=(void(Baizhantang::*)())ip;

(bzt.*iq)();

//--------------------------------------------// result : 白展堂 say "Hello"

//--------------------------------------------system("pause");

return0;

}

函数指针

方法 指针函数和函数指针的区别 关于函数指针数组的定义 为函数指针数组赋值 函数指针的声明方法为: 数据类型标志符 (指针变量名) (形参列表); 注1:“函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例如: int func(int x); /* 声明一个函数 */ int (*f) (int x); /* 声明一个函数指针 */ f=func; /* 将func函数的首地址赋给指针f */ 赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。 注2:函数括号中的形参可有可无,视情况而定。 下面的程序说明了函数指针调用函数的方法: 例一、 #include int max(int x,int y){ return(x>y?x:y); } void main() { int (*ptr)(int, int); int a,b,c; ptr=max; scanf("%d%d",&a,&b); c=(*ptr)(a,b); printf("a=%d,b=%d,max=%d",a,b,c); } ptr是指向函数的指针变量,所以可把函数max()赋给ptr作为ptr的值,即把max()的入口地址赋给ptr,以后就可以用ptr来调用该函数,实际上ptr 和max都指向同一个入口地址,不同就是ptr是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你想怎么做了。在程序中把哪个

C指针函数习题

C++指针函数习题 一、选择题 1.以下程序的运行结果是()。 sub(int x, int y, int *z) { *z=y-x; } void main() { int a,b; sub(10,5,&a); sub(7,a,&b); cout< #include<>

指针变量作为函数参数

用名作为其他变量名地别名. ; 等价于; ()声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名地一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元.故:对引用求地址,就是对目标变量求地址.与相等. ()不能建立数组地引用.因为数组是一个由若干个元素所组成地集合,所以无法建立一个数组地别名. 引用应用 、引用作为参数 引用地一个重要作用就是作为函数地参数.以前地语言中函数参数传递是值传递,如果有大块数据作为参数传递地时候,采用地方案往往是指针,因为这样可以避免将整块数据全部压栈,可以提高程序地效率.但是现在(中)又增加了一种同样有效率地选择(在某些特殊情况下又是必须地选择),就是引用. 【例】: ( , ) 此处函数地形参, 都是引用 { ; ; ; ; } 为在程序中调用该函数,则相应地主调函数地调用点处,直接以变量作为实参进行调用即可,而不需要实参变量有任何地特殊要求.如:对应上面定义地函数,相应地主调函数可写为: ( ) { ; >>>>; 输入两变量地值 (); 直接以变量和作为实参调用函数 <<<< ' ' <<; 输出结果 }

上述程序运行时,如果输入数据并回车后,则输出结果为. 由【例】可看出: ()传递引用给函数与传递指针地效果是一样地.这时,被调函数地形参就成为原来主调函数中地实参变量或对象地一个别名来使用,所以在被调函数中对形参变量地操作就是对其相应地目标对象(在主调函数中)地操作. ()使用引用传递函数地参数,在内存中并没有产生实参地副本,它是直接对实参操作;而使用一般变量传递函数地参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量地副本;如果传递地是对象,还将调用拷贝构造函数.因此,当参数传递地数据较大时,用引用比用一般变量传递参数地效率和所占空间都好. ()使用指针作为函数地参数虽然也能达到与使用引用地效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"地形式进行运算,这很容易产生错误且程序地阅读性较差;另一方面,在主调函数地调用点处,必须用变量地地址作为实参.而引用更容易使用,更清晰. 如果既要利用引用提高程序地效率,又要保护传递给函数地数据不在函数中被改变,就应使用常引用. 、常引用 常引用声明方式:类型标识符引用名目标变量名; 用这种方式声明地引用,不能通过引用对目标变量地值进行修改,从而使引用地目标成为,达到了引用地安全性. 【例】: ; ; ; 错误 ; 正确 这不光是让代码更健壮,也有些其它方面地需要. 【例】:假设有如下函数声明:

指向函数的指针详解

指向函数的指针 函数指针是指指向函数而非指向对象的指针。像其他指针一样,函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定,而与函数名无关: bool (*pf)(const string &,const string &); 这个语句将pf声明为指向函数的指针,它所指向的函数带有两个const string &类型的形参和bool 类型的返回值。 注意:*pf两侧的括号是必需的。 1.typedef简化函数指针的定义: 函数指针类型相当地冗长。使用typedef为指针类型定义同义词,可将函数指针的使用大大简化: Typedef bool (*cmpfn)(const string &,const string &); 该定义表示cmpfn是一种指向函数的指针类型的名字。该指针类型为“指向返回bool类型并带有两个const string 引用形参的函数的指针”。在要使用这种函数指针类型时,只需直接使用cmpfcn即可,不必每次都把整个类型声明全部写出来。 2.指向函数的指针的初始化和赋值 在引用函数名但又没有调用该函数时,函数名将被自动解释为指向函数的指针。假设有函数: Bool lengthcompare(const string &,const string &); 除了用作函数调用的左操作数以外,对lengthcompare的任何使用都被解释为如下类型的指针:

bool (*)(const string &,const string &); 可使用函数名对函数指针初始化或赋值: cmpfn pf1=0; cmpfn pf2=lengthcompare; pf1=legnthcompare; pf2=pf1; 此时,直接引用函数名等效于在函数名上应用取地址操作符: cmpfcn pf1=lengthcompare; cmpfcn pf2=lengthcompare; 注意:函数指针只能通过同类型的函数或函数指针或0值常量表达式进行初始化或赋值。 将函数指针初始化为0,表示该指针不指向任何函数。 指向不两只函数类型的指针之间不存在转换: string::size_type sumLength(const string &,const string &); bool cstringCompare(char *,char *); //pointer to function returning bool taking two const string& cmpFcn pf;//error:return type differs pf=cstringCompare;//error:parameter types differ pf=lengthCompare;//ok:function and pointer types match exactly 3.通过指针调用函数 指向函数的指针可用于调用它所指向的函数。可以不需要使用解引用

指针函数与函数指针的区别

指针函数与函数指针的区别 一、 在学习arm过程中发现这“指针函数”与“函数指针”容易搞错,所以今天,我自己想一次把它搞清楚,找了一些资料,首先它们之间的定义: 1、指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的指针 类型标识符 *函数名(参数表) int *f(x,y); 首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。 表示: float *fun(); float *p; p = fun(a); 注意指针函数与函数指针表示方法的不同,千万不要混淆。最简单的辨别方式就是看函数名前面的指针*号有没有被括号()包含,如果被包含就是函数指针,反之则是指针函数。来讲详细一些吧!请看下面 指针函数: 当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中。 格式: 类型说明符* 函数名(参数) 当然了,由于返回的是一个地址,所以类型说明符一般都是int。 例如:int *GetDate(); int * aaa(int,int); 函数返回的是一个地址值,经常使用在返回数组的某一元素地址上。 int * GetDate(int wk,int dy); main() { int wk,dy; do { printf(Enter week(1-5)day(1-7)\n); scanf(%d%d,&wk,&dy); } while(wk<1||wk>5||dy<1||dy>7); printf(%d\n,*GetDate(wk,dy));

指向函数的指针

指向函数的指针 c/c++ 2010-11-20 13:17:02 阅读41 评论0 字号:大中小订阅首先看这个程序: #include using namespace std; void max(int a, int b) { cout<<"now call max("<b?a:b; cout<

我曾经写过一个命令行程序,有很多命令,于是构着了一个结构的数组,大概是这样 struct{ char *cmd_name; bool (*cmd_fun)(); }cmd_info_list[MAX_CMD_NUM]; 程序中得到一个用户输入的命令字符串后,就匹配这个数组,找到对应的处理函数。 以后每次添加一个命令,只需要加个函数,然后在这个数组中加一个记录就可以了,不需要修改太多的代码。 这可以算是一种用法吧。呵呵。 Windows 中,窗口的回调函数就用到了函数指针。 用VC向导 New Projects ----> Win32 Application ----> A typical "Hello World!" application 其中的WndProc 是WNDPROC 类型的函数typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM); WndProc 作为窗口的回调函数,用来填充WNDCLASSEX 结构。 WNDCLASSEX wcex; wcex.lpfnWndProc = (WNDPROC)WndProc; void ListTraverse(LinkList L,void (*visit)(int)) { Link p; p=L->next; while(p) { visit(p->data); p=p->next; } return OK; } void print(int c) { printf("%d",c); } ListTraverse(L,print); 这算是个例子吧??? #include #include #include double Add (double x, double y) { return x+y; } double Sub (double x, double y) { return x-y; } double Mul (double x, double y)

函数及指针练习题

函数练习题 【1.54】对函数形参的说明有错误的是____。 A) int a(float x[],int n) B) int a(float *x,int n) C) int a(float x[10],int n) D) int a(float x,int n) 【1.55】如果一个变量在整个程序运行期间都存在,但是仅在说明它的函数内是可见的,这个变量的存储类型应该被说明为____。 A)静态变量B) 动态变量C) 外部变量D) 内部变量 【1.56】在一个C源程序文件中,?若要定义一个只允许在该源文件中所有函数使用的变量,则该变量需要使用的存储类别是。 A) extern B) register C) auto D) static 【1.57】在C语言中,函数的数据类型是指____。 A)函数返回值的数据类型B) 函数形参的数据类型 C) 调用该函数时的实参的数据类型D) 任意指定的数据类型 【1.58】已知如下定义的函数: fun1(a) { printf("\n%d",a); } 则该函数的数据类型是____。 A)与参数a的类型相同B) void型 C) 没有返回值D) 无法确定 【1.59】定义一个函数实现交换x和y的值,并将结果正确返回。能够实现此功能的是____。 A) swapa(int x,int y) B) swapb(int *x,int *y) { int temp;{ int temp; temp=x;x=y;y=temp;temp=x;x=y;y=temp; } } C) swapc(int *x,int *y) D) swapd(int *x,int *y) { int temp;{ int *temp; temp=*x;*x=*y;*y=temp;temp=x;x=y;y=temp; } } 【1.60】求一个角的正弦函数值的平方。能够实现此功能的函数是____。 A) sqofsina(x) float x; { return(sin(x)*sin(x)); } B) double sqofsinb(x) float x; { return(sin((double)x)*sin((double)x)); } C) double sqofsinc(x) { return(((sin(x)*sin(x)); } D) sqofsind(x) float x;

如何用指向函数的指针替换switch-case

程序中当switch-case里面有很多分支,且每个分支有很多操作要做时,会花费很多时间,优化的方法有很多,比如将可能性大的放在前面,将case操作压缩,其实,最好的方法利用指向函数的指针解决问题,下面我们来看一个例子: 先来一个switch-case的模板: view plaincopy to clipboardprint? 1. #include 2. #include 3. 4. enum MsgType{ Msg1=1 ,Msg2 ,Msg3 ,Msg4 }; 5. 6. int menu() 7. { 8. int choice; 9. printf("1.ADD/n"); 10. printf("2.DEL/n"); 11. printf("3.SORT/n"); 12. printf("4.exit/n"); 13. printf("input your choice:/n"); 14. scanf("%d",&choice); 15. return choice; 16. } 17. 18. void ADD() 19. { 20. NULL; 21. // 22. } 23. 24. void DEL() 25. { 26. NULL; 27. // 28. } 29. 30. void SORT() 31. { 32. NULL; 33. // 34. } 35. 36. int main()

37. { 38. while(1) 39. { 40. switch(menu()) 41. { 42. case Msg1: 43. ADD(); 44. break; 45. case Msg2: 46. DEL(); 47. break; 48. case Msg3: 49. SORT(); 50. break; 51. case Msg4: 52. exit(0); 53. break; 54. default: 55. printf("input error/n"); 56. break; 57. } 58. } 59. return 0; 60. } 61. 62. 1. #include 2. #include 3. 4. void ADD(); 5. void DEL(); 6. void SORT();

函数指针的使用方法

对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简而言之,回调函数是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来调用它所指向的函数时,我们就说这个函数是回调函数。 为什么要使用回调函数呢?我们先看一个小例子: Node * Search_List (Node * node, const int value) { while (node != NULL) { if (node -> value == value) { break; } node = node -> next; } return node; } 这个函数用于在一个单向链表中查找一个指定的值,返回保存这个值的节点。它的参数是指向这个链表第一个节点的指针以及要查找的值。这个函数看上去很简单,但是我们考虑一个问题:它只能适用于值为整数的链表,如果查找一个字符串链表,我们不得不再写一个函数,其实大部分代码和现在这个函数相同,只是第二个参数的类型和比较的方法不同。 其实我们更希望令查找函数与类型无关,这样它就能用于查找存放任何类型值的链表了,因此必须改变比较的方式,而借助回调函数就可以达到这个目的。我们编写一个函数(回调函数),用于比较两个同类型的值,然后把一个指向这个函数的指针作为参数传递给查找函数,查找函数调用这个比较函数来执行比较,采用这个方法,任何类型的值得都可以进行比较。 我们还必须给查找函数传递一个指向待比较的值的指针而不是值本身,也就是一个void *类型的形参,这个指针会传递给回调函数,进行最终的比较。这样的修改可以让我们传递指向任何类型的指针到查找函数,从而完成对任何类型的比较,这就是指针的好处,我们无法将字符串、数组或者结构体作为参数传递给函数,但是指向它们的指针却可以。 现在,我们的查找函数就可以这样实现: NODE *Search_List(NODE *node, int (*compare)(void const *, void const *) , void const *desired_value); { while (node != NULL) { if (compare((node->value_address), desired_value) == 0) { break; } node = node->next; } return node; }

实验四:函数与指针

陕西理工大学 《高级语言程序设计(C)》 实验报告 院系: 班级: 学号: 姓名:

目录 实验一:C开发环境与顺序结构程序设计 (2) 1.实验目的: (2) 2.实验环境: (2) 3.实验步骤: (2) 4.实验内容: (2) 5.实验总结 (9) 实验二:分支结构与循环结构程序设计 (10) 1.实验目的: (10) 2.实验环境: (10) 3.实验内容: (10) 4.实验总结 (18) 实验三数组及数组的应用 (19) 1.实验目的: (19) 2.实验环境: (19) 3.实验内容: (19) 4.实验总结: (20) 实验四:函数与指针 (20) 1.实验目的: (21) 2.实验内容: (21) 3.实验总结 (24)

实验一:C开发环境与顺序结构程序设计 1.实验目的: (1) 了解集成开发环境VC++6.0的使用方法,理解相关命令的含义,掌握编辑、编译、连接以及运行调试的方法,掌握程序的基本结构,掌握输入输出的方式。 (2) 掌握程序设计的基本要素中的数据类型、变量、运算符以及表达式的运用。 (3) 学会正确使用逻辑运算符和逻辑表达式以及关系运算符与关系表达式,掌握在程序设计中灵活使用顺序结构。 2.实验环境: (1) 硬件环境 CPU:Inter Pentium(R)4 CPU 3.00GHz 以上 内存:2GByte (2) 软件环境 操作系统:Microsoft Windows 7 编译系统:Microsoft Visual C++ 6.0 3.实验步骤: 按如图所示流程进行实验内容的 调试。 (1) 在XP操作系统中,启动 VC++6.0编译环境; (2) 在VC++6.0编译界面输入C源 程序; (3) 选择编译、组建、执行命令进 行编译、链接和运行,并记录实验 数据; (4) 按以上方法依次输入其他源程 序并调试、运行和记录实验数据。 4.实验内容: 4.1 输入并运行下列程序,查看程序运行的结果。 #include

C# 如何跨平台调用C++的函数指针

C# 如何跨平台调用C++的函数指针! 函数指针搞C++的人应该都知道,效率高,易用性强,隐蔽代码等。在C++里面调用C++写的dll的函数指针那是在容易不过了。使用C#就稍微麻烦点了!那怎么掉呢?通过上面的第一篇文章我们知道应该使用委托 delegate。如果再高级点,定义一个函数指针结构(有点像linux的内核),也同样可以用C#调用。 提示:委托就和C++中的函数指针一样 借用一下别人的列子:在C++的一个标准Win32 api 库ccLic.dll中有一个函数void* WINAPI GetFunctionAddress(unsigned int sn);此函数通过传sn序号得到函数指针即一个函数的地址.之后再通过返回回来的地址进行其它函数的调用那么我们必须知道.一个sn号对应的函数结构如 sn=1 -> bool WINAPI CCAskServerLicenseInfo(const char* server_address,unsigned short port,PCcLic_Info plicenseinfo) 在其中 typedef struct _CcLic_Info { char ower[64]; unsigned short manage_ip; unsigned short ramained_ip; unsigned short useable_time; unsigned char type; } CcLic_Info,*PCcLic_Info; 此列的目的就是通过C#调用 CCAskServerLicenseInfo 函数. [DllImport(@"ccLic.dll")] public static extern System.IntPtr Matrix(System.UInt32 sn);//声名入口函数 //定义函数指针模型 public delegate System.Int32 CCAskServerLicenseInfoHandle(Syste m.String servername, System.UInt16 port, System.IntPtr ptr); public static LicenseInfo GetLicentInfo(String server, System.UInt16

C语言学习笔记之函数指针

C/C++学习笔记之函数指针 函数指针的概念,在潭浩强先生的《C语言程序设计》这本经典的教程中提及过,在大多数情况下我们使用不到,也忽略了它的存在。函数名实际上也是一种指针,指向函数的入口地址,但它又不同于普通的如int*、double*指针,看下面的例子来理解函数指针的概念:view plain int function(int x,int y); int main(void) { int(*fun)(int x,int y); int a=10,b=20; function(a,b); fun=function; (*fun)(a,b);…… } 第一行代码首先定义了一个函数function,其输入为两个整型数,返回也为一个整型数(输入参数和返回值可为其它任何数据类型);后面又定义了一个函数指针fun,与int*或double*定义指针不同的是,函数指针的定义必须同时指出输入参数,表明这是一个函数指针,并且*fun也必须用一对括号括起来;并将函数指针赋值为函数function,前提条件是*fun 和function的输入参数和返回值必须保持一致,否则无法通过编译。可以直接调用函数function(),也可以直接调用函数指针,二者是等效的。 回调函数(callback)是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。请看下面的例子: void f();//函数原型 上面的语句声明了一个函数,没有输入参数并返回void.那么函数指针的声明方法如下:void(*)(); 函数存放在内存的代码区域内,它们同样有地址,我们如何能获得函数的地址呢? 如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。 定义一个指向函数的指针用如下的形式,以上面的test()为例: int(*fp)(int a);//这里就定义了一个指向函数的指针 函数指针绝对不能指向不同类型,或者是带不同形参的函数,在定义函数指针的时候我们很容易犯如下的错误。 int*fp(int a);//这里是错误的,因为按照结合性和优先级来看就是先和()结合,然后变成了一个返回整型指针的函数了,而不是函数指针,这一点尤其需要注意! 例如函数原型为: int fun(int*,int); 则函数指针可以声明为:int(*pf)(int*,int);当然从上述例子看不出函数指针的优点,目的主要是想引出函数指针数组的概念。我们从上面例子可以得知,既然函数名可以通过函数指针加以保存,那们也一定能定义一个数组保存若干个函数名,这就是函数指针数组。正确使用函数指针数组的前提条件是,这若干个需要通过函数指针数组保存的函

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

我知道函数指针是指向函数的指针,指针函数还是指一个函数的返回值是一个指针,但下面的几道题还是感觉很迷惑。各位能否讲的详细点呢? (1)float(**def)[10]def是什么? (2)double*(*gh)[10]gh是什么? (3)double(*f[10])()f是什么? (4)int*((*b)[10])b是什么? 这样老感觉有点乱,有什么窍门可以记得并理解的清楚一点么? (1)def是一个指针,指向的对象也是一个指针,指向的指针最终指向的是10个float构成的数组. (2)gh是指针,指向的是10个元素构成的数组,数组的元素是double*类型的指针. (3)f是10个元素构成的数组,每个元素是指针,指针指向的是函数,函数类型为无参数且返回值为double.下面要讲的窍门的例子跟这个很类似. (4)b是指针,指向的是10个元素构成的数组,数组元素为int*类型的指针. 窍门如下: 如果我们碰到复杂的类型声明,该如何解析它?例如: char(*a[3])(int); a到底被声明为什么东东?指针?数组?还是函数? 分析时,从a最接近(按运算符优先级)处开始。我们看到a最接近符号是[]——注意:*比[]的优先级低。a后既然有[],那么a是数组,而且是包含3个元素的数组。 那这个数组的每个元素是什么类型呢?虽然数组a只含有a[0]、a[1]、a[2]三个元素,a[3]实际上已经越界,但在分析数组a的元素的类型时,我们正好需要形式上的元素a[3]。知道了a[3]的类型,就知道了a的元素的类型。a[3]是什么类型?是指针,因为它的前面有*.由此可知,数组a的元素是指针。 光说是指针还不够。对于指针,必须说出它指向的东东是什么类型。它指向的东东是什么,就看*a[3]是什么(a[3]是指针,它指向的东东当然是*a[3])了。继续按优先级观察,我们看到*a[3]后面有小括号,所以可以肯定*a[3]是函数。即数组a的元素是指向函数的指针。 指向的是什么类型的函数?这很明显,是入参为int、返回值为char的类型的函数。 至此解析完毕。

C语言——指向函数的指针

1函数类型(* 函数指针变量)();//指向函数的入口地址 一个函数是若干语句的集合,经编译后存储在函数代码存储区,并占有一片连续的存储空间,对函数指针只能用函数名赋值而无其他运算 1#include 2 3int max(int x ,int y); 4 5int main() 6{ 7int(* p)() ;//定义p是指向函数的指针变量 8int a , b , c ; 9 10p= max ;//将函数max的入口地址赋给指针变量p 11scanf("%d %d",&a ,&b) ; 12c= (* p)(a , b) ;//用指向函数的指针变量p调用函数 13printf("a = %d , b = %d , max = %d", a , b , c); 14 15return0; 16} 17 18int max(int x ,int y) 19{ 20int k ; 21k= (x> y)? x : y ; 22 23return k ; 24} 函数名作为实际参数: 1 #include 2 3int fun1(int a , int b) 4 { 5return a+b ; 6 } 7 8int fun2(int (*q)() , int x , int y) 9 { 10return (*q)(x , y) ; 11 } 12 13int main() 14 { 15int (*p)() , k ; 16 p = fun1 ;

17 k = fun2( p , 8 , 5 ) ; 18 19printf("k = %d \n" , k); //输出 13 20 21return0 ; 22 } 设置一个函数proc ,每次调用它会实现不同的功能,输入 a , b 两个数,第一次调用proc时,找出两者中最大者,第二次找出最小者,第三次调用求两数之差: 1 #include 2 3int max(int *x , int *y); 4int min(int *x , int *y); 5int a_b(int *x , int *y); 6int proc(int *x , int *y , int(*p)()); 7 8int main() 9 { 10int a , b ; 11 12printf("Enter a and b :"); 13scanf("%d %d" , &a , &b); 14 15printf("a = %d \t b = %d \n" , a , b); 16 17printf("max(%d,%d) = " , a , b); 18 proc(&a , &b , max); 19 20printf("min(%d,%d) = " , a , b); 21 proc(&a , &b , min); 22 23printf("%d - %d = " , a , b); 24 proc(&a , &b , a_b); 25 26return0 ; 27 } 28 29int max(int *x , int *y) 30 { 31int k ; 32 33 k = (*x > *y) ? *x : *y ; 34 35return k ; 36 } 37 38int min(int *x , int *y)

指向对象的指针变量

指向对象的指针变量 定义的一般形式: 类名*指针变量名; p-> (*p). 指向对象的成员变量的指针变量: 1.该指针变量可出现在成员函数中,通过获取该成员变量的地址,然后通过(*指针变量名)访问该成员变量 class aa { int a; public: aa() { a=0; } aa(int a) { this->a=a; } void get_a() { int *p; p=&a; cout<<*p<a=a; } void get_a() { int *p; p=&a; cout<<*p<

} }; int main() { aa b(3); b.get_a(); aa *p; p=new aa[2]; (p+1)->get_a(); p[1].get_a(); int *q; q=&b.a; return 0; } 指向成员函数的指针变量: 定义的一般形式: 函数类型名(类名::*指针变量名)(参数); 赋值的一般形式: 指针变量名=对象名.成员函数名 指针变量名=&类名.成员函数名或指针变量名=类名.成员函数名通过指针变量引用对象的成员函数 一般形式:(对象名.*指针变量名)(参数) this指针 #include using namespace std; class aa { int a; public: aa() { a=0; } aa(int a) { this->a=a; } void get_a() { cout<a<

Keil C51中函数指针使用注意事项

Keil C51中函数指针使用注意事项 在我们的代码中大量使用了函数指针。当函数指针用在Keil C51中时,一定要注意编译器自动生成的函数调用树通常是不正确的,需要手动调整。否则可能造成无法预知的后果。 这是因为,Keil C51编译器并不把函数参数和局部变量压入堆栈中,而是放在寄存器或固定的内存位置。 C51的编译器监视函数调用的嵌套顺序,把几个函数的变量放在同样固定的位置。在C51编译器中连接器会搜索所有函数中变量占用存储区间最多的函数,然后以这个函数的变量的占用空间开辟一片空间,其他函数的变量也放在该空间中,同时实现了变量的覆盖(无相互调用)与地址的共享。例如函数A占10个字节,函数B占20个字节,函数C占15个字节,如果它们之间没有相互调用则仅需20个字节就可以满足45个字节的变量需要。 正是由于所有函数的参数和局部变量的共享一个覆盖区,函数没有相互的调用时,在执行一个函数时,会将另一个函数的变量的存储区覆盖。如果函数有调用,那么不会覆盖原来函数的局部变量的区间。 调用树(call tree)是由Keil链接器自动生成的,用于描述函数的调用关系(调用树可通过编译生成的*.M51文件的OVERLAY MAP OF MODULE部分查看,该部分详细的说明了函数的调用关系以及对覆盖存储区的使用情况)。链接器通过分析调用树来确定哪些寄存器或内存位置是可安全覆盖的。这样两个不同时调用的函数就可以共享同一块内存用于传递参数和存储局部变量。但对于函数指针来说,编译器并不知道函数指针将指向哪个函数。这导致了调用树构造出错的可能,函数的参数和局部变量也可能被错误覆盖(例如,函数A通过函数指针调用了函数B,但编译器并不知道它们之间存在调用关系,所以认为它们是可以共享同一块内存的。这样当函数A调用了函数B,回到函数A后,函数A的参数和局部变量可能已经被改变了,再往下运行就出错了)。 对此,Keil提供了链接器OVERLAY伪指令,可让用户自行修改调用树,调整函数的调用关系。 删除调用关系,命令格式: OVERLAY (sfname-caller ~ sfname-callee) OVERLAY (sfname-caller ~ (sfname-callee, sfname-callee)) 举例:OVERLAY(?PR?_FUNC?DMAIN ~ (?PR?_FUNC_A?DMAIN,?PR?_FUNC_B?DMAIN)) 意思是从FUNC函数中删除对FUNC_A和FUNC_B的调用。 添加调用关系,命令格式: OVERLAY (sfname-caller ! sfname-callee) OVERLAY (sfname-caller ! (sfname-callee, sfname-callee)) 举例:OVERLAY(?PR?_MAIN?DMAIN ! (?PR?_FUNC_A?DMAIN,?PR?_FUNC_B?DMAIN)) 意思是添加FUNC函数对FUNC_A和FUNC_B的调用。 可在链接命令行输入命令。或在Keil集成开发环境中,在“BL51 Misc”-“Overlay”中填入()中的内容。

函数与指针编程题

1.编一程序,将字符串computer赋给一个字符数组,然后从第一个字母开始间隔地输出该串。请用指针完成。 答案:#include "stdio.h" main() { static char x[]="computer": char *p; for(p=x;p void main() { char a[100],*p; int n=0; p=a; gets(p); for(;*p!='\0';p++) { n++; } printf("%d",n); } 3.下面findmax函数将计算数组中的最大元素及其下标值和地址值,请编写*findmax()函数。并将程序调试运行 #include *findmax(int *s,int t,int *k) { } main() { int a[10]={12,23,34,45,56,67,78,89,11,22},k,*add; add=findmax(a,10,&k); printf("%d,%d,%o\n",a[k],k,add); } 答案:#include int *findmax(int *s,int t,int *k) { int max; max=s[0]; for(int i=0;i

*k=i; } } return k; } void main() { int a[10]={12,23,34,45,56,67,78,89,11,22},k,*add; add=findmax(a,10,&k); printf("%d,%d,%o\n",a[k],k,add); } 4. 用指针方法处理。有一字符串,包含n(n>2)个字符。写一函数,将此字符串中从第2个字符开始的全部字符复制成为另一个字符串。 5. 用指针方法处理。有一字符串,包含n个字符。写一函数,将此字符串中从第m个字符开始的全部字符复制成为另一个字符串。 #include #include int main() { int m,len; char str1[100],*p,*q; p=str1; q=p; gets(p); len=strlen(p); printf("input m(m int search(int a[],int n,int x) { int i; for(i=0;i<5;i++)

相关文档