1. 程式人生 > >C語言實現程式跳轉到絕對地址0x100000處執行

C語言實現程式跳轉到絕對地址0x100000處執行

嵌入式筆試題:想讓程式跳轉到絕對地址0x100000處執行,該如何做?

網上看到有如下答案:

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

經過在VC++6.0和LINUX gcc4.4.3下測試,均不能通過編譯。

VC++6.0報錯:error C2100: illegal indirection

GCC報錯:error: void value not ignored as it ought to be

應該是怎麼寫呢?

經過測試,有兩種方法:

答案1.    (*(void(*)(void))0x100000)();

答案2.    ((void(*)(void))0x100000)();

仔細觀察,第一種寫法只是第一個*的位置不同,第二種寫法少了一個*,但是都能正確編譯通過,且正確執行。

為什麼會有這兩種答案呢?查閱資料後發現,與歷史原因有關……

先來看看如下例子:

例一:

#include <stdio.h>
void func(void)
{
	printf("hello.\n");
}
void main(void)
{
	printf("func=%d\n", func);
	printf("&func=%d\n", &func);
}

執行程式後發現


兩次列印結果相同!!!

按照&運算子本來的意義,它要求其運算元是一個物件,但函式名不是物件(函式是一個物件),本來&func是非法的,但很久以前有些編譯器已經允許這樣做,c/c++標準的制定者出於物件的概念已經有所發展的緣故,也承認了&func的合法性。
因此,對於func和&func可以這樣理解,func是函式的首地址,它的型別是void (),&func表示一個指向函式void func(void)這個物件的地址,它的型別是void (*)(),因此func和&func所代表的地址值是一樣的,但型別不一樣。func是一個函式,&func表示式的值是一個指標!

既然取不取址都可以,那麼*不*也都可以……

所以,在呼叫一個函式的時候,也有兩種方法,正如前面的兩種答案。

例二:

#include <stdio.h>

void func(void)
{
	printf("hello.\n");
}

void main()
{
	void (*func_p)(void) = func;		//定義一個函式指標,這個指標無返回值,無引數,指向fun函式
	
	(*func_p)();
	(func_p)();
}
上面的兩種呼叫方法也都是正確的,編譯通過,正確執行。


其實,

func_p();

也是正確的呼叫方式……

更有甚者

(*func)();	
還是正確的……只是平時不這麼用罷了(注意此處是func,不是func_p)

暫且不考慮那麼多呼叫方式(知道就好了),現在回過頭來看看

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

((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();也應該對呀?

但是,實際測試,編譯報錯:error C2064: term does not evaluate to a function

為啥呢?我也不知道了……反正不管有沒有 * ,記得加個括號就好了……

那為什麼最開始的*((void(*)(void)0x100000))();不對呢?

因為沒見過*func(); 這麼用的……

如果要是外面再加一層括號就對了,如:(*(void(*)(void)0x100000))) ();

其實,把藍色括號去掉(藍色括號和綠色括號重複了),就又變成答案一了……

所以無論如何,最外層不能是* ,必須是括號!

因為沒見過 *func(); 這麼用的……