函数指针与指针函数的学习总结

函数指针是指向函数的指针,指针函数是指一个函数的返回值是一个指针,但下面的几道题还是感觉很迷惑。各位能否讲的详细点呢?

(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的类型的函数。
至此解析完毕。
按上述方法,再复杂的也可以一步步解析出来。

就像习武不是为了打人而是为了防身一样,我们了解上述方法是为了看懂别人写的复杂声明,而不是为了在实践中自己去构造这种复杂的东东。实在需要复杂声明时,可以用typedef替代一部分。例如上面语句可改成两句:
typedef char (*FUN_PTR)(int);
FUN_PTR a[3];
这样就清晰多了。
此外,上面的分析方法还让我们对某些东西的本质更加清楚。比如,n维数组的本质都是一维数组。看个具体的例子:
int a[3][5];
这句声明的是一个包含3个元素的一维数组,其每个元素又是一个由5个int数构成的数组。我们不能理解为:a是一个包含5个元素的一维数组,其每个元素又是一个由3个int数构成的数组。为什么?还是按上面的方法分析,这里从略。

有的书上或网上提供"向右看,向左看"的方法, 其实缺乏通用性, 比如它不适用于对多维数组本质的分析. 而且这种方法掩盖了本质. 本质应该是按上面所讲的,根据运算符优先级逐层剥开.

==============================================================================
 
一、指针函数
当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中。
格式:
类型说明符 * 函数名(参数)
当然了,由于返回的是一个地址,所以类型说明符一般都是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));
        }

int * GetDate(int wk,int dy)
        {
            static int calendar[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}
            };
            return &calendar[wk-1][dy-1];
        }

程序应该是很好理解的,子函数返回的是数组某元素的地址。输出的是这个地址里的值。

二、函数指针
指向函数的指针包含了函数的地址,可以通过它来调用函数。声明格式如下:
类型说明符 (*函数名)(参数)
其实这里不能称为函数名,应该叫做指针的变量名。这个特殊的指针指向一个返回整型值的函数。指针的声明必须和它指向函数的声明保持一致。
指针名和指针运算符外面的括号改变了默认的运算符优先级。如果没有圆括号,就变成了一个返回整型指针的函数的原型声明。
例如:
    void (*fptr)();
把函数的地址赋值给函数指针,可以采用下面两种形式:
        fptr=&Function;
        fptr=Function;
取地址运算符&不是必需的,因为单单一个函数标识符就标号表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。
可以采用如下两种方式来通过指针调用函数:
        x=(*fptr)();
        x=fptr();
第二种格式看上去和函数调用无异。但是有些程序员倾向于使用第一种格式,因为它明确指出是通过指针而非函数名来调用函数的。下面举一个例子:


代码如下:

void (*funcp)();
        void FileFunc(),EditFunc();
        main()
        {
            funcp=FileFunc;
            (*funcp)();
            funcp=EditFunc;
            (*funcp)();
        }

void FileFunc()
        {
            printf("FileFunc/n");
        }

void EditFunc()
        {
            printf("EditFunc/n");
        }

程序输出为:
    FileFunc
    EditFunc

三、指针的指针
指针的指针看上去有些令人费解。它们的声明有两个星号。例如:
        char ** cp;
如果有三个星号,那就是指针的指针的指针,四个星号就是指针的指针的指针的指针,依次类推。
当你熟悉了简单的例子以后,就可以应付复杂的情况了。当然,实际程序中,一般也只用到二级指针,三个星号不常见,更别说四个星号了。
指针的指针需要用到指针的地址。
        char c='A';
        char *p=&c;
        char **cp=&p;
通过指针的指针,不仅可以访问它指向的指针,还可以访问它指向的指针所指向的数据。下面就是几个这样的例子:
        char *p1=*cp;   // (&c)
        char c1=**cp;
你可能想知道这样的结构有什么用?利用指针的指针可以允许被调用函数修改局部指针变量和处理指针数组。


代码如下:

void FindCredit(int **);
        main()
        {
            int vals[]={7,6,5,-4,3,2,1,0};
            int *fp=vals;
            FindCredit(&fp);
            printf(%d/n,*fp);
        }

void FindCredit(int ** fpp)
        {
            while(**fpp!=0)
            if(**fpp<0) break;
            else (*fpp)++;
        }

首先用一个数组的地址初始化指针fp,然后把该指针的地址作为实参传递给函数FindCredit()。FindCredit()函数通过表达式**fpp间接地得到数组中的数据。

为遍历数组以找到一个负值,FindCredit()函数进行自增运算的对象是调用者的指向数组的指针,而不是它自己的指向调用者指针的指针。语句(*fpp)++就是对形参指针指向的指针进行自增运算的。但是因为*运算符高于++运算符,所以圆括号在这里是必须的,如果没有圆括号,那么++运算符将作用于二重指针fpp上。

四、指向指针数组的指针
指针的指针另一用法旧处理指针数组。有些程序员喜欢用指针数组来代替多维数组,一个常见的用法就是处理字符串。


代码如下:

char *Names[]=
        {
             Bill,
             Sam,
             Jim,
             Paul,
             Charles,
             0
        };

main()
        {
            char **nm=Names;
            while(*nm!=0) printf(%s/n,*nm++);
        }

先用字符型指针数组Names的地址来初始化指针nm。每次printf()的调用都首先传递指针nm指向的字符型指针,然后对nm进行自增运算使其指向数组的下一个元素(还是指针)。注意完成上述认为的语法为*nm++,它首先取得指针指向的内容,然后使指针自增。

注意数组中的最后一个元素被初始化为0,while循环以次来判断是否到了数组末尾。具有零值的指针常常被用做循环数组的终止符。程序员称零值指针为空指针(NULL)。采用空指针作为终止符,在树种增删元素时,就不必改动遍历数组的代码,因为此时数组仍然以空指针作为结束。

(0)

相关推荐

  • 详解C语言结构体中的函数指针

    结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展.那么,我们在C语言的结构体中,只能通过定义函数指针的方式,用函数指针指向相应函数,以此达到调用函数的目的. 函数指针 函数类型 (*指针变量名)(形参列表):第一个括号一定不能少. "函数类型"说明函数的返回类型,由于"()"的优先级高于"*",所以指针变量名外的括号必不可少.  注意指针函数与函数指针表示

  • C#委托所蕴含的函数指针概念详细解析

    原则: 1.函数指针,实际上是函数编码后的指令在内存中的首地址,在C++/C中,这个地址可以用函数名直接使用 一个函数调用另一个函数的时候,就可以把被调用函数以函数指针的形式作为参数传入 2.回调函数callback使用的技术就是函数指针: 回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用.为此,你需要做三件事: 1). 声明: 2). 定义: 3). 设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于DLL调用. 回调函数是应用程序提供给Windo

  • c++回调之利用函数指针示例

    c++回调之利用函数指针示例 复制代码 代码如下: #include <iostream>using namespace std; /************************************************************************//*                下层实现: CALLBACK                                        *//**********************************

  • C++编程中的函数指针初步解析

    1.定义 每一个函数都占用一段内存单元,它们有一个起始地址,指向函数入口地址的指针称为函数指针. 2.语法 指向函数的指针变量的一般定义形式为: 数据类型 (*指针变量名)(参数表); 3.说明 1) 函数指针的定义形式中的数据类型是指函数的返回值的类型. 2) 区分下面两个语句: int (*p)(int a, int b); //p是一个指向函数的指针变量,所指函数的返回值类型为整型 int *p(int a, int b); //p是函数名,此函数的返回值类型为整型指针 3) 指向函数的指

  • c++传递函数指针和bind的示例

    复制代码 代码如下: #include <algorithm>class TestClass{public: int Sub(int x, int y) {  return y - x; } void InitAndTest() {  PrintWithClassMemberFunction(&TestClass::Sub);  PrintWithClassPointer(this); } // call: PrintWithClassMemberFunction(&TestC

  • 函数指针的一些概念详解

    函数指针 最近看android camera 的source ,发现大量的call back ,多线程,有必要对其中的基础 :函数指针复习一下,觉得函数指针主要还是用在call back 函数,以及多线程多进程编程中.函数在被编译器编译后就是一段二进制码,而这段二进制码有一个入口地址,而这个入口地址就是函数指针的值了. 首先看函数指针的语法,举一个最简单的例子,要创建一个函数指针,则它与它指向的函数,在参数个数类型以及返回值上都保持一致,跟重载的要求应该是一样的. Int a(int a ) {

  • 深入学习C语言中的函数指针和左右法则

    通常的函数调用     一个通常的函数调用的例子: //自行包含头文件 void MyFun(int x); //此处的申明也可写成:void MyFun( int ); int main(int argc, char* argv[]) { MyFun(10); //这里是调用MyFun(10);函数 return 0; } void MyFun(int x) //这里定义一个MyFun函数 { printf("%d\n",x); } 这个MyFun函数是一个无返回值的函数,它并不完成

  • C++普通函数指针与成员函数指针实例解析

    C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般函数的函数指针使用的比较多,而对指向类成员函数的函数指针则比较陌生.本文即对C++普通函数指针与成员函数指针进行实例解析. 一.普通函数指针 通常我们所说的函数指针指的是指向一般普通函数的指针.和其他指针一样,函数指针指向某种特定类型,所有被同一指针运用的函数必须具有相同的形参类型和返回类型. int (*pf)(int, int); // 声明函数指针 这里,pf指向的函数类型是int (

  • 详解C++中的指针、数组指针与函数指针

    C++中一个重要的特性就是指针,指针不仅具有获得地址的能力,还具有操作地址的能力.指针可以用于数组.或作为函数的参数,用来访问内存和对内存的操作,指针的使用使得C++很高效,但是指针也非常危险,使用不当会带来比较严重的问题. 1.指针 程序中所有的变量和常量都存在一个内存地址中,当然,函数也有对应的内存地址,内存地址的不同会导致程序执行时有所不同. 指针就是用来控制和存储内存地址的变量,它指向单个对象的地址,除了void之外,指针的数据类型与所指向地址的变量数据类型保持一致. 2.如何定义指针.

  • c++函数指针使用示例分享

    需求假设要设计一个名为estimate()的函数,估算编写指定行数的代码所需的时间,并且希望不同的程序员都可以使用该函数. 对于所有的用户来说,estimate()中一部分代码都是相同的,但该函数允许每个程序员提供自己的算法来估算时间. 为实现目标,采用的机制是,将程序员要使用的算法函数的地址传递给estimate(). 实现代码如下 复制代码 代码如下: // funpointer.cpp : 定义控制台应用程序的入口点.//#include "stdafx.h"#include &

随机推荐