C51里的函数指针

  1. 云栖社区>
  2. 博客>
  3. 正文

C51里的函数指针

余二五 2017-11-15 18:19:00 浏览543
展开阅读全文

在C语言里面提供了函数指针,我认为它比较重要的功能就是用来提供接口,使得C语言可以模拟面向对象的语言为某些功能提供接口,实现功能代码的隔离。
这不,前些日子写了个小程序,用C51写的,其中有个功能就是操作液晶屏,在上面显示菜单、输出结果什么的。在我看来,这部分功能使用函数指针最好不过了。譬如,不管是什么菜单,总得要显示出来吧,定义一个show()接口就好。这样上层代码很简单,反正对每个菜单都调用其show()接口,它们自己完成显示。
嗯,可是想法是好的,结果是不妙的。首先这么做在C51的语法层面上没有任何问题,编译后没有任何错误。可是实际运行时就发现一些奇怪的现象,譬如调用某个函数,明明入口参数的值是100,跑到函数内部就莫名其妙的变成了其它的值了。弄了好久不得其解。
最后没有办法,切换keil到汇编模式,这才发现出问题,入口参数的变量地址与某个全局变量的地址重了。在这里说一点题外话,我做的那个小东西用的是8031,片内变量空间才128个,这里面还包括了堆栈段的空间,因此我轻易不定义片内变量,大部分情况下都是使用片外变量,反正对于我做的那个东西,片外变量的速度也是足够了。
现在的问题是入口参数变量的地址怎么会跟其它全局变量的地址重复了呢?我仔细分析了半天,发现原来是函数指针惹的祸。如果没有函数指针,程序里的每个函数都有直接被调用的代码,譬如初始菜单显示,可能就会调用startMenuShow()函数。但是现在变成了函数指针,显示初始菜单的代码就变成了menu[START].show()。也就是说编译器在编译的时候并不知道startMenuShow()函数在哪里被调用,而只有在运行的时候才知道是哪一个函数。
本来如果没有函数指针,编译器能够构建出一个完整的函数调用树,并且根据这个树完成变量空间的分配。现在有了函数指针,这个函数调用树就跟实际的情况不一样了,编译器的优化导致有些变量空间就重复。譬如在C51中有如下代码:
unsigned short c_add(unsigned char xdata a, unsigned char xdata b)
{
    a++;
    b++;
    return (a + b);
}
void main(void)
{
    unsigned char xdata score1, score2;
    unisgned short xdata total;
    score1 = 100;
    score2 = 80;
    total = c_add(score1, score2);
}
在上面的代码中,函数c_add()的两个形参a、 b与main()函数的实参score1、score2的变量地址是不一样的,因为在main()函数中,调用完c_add()函数后,score1和score2这两个变量可能还有后续调用,因此编译器不会对这四个变量定义到同一个位置。
但是如果main()函数中采用函数指针调用c_add()函数,那么编译器就不知道main()函数中调用的函数到底是什么,出于代码优化的考虑,编译器为了节省变量空间,当它发现c_add()函数和main()函数没有直接、间接调用关系,那么它就可以把c_add()的两个形参a、 b与main()函数的实参score1、score2的变量地址定义到同一地址。
因为这四个变量都分别是本函数的局部变量,相互之间又没有直接调用关系,就算是地址重复了,也不会导致逻辑上有什么问题。这样定义可以节省程序变量所占用的空间,对于嵌入式系统来说,资源一般都是有限的,能省则省。编译器的优化缺省设置一般都是比较高的,我也不建议为了函数指针调整优化级别,毕竟优化带来的好处还是有一些的。
综上所述,我的结论就是在C51开发程序的时候,应该尽量避免使用函数指针,如果一定要用,也应该避免使用形参、局部变量,以免因为地址重复给程序造成一些不可预料的后果,而这些问题查找起来是非常困难的。
当然,如果用的不是C51,我所知道的在嵌入式开发中,在ARM9系统下使用的C语言,对于函数指针的支持是没有问题的,我的另外一个系统就大量使用了函数指针。




本文转自 tywali 51CTO博客,原文链接:http://blog.51cto.com/lancelot/224373,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
余二五
+ 关注