函数指针的强制转换用法

http://blog.chinaunix.net/uid-20459533-id-1944392.html

2004年7月的一天,在电子BBS讨论区上溜达,看到一个有趣的帖子,整个帖子内容如下: 

纯C51复位功能函数:一个大三学生,让人又爱又怕 

现单列复位部分如下:

main() 

unsigned char code rst[]={0xe4,0xc0,0xe0,0xc0,0xe0,0x32}; // 复位代码 
(*((void (*)())(rst)))(); // 执行上一行代码,将rst数组当函数调用 


本来我告诉他嵌入如下代码:

clr a 
push acc 
push acc 
reti 

结果他却玩了前面哪一段,而数组rst[]中的内容恰恰是上面的汇编机器码,他的做法是将 
rst数组的数据当作代码保存,然后采用绝对地址方式指向该数组,将该数组中的代码当作 
函数来运行。居然通过了!

我觉得有问题,我说即使如此,那绝对地址调用也应该写成(*((void (*)())(&rst)))()  
才对呀,结果他反驳说,那样的话,rst的地址就会当成参数传递给这个绝对地址函数,而 
实际LJMP调用的地址并非rst的地址,而是一个不确定的地址。于是我按照自己的说法尝试 
了一下,看看汇编结果,还真的是将rst的地址传递给了R1 R2,而绝对函数最终LJMP到了 
一个莫名其妙的地址上去了,死翘!

看来C真是一匹不容易驾驭的野马,这个大三学生理解力在我之上,我30多岁的人了,干了 
这么多年还没他的境界呢,唉,人家才学了几天啊,翻了几天书就这么厉害了,服了!

l 首先分析帖子的C语言代码

第一句定义一个数组rst[],数组内数据就是完成复位功能的汇编机器码,具体对应关系 
为:clr a == 0xe4、push acc == 0xc0,0xe0、reti ==0x32 

第二句是一个函数指针的用法,函数指针用法稍微有点复杂,可参看本人著的书,:),以 
下为快速入门讲解。 

定义一个返回值是空函数指针的定义形式如下:


void (*p) ( ) 

当把函数指针赋值后,就能通过函数指针调用函数,调用形式如下:

(*p) ( ); 

或等价的简化形式:

p ( ); 

假设rst就是函数指针,则如下调用形式就可以令单片机复位再起。

(*rst ) ( ); 

但可惜,rst不是函数指针,而是数组名,虽然两者都是地址,但不可直接调用数组名。

如同把char型变量a赋值给int型变量b,(int) 表示强制类型转换:

b = (int) a 

函数指针的强制类型转换公式如下(C语言的哲学是定义形式和使用一致): 

(void (*)() ) rst 

这样经过转换后的rst就可以当作函数指针使用了,简单的调用形式如下:

#define K  (void (*)( ) ) rst 

(*K) ( ) 


或: 

( * ( void (*)( ) )rst ) ( ); 

这样的语句就完成复位再启功能了。类型转换符()的优先级跟指针运算符*的优先级相同, 
二者的结合方向是自右至左,所以上述语句就能完成复位功能了。保险起见有些程序员常 
常喜欢再加个括号:

#define K  ( (void (*)( ) ) rst ) 
 
我这里:
定义处:
uint32                ActFunction;
extern void      menuStartMyMenuFunc(void);
ActFunction = (uint32)menuStartMyMenuFunc; 
 
调用处: 
void   (*ActFunctionCall)(void);
ActFunctionCall= (void(*)(void))(ActFunction );
(*ActFunctionCall)();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值