# 函数指针 一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。 函数指针的定义形式为: ```c returnType (*pointName)(param list); ``` returnType 为函数返回值类型,pointerName 为指针名称,param list 为函数参数列表。参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称,这一点和函数原型非常类似。 注意`( )`的优先级高于`*`,第一个括号不能省略,如果写作`returnType *pointerName(param list);`就成了函数原型,它表明函数的返回值类型为`returnType *`。 用指针来实现对函数的调用代码如下: ```c #include #include int maxNumber(int a, int b); int main() { int x, y, maxval; //定义函数指针 int (*pmax)(int, int) = maxNumber; //也可以写作int (*pmax)(int a, int b) printf("Input two numbers: "); scanf("%d, %d", &x, &y); maxval = (*pmax)(x, y); printf("Max value: %d\n", maxval); system("pause"); return 0; } int maxNumber(int a, int b) { if (a > b) { return a; } else { return b; } } ``` ## 深入理解指针 ```c int *p1[6]; //指针数组 int *(p2[6]); //指针数组,和上面的形式等价 int (*p3)[6]; //二维数组指针 int (*p4)(int, int); //函数指针 char *(* c[10])(int **p); int (*(*(*pfunc)(int *))[5])(int *); ``` ==C语言标准规定,对于一个符号的定义,编译器总是从它的名字开始读取,然后按照优先级顺序依次解析。对,从名字开始,不是从开头也不是从末尾,这是理解复杂指针的关键!== ### 1) int *p1[6]; 从 p1 开始理解,它的左边是 *,右边是 [ ],[ ] 的优先级高于 *,所以编译器先解析`p1[6]`,p1 首先是一个拥有 6 个元素的数组,然后再解析`int *`,它用来说明数组元素的类型。从整体上讲,p1 是一个拥有 6 个 int * 元素的数组,也即指针数组。 ### 2) int (*p3)[6]; 从 p3 开始理解,( ) 的优先级最高,编译器先解析`(*p3)`,p3 首先是一个指针,剩下的`int [6]`是 p3 指向的数据的类型,它是一个拥有 6 个元素的一维数组。从整体上讲,p3 是一个指向拥有 6 个 int 元素数组的指针,也即二维数组指针。 > 为了能够通过指针来遍历数组元素,在定义数组指针时需要进行降维处理,例如三维数组指针实际指向的数据类型是二维数组,二维数组指针实际指向的数据类型是一维数组,一维数组指针实际指向的是一个基本类型;在表达式中,数组名也会进行同样的转换(下降一维)。 ### 3) int (*p4)(int, int); 从 p4 开始理解,( ) 的优先级最高,编译器先解析`(*p4)`,p4 首先是一个指针,它后边的 ( ) 说明 p4 指向的是一个函数,括号中的`int, int`是参数列表,开头的`int`用来说明函数的返回值类型。整体来看,p4 是一个指向原型为`int func(int, int);`的函数的指针。 ### 4) char *(* c[10])(int **p); 这个定义有两个名字,分别是 c 和 p,乍看起来 p 是指针变量的名字,不过很遗憾这是错误的。如果 p 是指针变量名,`c[10]`这种写法就又定义了一个新的名字,这让人匪夷所思。 以 c 作为变量的名字,先来看括号内部(绿色粗体): char * **(\* c[10])** (int **p); [ ] 的优先级高于 *,编译器先解析`c[10]`,c 首先是一个数组,它前面的`*`表明每个数组元素都是一个指针,只是还不知道指向什么类型的数据。整体上来看,`(* c[10])`说明 c 是一个指针数组,只是指针指向的数据类型尚未确定。 跳出括号,根据优先级规则(() 的优先级高于 *)应该先看右边(红色粗体): char * **(\* c[10]) (int \**p)**; `( )`说明是一个函数,`int **p`是函数参数。 再看左边(橘黄色粗体): **char \*** **(\* c[10]) (int \**p)**; `char *`是函数的返回值类型。 从整体上看,我们可以将定义分成两部分: **char \*** **(\* c[10]) (int \**p)**; 绿色粗体表明 c 是一个指针数组,红色粗体表明指针指向的数据类型,合起来就是:c 是一个拥有 10 个元素的指针数组,每个指针指向一个原型为`char *func(int **p);`的函数。 ### 5) int (*(*(*pfunc)(int *))[5])(int *); 从 pfunc 开始理解,先看括号内部(绿色粗体): int (*(***(\*pfunc)**(int *))[5])(int *); pfunc 是一个指针。 跳出括号,看它的两边(红色粗体): int (*(***(\*pfunc)(int \*)**)[5])(int *); 根据优先级规则应该先看右边的`(int *)`,它表明这是一个函数,`int *`是参数列表。再看左边的`*`,它表明函数的返回值是一个指针,只是指针指向的数据类型尚未确定。 将上面的两部分合成一个整体,如下面的蓝色粗体所示,它表明 pfunc 是一个指向函数的指针,现在函数的参数列表确定了,也知道返回值是一个指针了(只是不知道它指向什么类型的数据)。 int (* **(\*(\*pfunc)(int \*))** [5])(int *); 蓝色粗体以外的部分,就用来说明函数返回什么类型的指针。 我们接着分析,再向外跳一层括号(红色粗体): int (***** **(\*(\*pfunc)(int \*))** **[5]**)(int *); [ ] 的优先级高于 *,先看右边,[5] 表示这是一个数组,再看左边,* 表示数组的每个元素都是指针。也就是说,* [5] 是一个指针数组,函数返回的指针就指向这样一个数组。 那么,指针数组中的指针又指向什么类型的数据呢?再向外跳一层括号(橘黄色粗体): **int** **(\*** **(\*(\*pfunc)(int \*))** **[5])** **(int \*)**; 先看橘黄色部分的右边,它是一个函数,再看左边,它是函数的返回值类型。也就是说,指针数组中的指针指向原型为`int func(int *);`的函数。 将上面的三部分合起来就是:pfunc 是一个函数指针(蓝色部分),该函数的返回值是一个指针,它指向一个指针数组(红色部分),指针数组中的指针指向原型为`int func(int *);`的函数。