• C/C++知识分享: 函数指针与指针函数,看完这篇你还能不懂?


    一、 什么是指针?

    定义:指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量;


     

    上面一个 4GB 的内存可以存放 2^32 字节的数据。左侧连续的十六进制编号就是内存地址,每个内存地址对应一个字节的内存空间。而指针变量保存的就是这个编号,也即内存地址。

    指针的声明:

    指针其实就是一个变量,指针的声明方式与一般的变量声明类似,如下:

    int *p; // 声明一个 int 类型的指针 p,该指针指向一个int类型的对象

    char *p        // 声明一个 char 类型的指针 p,该指针指向一个int类型的对象

    int *arr[10]    // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指向 int 类型对象的指针

    int (*arr)[10]  // 声明一个数组指针,该指针指向一个 int 类型的一维数组

    int **p;        // 声明一个指针 p ,该指针指向一个 int 类型的指针

    声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问之前,指针必须进行初始化:或是使他指向现有的内存,或者给他动态分配内存,否则我们并不知道指针指向哪儿,这个问题需要特别关注。


     

    二、什么是函数指针?

    函数指针定义:函数指针是指向函数的指针变量。因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。

    其通用表达式为:类型说明符 (*函数名) (参数)

    int (*fun)(int x) //函数指针的定义

    int (*fun)(int x,int y) //函数指针的定义

    函数指针在PC软件开发中使用较少,在嵌入式行业使用较多,但是无论是PC软件还是嵌入式软件,理解函数指针的定义和使用,对于理解程序设计都是很有好处的。

    函数指针的赋值

    函数指针和其他指针一样定义之后使用之前也是需要初始化。

    函数指针有两个用途:调用函数和做函数的参数

    int (*fun)(int x,int y) //函数指针的定义

    fun = &Function          //函数指针的赋值方式1

    fun = Function          //函数指针的赋值方式2

    x = (*fun)()            //函数指针的调用方式1

    x = fun()                //函数指针的调用方式2

    函数赋值的时候取地址运算符&不是必需的,因为一个函数标识符就表示了它的地址,并且赋值的时候函数不需要带圆括号;

    如果是函数调用,还必须包含一个圆括号括起来的参数表。

    函数指针的用法

    我们使用指针的时候,需要通过钥匙 * 来取其指向的内存里面的值,函数指针使用也如此。通过用(*pf)取出存在这个地址上的函数,然后调用它。

    char* fun(char* p1,char* p2)

    {

      int i = 0;

      i = strcmp(p1,p2);

      if(0 == i)

      {

        return p1;

      }

      else

      {

        return p2;

      }

    }

    int main()

    {

      char * (*pf)(char* p1,char* p2);

      pf = &fun;

      (*pf)("aa","bb");

      return 0;

    }

    这里需要注意到是,在Visual C++6.0里,给函数指针赋值时,可以用&fun或直接用函数名fun。这是因为函数名被编译之后其实就是一个地址,所以这里两种用法没有本质的差别。


     

    用法延申

    当我们不满足于函数指针上面如此简单的用法时,这时候需要一个高级用法来扩展我们对于函数指针的认知边界。

    感兴趣的同学可以看看下面这个用法,并尝试理解该表达式是如何使用的函数指针。

    (* (void(*)()) 0)(); //出自《C Trap and Pitfalls》这本经典的书

    答案如下:   ``

    第一步:通过void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。

    第二步:通过(void(*) ())0,可以明白这是将0强制转换为函数指针类型,0是一个地址,也就是说一个函数存在首地址为0的一段区域内。

    第三步:通过(*(void(*) ())0),可以明白这是取0地址开始的一段内存里面的内容。

    第四步:最终理解(*(void(*) ())0)(),这是函数调用。

    让程序跳转到绝对地址为0x0113F90C

    方法一:

    将0x0113F90C地址强制转换为函数指针类型,即: (void (*)())0x0113F90C

    然后调用:((void (*)())0x0113F90C)()

    方法二:

    typedef (void (*)()) VoidFuncPtr;

    ((VoidFuncPtr)0x0113F90C)();


     


    面试题:指出程序的错误

    #include<stdio.h>

    void main(void)

    {

      int max(x,y);

      int *p=max;

      int a,b,c,d;

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

      d=p(p(a,b),c);

      printf("d:%d ",d);

      return;

    }

    int max(int a,int y)

    {

      return(x > y ?x:y);

    }

    答案:

    int max(x ,y);函数声明错误,改为:int max(int x,int y);

    解析:max函数声明只是写出了函数的形参的名称,这对参数的类型来说是毫无意义的,编译器会把x和y当做数据类型来看,编译时会出错,max的调用肯定也会出错。

    int *p=max;指针定义错误,改为:int (*p)(int ,int)=max;

    解析:函数的指针是不能直接赋值给int型指针.

    scanf("%d %d %d",a,b,c);库函数使用错误,改为scanf("%d %d%d",&a,&b,&c);

    解析:库函数使用错误,第二部分应该是接收数据的地址,这里却写成了变量。

    d=p(p(a,b),c);函数指针调用函数错误,改为d=(*p)((*p)(a,b),c);`

    解析:用函数指针调用函数的格式如下:(【*】【函数指针名称】)(【参数列表】);不能直接用函数指针加上参数就直接调用。


     

    三、什么是指针函数?

    指针函数定义:指针函数的落脚点是一个函数,这个函数的返回值是一个指针,与普通函数int function(int,int)类似,只是返回的数据类型不一样而已。

    _type_ *function(int, int) //返回的是指针地址

    int function(int,int)    //返回的是int型数据。

    int * fun(int x,int y)  //指针函数的定义

    int* fun(int x,int y)  //指针函数的定义

    以上三种写法均正确,但是*靠近返回值一点更容易理解。int *fun(int x) //指针函数的定义

    指针函数的调用

    在调用指针函数时,需要一个同类型的指针来接收其函数的返回值。

    typedef struct _Data{

        int a;

        int b;

    }Data;

    //指针函数

    Data* f(int a,int b){

        Data * data = new Data;

        data->a = a;

        data->b = b;

        return data;

    }

    int main(int argc, char *argv[])

    {

        QApplication a(argc, argv);

        //调用指针函数

        Data * myData = f(4,5);

        qDebug() << "f(4,5) = " << myData->a << myData->b;

        return a.exec();

    }

    不过也可以将其返回值定义为 void* 类型,在调用的时候强制转换返回值为自己想要的类型。

    其输出结果是一样的,不过不建议这么使用,因为强制转换可能会带来风险。返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。

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

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


     

    四、函数指针与指针函数区别

    通过以上的介绍,小伙伴应该都能理解二者的定义。那么简单的总结下二者的区别:

    1. 定义不同

    指针函数本质是一个函数,其返回值为指针。

    函数指针本质是一个指针,其指向一个函数。

    2. 写法不同

    指针函数:int* fun(int x,int y);

    函数指针:int (*fun)(int x,int y);

    可以简单粗暴的理解为,指针函数的*是属于数据类型的,而函数指针的星号是属于函数名的。

    再简单一点,可以这样辨别两者:函数名带括号的就是函数指针,否则就是指针函数。

    3. 用法不同

    上面函数指针和指针函数的用法都有,但是函数指针的用法会更多,相对而言难度也更大,例如函数指针与回调函数,如果是C++非静态成员函数指针,其用法也会有一些区别,感兴趣的同学可以关注后续推文或自行查阅相关书籍。

    总而言之,这两个东西很容易搞混淆,一定要深入理解其两者定义和区别,避免犯错。

    看到这里,你是不是对“C/C++”又有了一点新的认知呢~如果你喜欢这篇文章的话,动动小指,加个关注哦~


     

    最后,如果你也想成为程序员,想要快速掌握编程,这里为你分享一个学习企鹅圈子!

    里面有资深专业软件开发工程师,在线解答你的所有疑惑~编程语言入门“so easy”

    资料包含:编程入门、游戏编程、课程设计、黑客等。

    编程学习书籍:


     

    编程学习视频:


     
  • 相关阅读:
    Python学习系列(七)( 数据库编程)
    Python学习系列(六)(模块)
    web.xml的常见配置
    [springMvc]常见配置
    常用JDBC数据库驱动包和类名
    log4j配置项
    BASE64Encoder cannot be resolved to a type类似问题的解决办法
    IDEA激活码
    eclipse快捷键
    ant 打包脚本
  • 原文地址:https://www.cnblogs.com/mu-ge/p/13944779.html
Copyright © 2020-2023  润新知