将指针当作一种普通数据类型
不过C语言指针的灵活与强大,也导致很多初学者认为指针是一个很难的概念,因此在遇到指针时,常常会觉得“紧张”。例如下面这个例子:
int fun(int a){a = 3;}int val = 1;fun(val);printf("val = %d\n", val);即使是初学者,只要了解了函数形参和实参的关系,也知道上面这段C语言代码编译后会输出 val = 1。但是如果将C语言代码做适当修改:
int fun(int *a){a = (int*)3;}int *val = (int*)1;fun(val);printf("val = %p\n", val);
这段C语言代码编译后,会输出什么呢?可能一些初学者会认为输出 val=0x3,但是将这段代码实际编译运行,得到的输出却是:
# gcc t.c# ./a.out val = 0x1怎么回事呢?原因当然是简单的,这个问题仍然可以用函数形参和实参的关系解释,很基础。但是有些初学者还会迷惑,“这可是指针!”其实,这就是将C语言中的指针“特殊化”了。
在分析指针问题时,一个小技巧就是将指针当作C语言中的“普通数据类型”,例如可以将 int* 变量看作是“int指针型”变量。现在改写上面的C语言代码:
typedefint* IPTR;int fun(IPTR a){a = (IPTR)3;}IPTR val = (IPTR)1;fun(val);printf("val = %p\n", val);这里使用 IPTR 表示 int* 类型,一切看起来就很容易理解了。
“函数指针类型”
既然把C语言中的指针看作是一种数据类型,那么函数指针也就容易理解了,也不过是一种像 char 、int 一样的数据类型而已。char 、int 作为C语言中的基础数据类型,是允许用其定义数组的,那函数指针这种“数据类型”也可以定义数组吗?答案是肯定的,请看:
typedefint (*funs[8])(int *data);上面这行C语言代码就定义了一个函数指针数组 funs,funs 可以管理 8 个函数指针。有时候为了便于理解,上述代码常常会拆解成下面两行:
typedefint (*FPTR)(int *data);FPTR funs[8];funs 的各个元素可以执行原型为 int f(int *p); 的函数,例如:
funs[0] = fun;funs[3] = fun;使用 funs 实现函数调用也很简单:
funs[0](val);funs[3](val);函数指针数组的意义
相信读者已经了解函数指针数组的定义和使用了,那它有什么意义呢?实际嵌入式C语言项目开发中常会遇到这样的需求:服务端采集到数据后,需要将其分别发送给模块1、模块2、…、模块 n,但是各个模块使用的协议不一致,因此这里假设每一个模块都定义了一个函数用于传输数据:
void m1Send(void *data){// 模块1 发送函数}void m2Send(void *data){// 模块2 发送函数}...void mnSend(void *data){// 模块n 发送函数}这些发送函数根据各自的协议自定义逻辑,但是接口却是一致的,如果服务端需要分发数据,则需要:
m1Send(data);m2Send(data);...mnSend(data);如果有若干组数据需要分发,那么上面这样的C语言代码需要重复写若干次,太繁琐了,而且重复的代码不利于后期维护。这种情况下,使用函数指针数组就非常方便了:
void (*mxSend[N])(void *data);void init(){mxSend[0] = m1Send;...mxSend[N-1] = mnSend;}只需调用 init() 函数将各个模块的数据分发函数注册到 mxSend 数组,之后使用 mxSend 就很方便了,请看下面的C语言代码:
int i;for(i=0; i
小结
经过本节的讨论,相信读者能够发现C语言中的指针也不过如此,在实际使用中,只需将其当作普通的数据类型就可以了。这样一来,函数指针数组也就不难理解了,合理的使用它,可以写出更加紧凑的C语言代码。本节最后还举了一个数据分发的实例用于说明C语言函数指针数组的方便,当然了,函数指针的用途远不止于此。
看我主页简介免费C++学习资源,视频教程、职业规划、面试详解、学习路线、开发工具
每晚8点直播讲解C++编程技术。
网友评论