C语言绝对地址跳转.docx
《C语言绝对地址跳转.docx》由会员分享,可在线阅读,更多相关《C语言绝对地址跳转.docx(6页珍藏版)》请在冰豆网上搜索。
C语言绝对地址跳转
C语言使用函数指针跳转到程序固定地址(0x8000)执行程序的方法
使用函数指针,把一个纯数据强制转换为函数指针类型。
intmain(void)
{
void(*my_function)(void);
//int*my_address=0x8000;
my_function= (void(*)()) (0x8000);
my_function();
}
其实更简单,不适用中间变量,直接一步到位:
(*(void(*)())0x8000)();
转成汇编就占两条指令.
在IAP的bootloader中经常使用到地址跳转,指定程序跳转到某一地址运行,例如强制跳转到0x2c去执行,则可使用(*((void(*)(void))0x2c)))();
实际上这是运用的函数指针,可以这样分解:
1,函数指针的定义为void(*fd)(void);省略参数的函数原型为
void(*)(void).
2,0x2c, 这里的0x2c为地址,可以认为是变量(个人理解),可以理解为将变量0x2c进行强制类型转换,转换成函数指针类型,即( void(*)(void) )0x2c.
3,调用函数。
(* (func) ) (); func为函数指针(void(*)(void))0x2c, 合起来就是(*((void(*)(void))0x2c))();
嵌入式笔试题:
想让程序跳转到绝对地址0x100000处执行,该如何做?
网上看到有如下答案:
*((void(*)(void))0x100000)();
经过在VC++6.0和Linux gcc4.4.3下测试,均不能通过编译。
VC++6.0报错:
errorC2100:
illegalindirection
GCC报错:
error:
voidvaluenotignoredasitoughttobe
应该是怎么写呢?
经过测试,有两种方法:
答案1. (*(void(*)(void))0x100000)();
答案2. ((void(*)(void))0x100000)();
仔细观察,第一种写法只是第一个*的位置不同,第二种写法少了一个*,但是都能正确编译通过,且正确执行。
为什么会有这两种答案呢?
查阅资料后发现,与历史原因有关……
先来看看如下例子:
例一:
[cpp] viewplain copy
print?
1.#include
2.void func(void)
3.{
4. printf("hello.\n");
5.}
6.void main(void)
7.{
8. printf("func=%d\n", func);
9. printf("&func=%d\n", &func);
10.}
运行程序后发现
两次打印结果相同!
!
!
按照&运算符本来的意义,它要求其操作数是一个对象,但函数名不是对象(函数是一个对象),本来&func是非法的,但很久以前有些编译器已经允许这样做,c/c++标准的制定者出于对象的概念已经有所发展的缘故,也承认了&func的合法性。
因此,对于func和&func可以这样理解,func是函数的首地址,它的类型是void(),&func表示一个指向函数void func(void)这个对象的地址,它的类型是void(*)(),因此func和&func所代表的地址值是一样的,但类型不一样。
func是一个函数,&func表达式的值是一个指针!
既然取不取址都可以,那么*不*也都可以……
所以,在调用一个函数的时候,也有两种方法,正如前面的两种答案。
例二:
[cpp] viewplain copy
print?
1.#include
2.
3.void func(void)
4.{
5. printf("hello.\n");
6.}
7.
8.void main()
9.{
10. void (*func_p)(void) = func; //定义一个函数指针,这个指针无返回值,无参数,指向fun函数
11.
12. (*func_p)();
13. (func_p)();
14.}
上面的两种调用方法也都是正确的,编译通过,正确执行。
其实,
[cpp] viewplain copy
print?
1.func_p();
也是正确的调用方式……
更有甚者
[cpp] viewplain copy
print?
1.(*func)();
还是正确的……只是平时不这么用罢了(注意此处是func,不是func_p)
暂且不考虑那么多调用方式(知道就好了),现在回过头来看看
[cpp] viewplain copy
print?
1.(*(void(*)(void))0x100000)();
和
[cpp] viewplain copy
print?
1.((void(*)(void))0x100000)();
到底是什么东东……
1.首先来认识一个新的数据类型,如:
void(*)(void),和int*类似的一个数据类型,只不过int*是一个指向int型的指针,而void(*)(void)是一个指向函数的指针,且这个函数无返回值,无参数。
2.然后给他外层加个括号,如:
(void(*)(void)),这样是不是很像(int*),我们在做强制类型转换的时候需要在类型外加个括号的是吧。
3.接着把0x100000强制转化为一个函数指针,即:
(void(*)(void))0x100000
4.最后就是调用这个函数,外层再加个括号,后面在加一对括号(参考例二的形式),
如:
((void(*)(void))0x100000)();就可以到绝对地址0x100000处去执行了……
或者(*(void(*)(void))0x100000)();只是加不加 * 的问题。
上面例二中可以看出,在用函数指针调用一个函数时,加不加 * 都是可以的。
所以答案就出来了……
另外,你可能疑惑,按照例二中func_p();的形式,那么 (void(*)(void))0x100000();也应该对呀?
但是,实际测试,编译报错:
errorC2064:
termdoesnotevaluatetoafunction
为啥呢?
我也不知道了……反正不管有没有*,记得加个括号就好了……
那为什么最开始的*((void(*)(void)0x100000))();不对呢?
因为没见过*func(); 这么用的……
如果要是外面再加一层括号就对了,如:
(*( (void(*)(void)0x100000) ))();
其实,把蓝色括号去掉(蓝色括号和绿色括号重复了),就又变成答案一了……
所以无论如何,最外层不能是* ,必须是括号!
因为没见过 *func(); 这么用的……
一小段精彩的 C语言,1和2实现相同的功能,欢迎大伙跟帖呵(特别感谢trio老师)
1 void(*theUboot)(void);
。
。
。
。
theUboot=(void(*)(void))(0x30700000);
theUboot();
。
。
。
。
。
2 (*(void(*)(void))(0x30700000))();
aokikyon发表于2008-9-1021:
00
看不懂。
。
。
。
。
。
。
。
ToT
讲解下好吗
shixq发表于2008-9-1109:
13
呵呵,强制类型转换,将一个绝对地址转换为一个函数指针,并调用这个函数以跳转到前面提到的绝对地址.
翻译成汇编就是:
movr0,0x30700000;
movpc,r0
trio老师说了,会用函数指针就可以成为c语言高手.
目前尚处于模仿阶段,还没有领会函数指针的精髓,继续修炼中...
shixq发表于2008-9-1109:
16
原来用2的方式实现要加一个括号...
看来对优先级的领会还是不够,当天少加了一个括号,编译死活通不过,没办法才用1的方法,先定义了一个空指针...
trio发表于2008-9-1110:
10
(*(void(*)(void))(0x30700000))();
可以这样理解
首先(void(*)(void))是一个强制类型转换符,他将后面的0x30700000这个无符号整数强制转化为一个函数指针,该函数指针所指向的函数入口参数为void,返回值也是void。
如果到这步你看懂了,那么设(void(*)(void))(0x30700000)为fp;那么上面的表达式就可以简化为(*fp)(); OK,这下就清楚了吧,我们将上面转化好的函数指针进行引用(也就是调用函数指针指向的函数)。
dsp立即寻址、绝对寻址、直接寻址、间接寻址的差别是什么?
如何判断汇编里边的语句他们是属于哪类寻址方式
cz447307145 | 浏览2742次
发布于2013-01-1710:
26
最佳答案
所有汇编语句必然是两个操作数,而其一必然是寄存器,因为寄存器速度最快,为提高处理速度必须在寄存器中完成操作。
就电脑存储层次的速度而言,寄存器register>缓存cache>内存memory>硬盘hard disk,可见离cpu越近速度越快(register是直接做在cpu内部的,就像下面例子中的教学楼、教务处等,所以速度够快)。
DSP与此类似。
根据另一个操作数所在的位置分类的话,就是上述几种和另外一些了寻址模式了,如堆栈寻址、MMR等。
举个例子。
你现在你的学校里,去找人处理一些事情,那么此人在哪里呢?
几种可能:
在身边、在某省市县小区幢101、你校第1教学楼301或你校第2教学楼301、此人不在学校但是教务处办公室可以查到他现在的地址,以上分别对应立即寻址、绝对寻址、直接寻址、间接寻址。
例子:
LD #10,A
STL A,*(y)
LD @x,A
LD *AR1,A
可以看到,
1.立即寻址,立即数前面必须加#;
2.绝对寻址是*和(),y就是这个绝对地址省市县区楼;
3.直接寻址的地址x前有@,就是301,至于是1教还是2教作基地址,需要提前设置到底是到哪个教学楼去找,对应sp堆栈或者数据存储器页指针DP设置。
用SSBX CPL(SET bit置CPL为1,用SP作基地址)或RSBX CPL(reset将CPL置0,用DP做基地址),到某页数据存储器中找,还需要设置此页数,如另DP为3就到第三页数据存储器中找),将堆栈当前地址sp+x低7位或将DP第三页首地址+x低7位;
4.间接寻址也有*,不过紧跟的是一个寄存器AR1而不是直接存储器中的数值x。