深圳单片机开发网
 
-- 
深圳单片机交流网
收藏本站
联系站长
关于本站
首页 信息发布 产品宣传 论坛交流 学习文章 技术人生 项目源码 技术资料 人才收录
 今天是: 2018年9月25日 星期 二
欢迎光临!单片机群号:154389772 25930265 71062262 21829895 64584393 26583231  
热门文章推荐


当前位置:首页>>>单片机学习文章>> 指针应用
指针应用
作者:ygzhw给他留言 [转载] 字体:
发表于:
2014-09-21 09:33:07

(*(void(*)())0) ()讲解-单片机另类的复位

作者:佚名   来源:本站原创   点击数:168   更新时间:2013年06月09日   【字体:  】 

深圳单片机交流网

深圳单片机交流网板子介绍:http://www.51hei.com/150a.htm
淘宝购买:http://item.taobao.com/item.htm?id=3670564802 深圳单片机交流网:125739409 电话:15173607302

硬件地址跳到0处

(*(void(*)())0) ();

 

预备知识

float (*h)();

表示h是一个指向返回值float类型的函数的指针

(float(*)())

表示一个"指向返回值float类型的函数的指针"的类型转换符

 

 

假设fp是一个函数指针,那么如何调用fp所指向的函数,调用方法如下:

 (*fp)();

 

按照人们的惯性思维,那么我们可以这样写

(*0)();

 

上式不能生效,因为运算符*必须要一个指针来做操作数,而且这个指针还必须是个函数指针。所以我们必须要把0强制转换成一个函数指针(指向返回值为void类型的函数的指针)

 

假设fp是个float指针,声明如下

float * fp;

 

把0强制转换成一个float指针(把变量fp去掉就可以了)

(float *)0;

 

类似:

假设fp是函数指针为void类型的函数的指针),声明如下:

void (*fp)();      

 

把0强制转换成该函数指针(变量fp去掉就可以了)

(void(*)())0

 

最后用(void(*)())0代替fp,从而得到调用的用法

(*(void(*)())0) ();

 

 

 

 

可用typedef简化函数指针

例如:

typedef char * string;

 

string test="hello";

 

类似

typedef void(*func)();   //这样func就表示一个函数指针的类型

 

(*(func)0)();

 

 

例子

方法一:

typedef void (*pfunction)(void);

 

 void FMI_Jump(void)
{   
  pfunction jump;
  jump=(pfunction)(0x80000);
  jump();
  
}

 

 方法二:

((void(code *)(void))0xF400)();

 

 

从C51的指针到病毒-c51软复位,经典,分析透彻

作者:佚名   来源:本站原创   点击数:1129   更新时间:2008年11月21日   【字体:  】 

深圳单片机交流网

深圳单片机交流网板子介绍:http://www.51hei.com/150a.htm
淘宝购买:http://item.taobao.com/item.htm?id=3670564802 深圳单片机交流网:125739409 电话:15173607302

单片机指针说到黑客程序
纯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 )
(*K) ( )

( *( ( void (*)( ) )rst ) ) ( );
由于没有输入参数,上述复位代码更严谨的写法是: 
#define K ( ( (void (*)(void ) ) rst )
(*K) ( )

( *( ( void (*)(void ) )rst ) ) ( );
l 关于帖子作者的解释
千万不要犯“&rst”形式的错误,对于一维数组而言,数组名rst就代表地址。以下二者等
价,更常用的是等式左边的形式:
rst == &rst[0]
整个函数指针无所谓参数传递,只是把rst当作程序执行地址调用而已,那个学生的解释也
有问题。
还有一点必须提及,不是说能通过编译,甚至生成正确代码,就表示某语句一定是对的。
对很复杂的语句,要考虑到编译器不严格甚至出错的可能性。
l 哈佛结构和一个蠕虫病毒
请注意,定义数组rst[]时用了关键字code,这是C51特有的关键字,意味着把数组定义到
程序空间。标准C是没有关键字code的。
哈佛结构和普林斯顿结构:
哈佛结构——程序空间和存储空间分开的。C51算是不太严格的哈佛结构——虽地址线分
开,但数据线没有分开。DSP是增强的哈佛结构。
PC电脑上奔腾CPU是普林斯顿结构——数据空间和程序空间统一编址。
如果数组rst[]数据的汇编机器码是删除文件的机器码,这算不算是病毒?
曾经流行过一种蠕虫病毒,其发作机理采取的就是将恶意代码保存成文本文件,然后通过
指针调用执行这个文本,很多杀毒程序也不会查询文本文件。
程序也罢,数据也罢都是二进制形式,如果数据空间和程序空间是统一编码的, 数据当然
可以当作程序运行。
在这一点上,相对而言,哈佛结构的CPU安全性会好一点点。但嵌入式应用少有病毒,一般
不用关心。
单片机复位的更好方法
帖子中汇编语言解释如下:
clr a //清除ACC=0
push acc //压0到堆栈——8位
push acc //再压0到堆栈——再8位
reti //返回到0地址,从而执行。
帖子作者的这种复位方法比较麻烦,更加简单的复位写法是(摘自《C缺陷与陷阱》):
( * ( void (*)( ) )0 ) ( );
本句的分析方法同上,但更加精炼,没有多余的汇编语句。
上述复位的方法可称为软件复位。
软件复位跟真正上电复位有很大差别:上电复位时大部分寄存器都有确定的复位值;软件复位则只相当于从0地址开始执行而已,寄存器不会变为确定的复位值。
如果用户要编程实现上电复位这种情况,在程序中不要踢看门狗即可。大部分单片机都有看门狗吧

 

, so-shading: rgb(246,251,255)">第一句定义一个数组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 )
(*K) ( )

( *( ( void (*)( ) )rst ) ) ( );
由于没有输入参数,上述复位代码更严谨的写法是: 
#define K ( ( (void (*)(void ) ) rst )
(*K) ( )

( *( ( void (*)(void ) )rst ) ) ( );
l 关于帖子作者的解释
千万不要犯“&rst”形式的错误,对于一维数组而言,数组名rst就代表地址。以下二者等
价,更常用的是等式左边的形式:
rst == &rst[0]
整个函数指针无所谓参数传递,只是把rst当作程序执行地址调用而已,那个学生的解释也
有问题。
还有一点必须提及,不是说能通过编译,甚至生成正确代码,就表示某语句一定是对的。
对很复杂的语句,要考虑到编译器不严格甚至出错的可能性。
l 哈佛结构和一个蠕虫病毒
请注意,定义数组rst[]时用了关键字code,这是C51特有的关键字,意味着把数组定义到
程序空间。标准C是没有关键字code的。
哈佛结构和普林斯顿结构:
哈佛结构——程序空间和存储空间分开的。C51算是不太严格的哈佛结构——虽地址线分
开,但数据线没有分开。DSP是增强的哈佛结构。
PC电脑上奔腾CPU是普林斯顿结构——数据空间和程序空间统一编址。
如果数组rst[]数据的汇编机器码是删除文件的机器码,这算不算是病毒?
曾经流行过一种蠕虫病毒,其发作机理采取的就是将恶意代码保存成文本文件,然后通过
指针调用执行这个文本,很多杀毒程序也不会查询文本文件。
程序也罢,数据也罢都是二进制形式,如果数据空间和程序空间是统一编码的, 数据当然
可以当作程序运行。
在这一点上,相对而言,哈佛结构的CPU安全性会好一点点。但嵌入式应用少有病毒,一般
不用关心。
单片机复位的更好方法
帖子中汇编语言解释如下:
clr a //清除ACC=0
push acc //压0到堆栈——8位
push acc //再压0到堆栈——再8位
reti //返回到0地址,从而执行。
帖子作者的这种复位方法比较麻烦,更加简单的复位写法是(摘自《C缺陷与陷阱》):
( * ( void (*)( ) )0 ) ( );
本句的分析方法同上,但更加精炼,没有多余的汇编语句。
上述复位的方法可称为软件复位。
软件复位跟真正上电复位有很大差别:上电复位时大部分寄存器都有确定的复位值;软件复位则只相当于从0地址开始执行而已,寄存器不会变为确定的复位值。
如果用户要编程实现上电复位这种情况,在程序中不要踢看门狗即可。大部分单片机都有看门狗吧

 


(本文引自www.mcujl.com/article.asp?conID=3080)


---------------------------------------------------------------------------------------------------
[打印文章] [关闭本页] [返回顶部]
本网站部分资料转自网上,如有侵权请来信告明,我们会尽快删除  | 网站地图
Copyright @ 2007-2010 深圳单片机开发网,单片机交流网.版权所有
网站管理员:詹工,李工
网站支持:zcl843@163.com QQ:380476830