700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > C语言指针的那些事:第三篇(函数指针 指针函数 函数指针数组 指向函数指针数组的

C语言指针的那些事:第三篇(函数指针 指针函数 函数指针数组 指向函数指针数组的

时间:2022-04-05 05:48:48

相关推荐

C语言指针的那些事:第三篇(函数指针 指针函数 函数指针数组 指向函数指针数组的

文章目录

1. 函数指针1)函数指针的例题2. 指针函数3. 函数指针数组1)函数指针数组的用途4. 指向函数指针数组的指针5. 一些后话

1. 函数指针

函数指针就是指向函数的指针;本质上还是一个指针

首先我们要知道函数的地址就是函数名,取地址函数名也是函数的地址;这两个方式事等价的

类如:

int Add(int x, int y){return x + y;}# include<stdio.h>int main(){int(*p)(int, int) = Add; //这里函数名就是函数的地址int add = (*p)(10, 20); //通过函数指针调用函数//int add = p(10, 20);printf("%d", add);return 0;}

首先关注int(*p)(int, int) = Add;这里说明 有*号,说明p是个指针指向函数,函数的返回类型为int,参数列表为(int,int); 那解引用p得到就是指针指向的东西,该指针指向的就是 函数,所以*p就是函数名,那么我就可以这样调用函数:(*p)(10,20);那既然函数名就是函数的地址,那也就是说还可以这么用指针当作函数名,可以这么调用函数 :p(10,20);

所以说,有函数指针,调用函数的方式就多了两种,这三种调用的方式是等价的:

(*p)(10,20),通过解引用指针调用函数

p(10,20),通过函数指针调用函数

Add(10,20);通过函数名调用函数

C语言规定:其实函数指针的变量名就是函数名,通过函数指针去调用函数时候,不需要解引用函数指针,也可以调用函数。

注意:

这三种函数指针的调用方式是等价的,说明调用函数时候*号没用

1)函数指针的例题

看一道题:void( * (signal( int ,void(*)(int)) ) (int)请问如何理解这句声明:

首先我们从简单的看起:void(*)(int),看这个*说明是一个指针,指针的类型为去掉指针名,剩下的

就是指针类型,那这里没有指针名,所以指针类型为 void(*)(int),指向的函数返回类型为为 void 参数列表为 >int,看signal( int ,void(*)(int));这就是signal 函数声明。 而 去掉函数声明,剩下的void(* )(int)又是一个>函数指针,即为signal函数声明返回的类型,所以我们可以这么说:

void( * (signal( int ,void(*)(int)) ) (int)是函数声明,signal是函数名, 返回类型为void(*) (int),参数列表为intvoid(*)(int);返回类型是一个函数指针,指向的函数返回类型为void和参数列表为int;

上面的函数声明等价下面的方式:

typedef void(*pfun_t)(int);//可以这么想 typedef void(* )(int) p_fun_t;//把 void(* ) (int )类型 命名为 pfun_t类型;pfun_t signal(int,P_fun_t);

其实你可以类比一下数组,比如 定义数组声明时候int arr[10]; 数组的类型为int [10];写法也是把名字插入到类型中间,而函数的类型也是,写法也是讲函数名插入到类型中间

看看另一道题目:*((void(*)())0)(),解释一些这语句标识什么意思。(来自《c缺陷和指针》)

解释:

*( ( void(*)() ) 0 ) ();首先我们知道一个数组前面带一个括号,括号里面是类型的话,是发生强制类型转化的。这里就是 0 为数组 ,( void(*)() )0,这个意思就是把0转化为 void(*) () 类型我们知道 ( void(*)() ) 0 其实就是把 0转化为 函数指针类型。那么此时 0就表示 函数地址了。然后 * 号标识解引用该函数指针类型的 0 ,即*( ( void(*)() ) 0 ) 表示解引用函数指针类型的0的地址得到指针指向的内容,其实就是函数名了。最后的括号表示用函数指针类型解引用去调用函数。所以最终解释为:*( ( void(*)() ) 0 ) (); 这是一次函数调用,讲 0强制转化为函数指针类型,通过解引用该 指针类型去调用 0 位置的地址,得到函数名,然后 调用函数。

函数指针到底有什么用途呢?到时候学深入你就知道了,这里只是点播一下,以后碰到不至于不会。

2. 指针函数

其实指针函数本质是函数,之所以叫指针函数,只不过其返回类型是指针类型。

如:

int* Add(int x,int y); Add就是指针函数指针函数没有什么神秘的,就是平时我们写的返回类型为指针的函数而已。值得注意的一点是,不要返回局部变量的地址,让指针去接收她,不然,会有意想不到的错误发生。

3. 函数指针数组

函数指针数组,本质还是数组,只不过数的返回类型为 函数指针。

我们知道,在一个声明语句中,一个变量(指针变量也是变量)的类型,是去掉变量名剩下的类型。

所以 int arr[5]; arr的类型为 int[5];int*arr[5]; arr 的类型为 int*[5];int *(*parr)[5];parr 的类型为 int *(*)[5];你可能,不理解类型的含义是什么?(当然前面的指针篇幅做出过解释),但是你却可以清楚的知道 一个变量的 类型是什么。所以 看看这个 声明int(*arr[5])(int,int) ; arr的类型为 :int(* [5])(int ,int);这是什么呢?假如我把int(*arr[5])(int,int) 写成 int(*)(int,int) arr[5] ;是否看得更清楚:这样写,则类型为 int(*)(int,int)[5] ;其实就是一个数组类型,但是我们却不可以这么写,需要写成 这种方式int(* [5])(int ,int);声明时候也是写成:int(*arr[5])(int,int) ; 即使我们知道 arr是一个数组 ,也是需要 插入到类型的中间去,这是c语言规定的写法咯。

所以我们来解读一下:int(*arr[5])(int,int)声明的含义

int(*arr[5])(int,int);首先:arr是一个数组,该数组有5个元素,每个元素的类型为函数指针类型 ,即int(*)(int,int);该函数指针指向的函数返回类型为 int ,参数列表为有两个 分别为int int;

在我的C语言指针的那些事:第一篇(有指针书写的技巧),系统的讲过数组的元素类型,数组类型,变量类型,指针类型,指针指向的类型到底怎么看出来的。顺便说了书写指针技巧。

总得来说:

要得到变量的类型,只要去点变量名就可以,这个变量名广泛(指针变量,函数名,数组名,普通变量名)都是变量名。

要得到指针指向的类型,只要去掉指针名和一个指针*星号,这个星号要和指针名挨着的星号呀,剩下都属于指针指向的变量类型。无论你的指针是多少级的指针,都是合适用的。

1)函数指针数组的用途

函数指针数组,可以存放函数名,也就是函数的地址,因为函数的地址类型就是函数指针。

我们先不讲函数指针数组,先引入一个案例,看看是否能优化,用什么办法优化。(这是极其简单的案例,不涉及过多的细节思考,只为了讲明白函数指针数组的用途)

首先我们有一个需求,就是实现一个计算器,该计算器 有 加减乘除 的方法。

通过输入对应的选项可以执行你指向的加减乘除法,然后输入你的计算数字,可以显示计算结果。

分析需求

我们可以设计四个函数,分别为加法 减法 乘法 除法 函数;

通过循环语句和 witch语句去控制选择 是算 加 减 乘 除;

其次输入计算条件,得到计算结果。

那么开始编码:大概就是这样:

#include <stdio.h>int add(int a, int b) //加法函数{return a + b;}int sub(int a, int b) //减法函数{return a - b;}int mul(int a, int b) //乘法函数{return a*b;}int div(int a, int b)//除法函数{return a / b;}int main(){int x, y;int input = 1;int ret = 0;do{printf( "*************************\n" );printf( " 1:add2:sub \n" );printf( " 3:mul4:div \n" );printf( "*************************\n" );printf( "请选择:" );scanf( "%d", &input);switch (input) //通过选择对应数字决定你是要做加减乘除法{case 1:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = add(x, y);//加法一次调用printf( "ret = %d\n", ret);break;case 2:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = sub(x, y); //减法一次调用printf( "ret = %d\n", ret);break;case 3:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = mul(x, y);//乘法一次调用printf( "ret = %d\n", ret);break;case 4:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = div(x, y);//除法一次调用printf( "ret = %d\n", ret);break;case 0:printf("退出程序\n");breark;default:printf( "选择错误\n" );break;}} while (input);return 0;}

本代码很简单,目的是谈一谈什么地方可以优化。

你发现了吗?代码是否很冗长,不够美观。是因为我调用函数时候,写出了 四个调用函数。这写函数的代码基本一致,逻辑也是一致。返回类型也是一样。

是否有更好的办法去解决这个函数调用的问题,我希望有一种方式:可以写1 种代码的形式,就可以调用 上面 4个函数。那就是我们的函数指针数组,数组就是处理一些类型相同的代码而产生的。

所以一旦我们设计把 不同的函数名,初始化函数指针数组,通过下标就可以访问到数组的函数名,再通过()函数调用符号,就可以直接调用函数了,这不美哉?<?font>

说干就干。

#include <stdio.h>int add(int a, int b){return a + b;}int sub(int a, int b){return a - b;}int mul(int a, int b){return a*b;}int div(int a, int b){return a / b;}int main(){int x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = {0, add, sub, mul, div }; //转移表while (input){printf( "*************************\n" );printf( " 1:add2:sub \n" );printf( " 3:mul4:div \n" );printf( "*************************\n" );printf( "请选择:" );scanf( "%d", &input);if ((input <= 4 && input >= 1)){printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = (*p[input])(x, y); //重点关注这句}elseprintf( "输入有误\n" );printf( "ret = %d\n", ret);}return 0;}

来分析分析:

int(*p[5])(int x, int y) = {0, add, sub, mul, div }; 这就是函数指针数组的写法了,里面初始值为:函数名;当我们调用时候:通过该语句ret = (*p[input])(x, y);就可以通过数组名p和下标运算符[],访问到数组元素啦,由于数组元素是函数名(函数地址),所以解引用一次,加个函数调用符号,就可以调用函数了。这真实秒啊。

4. 指向函数指针数组的指针

学了那么多指针,发现了吗,这些指针的名字都是一些排列组合形成的。

要明白指向函数指针数组的指针是什么东西,首先需要观察它的最后两个字:指针;说明就是指针,前面都是修饰作用。

所以:

指向函数指针数组的指针:就是指针指向的类型为函数指针数组类型。

那么我们知道声明这个函数指针类型吗?

想想我上面说过的话,如何定位,知道一个变量的类型。

好了,我们看看这个声明:void (*(*ppfunArr)[10])(const char*)

是不是很复杂? 那么我们来拆解一下:提出变量名 ppfunArr 剩下的是变量名的类型 :void( (* (*)[10] )(const char*);假如我们继续把指针ppfunArr挨着的星号*去掉,得到就是指针ppfunArr指向的类型:void( (* [10] )(const char*);看到这个有没有熟悉的感觉?不就是一个函数指针数组嘛。对的,就是它。所以我们如何解释这个声明呢?解释: 首先 ppfunArr是一个指针,该指针指向的类型为函数指针数组,该数组里面有10个元素,每个元素的类型为函数指针,该函数指针指向的函数返回类型为 void ,参数列表为 const char*;

5. 一些后话

其实 指向函数指针的数组指针,了解的一点就够了,再复杂的不必要了解了,了解到这里,可以说对指针有着非常非常深刻的认识了,至于这个指针到底有什么应用,等到以后有需求时候,或者看别人代码时候,你就会知道怎么应用了,我们都是通过需求来应用知识滴,所以希望,大家可以知道这个点,不要认为学的东西没有用,理解这么复杂指针没有用。你保不定什么时候就用上了呢,对吧。

还有 ,可能,大家对复杂类型的声明还是不太会解释,或者说阅读还是有一定的困难。

所以我会写一篇番外篇:教你如何阅读复杂类型的声明,图文并茂帮你解决

接下来还有一些指针的其他内容讲,比如一些常见指针笔试题,和一些其他有关的知识,回调函数等

C语言指针的那些事:第三篇(函数指针 指针函数 函数指针数组 指向函数指针数组的指针)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。