函数指针的另类用法 from . Andrews

来源:互联网 发布:easyui ssh项目源码 编辑:程序博客网 时间:2024/04/30 14:19
我曾经和一些人聊过天,他们那时在书写在一个小型的微处理器上单机运行的C程序。当这台机器的开关打开的时候,硬件会调用地址为0处的子程序。 为了模仿电源打开的情形,我们要设计一条C语句来显式地调用这个子程序。经过一些思考,我们写出了下面的语句: (*(void(*)())0)(); 这样的表达式会令C程序员心惊胆战。但是,并不需要这样,因为他们可以在一个简单的规则的帮助下很容易地构造它:以你使用的方式声明它。 每个C变量声明都具有两个部分:一个类型和一组具有特定格式的期望用来对该类型求值的表达式。最简单的表达式就是一个变量:float f, g; 说明表达式f和g——在求值的时候——具有类型float。由于待求值的时表达式,因此可以自由地使用圆括号: float ((f)); 则表示((f))求值为float并且因此,通过推断,f也是一个float。 同样的逻辑用在函数和指针类型。例如: float ff(); 表示表达式ff()是一个float,因此ff是一个返回一个float的函数。类似地,float *pf;表示*pf是一个float并且因此pf是一个指向一个float的指针。这些形式的组合声明对表达式是一样的。因此float *g(), (*h)();表示*g()和(*h)()都是float表达式。由于()比*绑定得更紧密,*g()和*(g())表示同样的东西:g是一个返回指float指针的函数,而h是一个指向返回float的函数的指针。 当我们知道如何声明一个给定类型的变量以后,就能够很容易地写出一个类型的模型(cast):只要删除变量名和分号并将所有的东西包围在一对圆括号中即可。因此,由于float *g();声明g是一个返回float指针的函数,所以(float *())就是它的模型。 有了这些知识的武装,我们现在可以准备解决(*(void(*)())0)()了。 我们可以将它分为两个部分进行分析。首先,假设我们有一个变量fp,它包含了一个函数指针,并且我们希望调用fp所指向的函数。可以这样写: (*fp)(); 如果fp是一个指向函数的指针,则*fp就是函数本身,因此(*fp)()是调用它的一种方法。(*fp)中的括号是必须的,否则这个表达式将会被分析为*(fp())。我们现在要找一个适当的表达式来替换fp。 这个问题就是我们的第二步分析。如果C可以读入并理解类型,我们可以写(*0)(); 但这样并不行,因为*运算符要求必须有一个指针作为他的操作数。另外,这个操作数必须是一个指向函数的指针,以保证*的结果可以被调用。因此,我们需要将0转换为一个可以描述“指向一个返回void的函数的指针”的类型。 如果fp是一个指向返回void的函数的指针,则(*fp)()是一个void值,并且它的声明将会是这样的void (*fp)();因此,我们需要写 void (*fp)(); (*fp)();来声明一个哑变量。一旦我们知道了如何声明该变量,我们也就知道了如何将一个常数转换为该类型:只要从变量的声明中去掉名字即可。因此,我们像下面这样将0转换为一个“指向返回void的函数的指针” (void(*)())0 接下来,我们用(void(*)())0来替换fp: (*(void(*)())0)(); 结尾处的分号用于将这个表达式转换为一个语句。 在这里,我们就解决了这个问题时没有使用typedef声明。通过使用它,我们可以更清晰地解决这个问题: typedef void (*funcptr)(); (*(funcptr)0)();
原创粉丝点击