函数指针及其的运用(上)——何为函数指针

=========================引子=========================

我们都知道,数组名就是指向数组第一个元素的常量指针(详见《数组拾遗》)。同理,对于一个函数而言,函数名也是指向函数第一条指令的常量指针。而编译器要做的就是在程序编译之后,为每个函数分配一个首地址,即该函数第一条指令的地址。一般情况下,我们可以用一个指针来保存这个地址,而这个指针就是函数指针,该指针可以看作是它指向函数的别名,所以我们可以用该指针来调用这个函数。

=========================函数指针的声明方法=========================

type (*func)(type &,type &)

  该语句声明了一个指针func,它指向了一个函数,这个函数带有了2个type型参数并返回一个type的值。

p.s. type类型可以被看成是int啊或者是floast等C++的类型。

=========================注意事项=========================

  1. 一个指向函数的指针必须确保该函数被定义且分配了内存,否则它将指向一个空地址,这个可是大忌!
  2. 特别注意第一个括号的位置。如果我们不写括号,如下:

type *func(type ,type)

  这就不是一个指向函数的指针了,而是声明了一个函数,该函数返回一个type类型的指针 

=========================函数指针应用示例=========================

我们以这样一个程序为例:该程序的功能是计算三角形的矩形的面积。其中,三角形的长和高,矩形的长和宽由用户自己输入,并且我们提供一个交换函数,用来交换用户输入的长和高(宽)。


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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

//例1 普通函数示例

#include <iostream>

using namespace std;

//函数声明

double triangle_area(double &x,double &y);//三角形面积

double rectangle_area(double &x,double &y);//矩形面积

double swap_value(double &x,double &y);//交换值

double set_value(double &x,double &y);//设定长宽(高)

double print_area(double &x,double &y);//输出面积

//函数定义

double triangle_area(double &x,double &y)

{

    return x*y*0.5;

}

double rectangle_area(double &x,double &y)

{

    return x*y;

}

double swap_value(double &x,double &y)

{

    double temp;

    temp=x;

    x=y;

    y=temp;

    return 0.0;

}

double print_area(double &x,double &y)

{

    cout<<"执行函数后:\n";

    cout<<"x="<<x<<"  y="<<y<<endl;

    //coming soon in e.g.2...

    return 0.0;

}

double set_value(double &x,double &y)

//注意参数一定要定义成引用,要不是传不出去哈!

{

    cout<<"自定义长宽(高)为:\n";

    cout<<"长为:";

    cin>>x;

    cout<<"宽或者高为:";

    cin>>y;

    return 0.0;

}

int main()

{

    bool quit=false;//初始化退出的值为否

    double a=2,b=3;//初始化两个参数a和b

    char choice;

    while(quit==false)

    {

        cout<<"退出(q); 设定长、宽或高(1); 三角形面积(2); 矩形面积(3); 交换长宽或高(4)."<<endl;

        cin>>choice;

        switch(choice)

        {

            case ‘q‘:

                quit=true;

                break;

            case ‘1‘:

                set_value(a,b);

                print_area(a,b);

                break;

            case ‘2‘:

                print_area(a,b);

                cout<<"三角形的面积为:\t"<<triangle_area(a,b)<<endl;

                break;

            case  ‘3‘:

                print_area(a,b);

                cout<<"矩形的面积为:\t"<<rectangle_area(a,b)<<endl;

                break;

            case ‘4‘:

                swap_value(a,b);

                print_area(a,b);

                break;

            default:

                cout<<"请按规矩出牌!"<<endl;

        }

    }

    return 0;

}

  在这个例子中,我们采用普通函数大方法,来输出三角形和矩形的值,输出如下:

下面,我们来看看如果采用函数指针,效果会是怎样?由于我们在前面分析过了,函数指针就是一个指向函数的指针。那么我们在调用函数的时候,就可以运用指针来调用这个函数。而且,周所周知,指针可以作为一个函数的参数,那么函数指针也不应该例外。这样就好了,我们可以讲函数的指针作为函数参数来调用。对于一个普通要调用指针的函数而言,声明应该是这个样子的:

type func(type*, type , type)

  那么由上面这句话就可以看出来,这个函数func的第一个参数就是一个指向type类型的指针,而后面两个参数就是两个类型为type的形式参数。结合本文一开始所列的函数参数的声明格式,那么函数指针作为函数参数的一般形式就是:

type func(type(*p)(type &, type &),type &,type &);

  该函数func有3个参数,第一个参数为type(*p)(type &, type &),这就是一个函数指针,他指向一个带有两个type类型的参数并且返回type值的函数,另外两个参数都是type类型的引用。

这样一来,我们就可以利用函数指针把函数作为另外一个函数的参数调入到那个函数中使用了。对于上面这个例子,我故意把所有的函数都生声明为带有两个double类型的变量,且返回值均为double。这样做的原因只是为了让后面我们在利用函数指针调用的时候方便一些。因为我们只需要将函数指针声明成与之想匹配的函数就行了。从上面这个例子看出,它麻烦就麻烦在每次输出的时候都需要在case语句中进行操作,那么我们能不能利用函数指针将其一并简化到print函数中呢,请看下例:


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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

//例2 函数指针示例

#include <iostream>

using namespace std;

//函数声明

double triangle_area(double &x,double &y);//三角形面积

double rectangle_area(double &x,double &y);//矩形面积

double swap_value(double &x,double &y);//交换值

double set_value(double &x,double &y);//设定长宽(高)

// double print_area(double &x,double &y);//输出面积

double print_area(double(*p)(double&,double&), double &x,double &y);//利用函数指针输出面积

//函数定义

double triangle_area(double &x,double &y)

{

    cout<<"三角形的面积为:\t"<<x*y*0.5<<endl;

    return 0.0;

}

double rectangle_area(double &x,double &y)

{

    cout<<"矩形的面积为:\t"<<x*y<<endl;

    return 0.0;

}

double swap_value(double &x,double &y)

{

    double temp;

    temp=x;

    x=y;

    y=temp;

    return 0.0;

}

double print_area(double(*p)(double &x,double &y), double &x,double &y)

{

    cout<<"执行函数前:\n";

    cout<<"x="<<x<<"  y="<<y<<endl;

    //it is coming!...

    p(x,y);

    cout<<"函数指针传值后:\n";

    cout<<"x="<<x<<"  y="<<y<<endl;

    return 0.0;

}

double set_value(double &x,double &y)

//注意参数一定要定义成引用,要不是传不出去哈!

{

    cout<<"自定义长宽(高)为:\n";

    cout<<"长为:";

    cin>>x;

    cout<<"宽或者高为:";

    cin>>y;

    return 0.0;

}

int main()

{

    bool quit=false;//初始化退出的值为否

    double a=2,b=3;//初始化两个参数a和b

    char choice;

    //声明的p为一个函数指针,它所指向的函数带有梁个double类型的参数并且返回double

    double (*p)(double &,double &);

    while(quit==false)

    {

        cout<<"退出(q); 设定长、宽或高(1); 三角形面积(2); 矩形面积(3); 交换长宽或高(4)."<<endl;

        cin>>choice;

        switch(choice)

        {

        case ‘q‘:

            quit=true;

            break;

        case ‘1‘:

            p=set_value;

            print_area(p,a,b);

            break;

        case ‘2‘:

            p=triangle_area;

            print_area(p,a,b);         

            break;

        case  ‘3‘:

            p=rectangle_area;

            print_area(p,a,b);

            break;

        case ‘4‘:

            p=swap_value;

            print_area(p,a,b);

            break;

        default:

            cout<<"请按规矩出牌!"<<endl;

        }

    }

    return 0;

}

在例2中,我们采用了函数指针的方式,可以看到,在case语句中只需要制定每个函数指针所指向的函数是什么,那么在print函数中,我们就可以调用这个函数了。输出如下所示:

在该程序的第61行,我们就声明了一个函数指针。可以看到,在程序的case语句中,我们只需要把这个函数指针传递到print_area()函数里面就可以了。这样十分方便。而且我们可以看到,其实只要在print_area()中,即程序的第38行加上对这个函数指针的调用,我们就可以利用指针所指向的函数了。这十分方便。但是有几个小知识点应该注意一下:

  1. 声明函数指针时,其返回值,参数个数,参数类型应该与需要它指向的函数保持一致;否则,编译器会报错,无法从“***”转换到“***”;
  2. 利用函数指针只想某个函数的时候,我们只用,也只能给出该函数的函数名,不能把参数一并给出了。比如说在上例中,如果我们把程序的第84行改成:

    p=swap_value(a,b);

     那么编译器会报错:
    func_pointer.cpp(84) : error C2440: “=”: 无法从“double”转换为“double (__cdecl *)(double &,double &)
    这个错误的原因就是因为我们忘记了在文章一开头所讲的函数指针的一句话:函数名也是指向函数第一条指令的常量指针。因为函数指针就是指向其函数的地址的,那么我们就应该利用函数指针来指向函数名就可以了。

=========================补充一点哈 ^_^=========================

如果你认为上面所诉的函数指针的声明格式有点罗嗦,那么我们也可以利用typedef来简化声明和定义的操作。比如说在上例2的第61行,那么长一串。我们完全可以在在程序一开始利用typedef来代替:

typedef double (*vp)(double &,double &);

  这样一来,我们就可以把程序的第61行简化成:

vp p;

  而且,我们在声明和定义print_area()函数的时候,就可以程序的第10行和第33行换成:

//函数声明

double print_area(vp,double &x,double &y);

//函数定义

double print_area(vp p, double &x,double &y)

  好了,关于函数指针就总结到这里,更多内容请关注“唯一的天空”其他内容,谢谢 ^_^。

转自:http://www.cnblogs.com/uniqueliu/archive/2011/07/27/2118619.html

时间: 2024-10-25 08:16:51

函数指针及其的运用(上)——何为函数指针的相关文章

C++成员函数指针错误用法警示(成员函数指针与高性能的C++委托,三篇),附好多评论

今天做一个成绩管理系统的并发引擎,用Qt做的,仿照QtConcurrent搞了个模板基类.这里为了隐藏细节,隔离变化,把并发的东西全部包含在模板基类中.子类只需注册需要并发执行的入口函数即可在单独线程中执行.最终目标是,继承的业务逻辑类外部调用时有两个接口可选,调用syncRun同步执行:调用由引擎自动生成的asyncRun就异步执行.最终自动生成asyncRun的模板基类没能实现,主要原因是mingw对this处理的太有问题了!!原本以为编译器问题,后来才知道成员函数指针和this指针如此特殊

【转载】GetAdaptersInfo函数在64位系统上返回ERROR_NOACCESS的有关问题

From:http://www.educity.cn/wenda/351190.html GetAdaptersInfo函数在64位系统下返回ERROR_NOACCESS的问题 实际应用中一个程序在长时间运行后内存占用较高时发生崩溃,从dump信息中,发现GetAdaptersInfo函数返回了一个奇怪的错误码998(ERROR_NOACCESS),百度搜索不到相关的信息.MSDN上GetAdaptersInfo函数的错误码正常情况下只有5种.并且一共发生的两次崩溃都出现在一台Win7 64位机

c++,虚函数的作用,承接上一篇随笔

和前面的例子相比,本例仅仅是在 display() 函数声明前加了一个virtual关键字,将成员函数声明为了虚函数(Virtual Function),这样就可以通过 p 指针调用 Teacher 类的成员函数了,运行结果也证明了这一点(赵宏佳已经是一名老师了,不再是无业游民了). 有了虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员.换句话说,基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种

【03】指针、返回值、成员函数的const

1 // 2 // 2015-03-05 03/55 3 // 指针.返回值.成员函数 之 const 4 // 5 6 #include <iostream> 7 #include <string> 8 9 using namespace std; 10 11 // ------------------------------ 12 // 1. 指针的const 13 // ------------------------------ 14 const char *p1 = &q

VC和gcc在保证函数static变量线程安全性上的区别

VC和gcc不同,不能保证静态变量的线程安全性.这就给我们的程序带来了很大的安全隐患和诸多不便.这一点应该引起我们的重视!尤其是在构造函数耗时比较长的时候,很可能给程序带来意想不到的结果.本文从测试代码开始,逐步分析原理,最后给出解决方案. 多线程状态下,VC不能保证在使用函数的静态变量的时候,它的构造函数已经被执行完毕,下面是一段测试代码: class TestStatic { public: TestStatic() { Sleep(1000*10); m_num = 999; } publ

基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------&gt; 可以返回派生类对象的引用或指针

您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. 百度和网页 http://bbs.csdn.net/topics/380238133 的作者无关,不对其内容负责.百度快照谨为网络故障时之索引,不代表被搜索网站的即时页面. 首页 精选版块 移动开发 iOS Android Qt WP 云计算 IaaS Pass/SaaS 分布式计算/Hadoop J

PHP单文件、多个单文件、多文件上传函数的封装

//表单: //s.php //要在选择上传文件时能一次选择多个文件,那么就加multiple="multiple" ,还有注意下name="myFile1"和name="myFile[]"的区别,单文件.多文件上传. <!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title

无法把指针变量本身传递给一个函数

例1: #include<stdio.h> #include<stdlib.h> void fun(char*p) { char c = p[3];//或者是char c=*(p+3); } int main() { char*p2 = "abcdefg"; fun(p2); system("pause"); return 0; } 错误,因为无法把指针变量本身传递给一个函数 应该对实参做一份拷贝并传递给被调用函数,即对p2做一份拷贝,假设其

PHP单文件上传原理及上传函数的封装

<?php //单文件上传函数的封装 //文件上传原理:将客户端的文件上传到服务器端,再将服务器端的临时文件移动到指定目录即可. //文件的方向:客户端-->服务器(临时文件)-->指定目录,当文件进入服务器时它就是临时文件了,这时操作中要用临时文件的名称tmp_name. //在客户端设置上传文件的限制(文件类型和大小)是不安全的,因为客户能通过源代码修改限制,所以在服务端这里设置限制. //设置编码为UTF-8,以避免中文乱码 header('Content-Type:text/ht