分类: C/C++
2013-06-04 16:27:15
原文地址:函数指针和函数指针 作者:jerry20000
姑且这么叫吧,但《C和指针》这本书上把这些知识点叫做“高级声明”。
在看例子之前,需要强调,函数指针本质上是一个指针(函数的指针),它指向的是一个函数。而指针函数是一个函数(指针的函数),函数的返回值是一个指针。
1. int abc(int a)
2. int abc[3]
3. int **abc(int a)
4. int (*abc)(int a)
5. int (*abc)[6]
6. int *abc(int a)
7. int **(*abc[6])(int a)
8. int **abc[6]
9. int *(*abc)[6]
10. int *(*abc(int a))(int a)
11. int (*(*abc)(int a))(int a)
12. int (**(*abc)(int a))(int a)
13. int (*(*abc)(int a))[6]
14. int *(*(*(*abc)(int a))[6])(int a)
解答
1. 返回值为int的函数。
2. int 型数组
3. 返回值为“int 型指针的指针”的函数。()的优先级高于*(这里指右边的*),所以abc先和()结合,即abc(int a),说明abc是一个函数(这是abc的真面目)。那么函数的返回值是什么类型的呢?abc(int a)接着和*(右边的*)结合,即*abc(int a),说明函数abc的返回值是指针类型的?那是什么类型的指针呢?从int *(*abc(int a))可以看出,abc(int a)的返回值是指向int型指针的指针。
4. 指向函数返回值为int的函数指针。由(*abc)可以看出,*先与abc结合,所以abc是一个指针(而不是函数),该指针指向形参为int a,返回值为int的函数。
5. 指向“int 型数组”的指针。从(*abc)可以看出,abc是一个指针,那是什么类型的指针呢?(*abc)和后面的[]结合,即(*abc)[6],所以abc指向一个数组,那么数组元素的类型呢?从int (*abc)[6]可以看出,数组元素的类型为int型。再分析一遍,指针abc指向一个数组,数组的元素类型为int型,即abc是一个指向int型数组的指针。
6. 返回值为“int型指针”的函数。由于()的优先级高于*,所以abc首先和()结合,即abc(int a),由此看出abc是一个函数(而不是指针),那么该函数的返回值是什么类型的呢?abc(int a)和前面的*结合,即*abc(int a),可以看出,函数abc的返回值是指针类型的。接着,*abc(int a)和int结合,即int *abc(int a)。再分析一遍,abc是一个函数,返回值为int型指针。
7. 元素为指向“返回值为int型指针的指针的函数”的指针数组。由于[]的优先级高于*,所以abc先和[]结合,即abc[6],所以abc是一个数组。然后abc[6]和*结合,即*abc[6],说明数组abc的每个元素是指针,且该指针指向形参为int a,返回值为int指针的指针的函数。
8. int型指针的指针数组。[]的优先级高于*,所以abc首先和[]结合,即abc[],所以abc是一个数组,然后abc[6]和*(右边的*)结合,即*abc[6],可以看出数组abc的每个元素为指针。那么该指针指向什么呢?接着,*abc[6]和左边的*结合(*的结合顺序为从右到左),即*(*abc[6]),所以数组abc的每个元素为指针,且该指针指向int型的指针,也就是说,数组abc的每个元素为int型的指针的指针。
9. 指向“int型指针数组”的指针。由于()的优先级高于[],所以abc和*先结合,即*abc,所以abc是一个指针(这是本质)。而下标[]的优先级高于*,所以*abc然后和[]结合,即(*abc)[6],可以看出,abc指向的是一个数组,数组的元素类型为int *,即int型指针。
10. 返回值为“指向返回值为int型指针的函数指针”的函数。是不是有些头晕呢?没关系,不管你头晕不晕,我都会让你清醒的。由于()的优先级高于*,所以abc先和()结合,即abc(int a),说明abc是个函数,这是本质。然后abc(int a)和前面的*结合,即*abc(int a),说明函数的返回值为指针,那么该指针(ptr1)指向什么呢?int *(*abc(int a))(int a),看到红色的部分了吗?可以看出,ptr1指向一个函数,该函数形参为int a,返回值为int型指针。最后我们再分析一遍,abc(int a)函数的返回值是一个指针,该指针指向形参为int a,返回值为int型指针的函数。
11. 指向返回值为“指向返回值为int的函数指针”的函数指针。从(*abc)(int a)可以看出,abc是一个函数指针,那么该函数指针所指的函数(f)返回值是什么类型的呢?从*(*abc)(int a)可以看出,函数f的返回值为指针类型(ptr_type),那ptr_type到底是什么类型的呢(指向int,char,还是函数)?从int (*(*abc)(int a))(int a)可以看出,ptr_type是函数指针,所指向的函数形参为(int a),返回值为int型。
12. 指向函数返回值为“返回值为int的函数指针的指针”的函数指针。分析参考11。
13. 指向函数返回值为“指向int型数组的指针”的函数指针。abc先和*结合,即*abc,abc是一个指针。接着,*abc和()结合,即(*abc)(int a),abc指向的是一个函数,即abc是一个函数指针。那么abc指向的函数返回值是什么类型呢?(*abc)(int a)接着和前面的*结合,即*(*abc)(int a),说明abc所指向的函数的返回值是一个指针,那么这个指针(ptr)指向哪儿呢?接着往下看,(*(*abc)(int a))[6],ptr指向一个数组,数组的元素类型为int型。再分析一遍,函数指针abc所指的函数返回值是一个指针,该指针指向int型的数组。
14. 指向函数返回值为“指向‘返回值为int型指针的函数指针’的数组的指针”的函数指针。看到这个表达式,你是不是头有点大了?如果你的answer是yes,那么恭喜你,说明你还是个正常的地球人。哈哈!开个玩笑。从(*abc)(int a)可以看出,abc是一个函数指针,即指针abc指向的是一个函数(f)。那么函数f的返回值是什么类型的呢?从*(*abc)(int a)可以看出,函数f的返回值类型是指针型(ptr)的,那么这个指针指向哪里呢?从(*(*abc)(int a))[6]可以看出,ptr指向一个数组,那么该数组(arr)的元素是什么类型的呢?从*(*(*abc)(int a))[6],arr的数组的元素类型为指针类型(ptr_type),那是什么类型的是指针呢(指向int,char,还是函数)?从int *(*(*(*abc)(int a))[6])(int a)可以看出,ptr_type是指向函数的指针,该函数的形参为int a,返回值为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 的类型的函数。至此解析完毕。
按上述方法,再复杂的也可以一步步解析出来。
就像习武不是为了打人而是为了防身一样,我们了解上述方法是为了看懂别人写的复杂声明 ,而不是为了在实践中自己去构造这种复杂的东东。实在需要复杂声明时,可以用 typede f替代一部分。例如上面语句可改成两句:
typedef char (*FUN_PTR)(int);
FUN_PTR a[3];
这样就清晰多了。
此外 , 上面的分析方法还让我们对某些东西的本质更加清楚 。 比如 , n 维数组的本质都是一维数组。看个具体的例子:
int a[3][5];
这句声明的是一个包 含 3 个元素的一维数组 , 其每个元素又是一个 由 5 个 in t 数构成的数组 。
我们不能理解为: a 是一个包含 5 个元素的一维数组,其每个元素又是一个由 3 个 int 数构成的数组。为什么?还是按上面的方法分析,这里从略。
有的书上或网上提供 " 向右看 , 向左看 " 的方法 , 其实缺乏通用性 , 比如它不适用于对多维数组本质的分析 . 而且这种方法掩盖了本质 . 本质应该是按上面所讲的 , 根据运算符优先级逐层剥开。
关于上面那个typedef的定义为什么和上面那个例子等价的问题,另启一篇讲述。