通過函式的入口位址來呼叫函式

2021-08-20 16:16:27 字數 3696 閱讀 2739

例程:

int i; //定義乙個測試變數

void test() //定義乙個函式

int main()

//經常見到嵌入式設計中,將某一程式段的入口位址轉換為乙個函式,我們來分析一下它的成分:

如在bootloader的0x00000020位址上的雙字單元中有這樣一條語句:

@address is 0x00000020

bpowerdown         @jump to the flag "powerdown"

......

powerdown:

......

然後在某一c標頭檔案中可以見到這樣的巨集宣告:

#define enterpwdn(clkcon) ((void (*)(int))0x20)(clkcon)

初一看,亂七八糟的,現在,我們來整理一下。

不難看出,當我們程式設計呼叫enterpwdn(clkcon)函式的時候,編譯器在編譯前首先把enterpwdn(clkcon)轉換為

((void (*)(int))0x20)(clkcon)語句。

對於這個語句,我們將之分解成3部分來看。

1:(void (*)(int)

2:0x20

3:(clkcon)

(clkcon)是函式的引數,就不用說了。0x20呢?當然就是要轉換的函式的入口位址了。那對於(void(*)(int)呢??呵呵……,這一部分作為乙個整體,描述了轉換後的函式的型別,即無返回值,帶乙個整形引數。而中間那個」(*)「,就表示要轉換成乙個函式(或者說把0x20轉換為乙個位址,因為在arm彙編中,就把c語言的函式名當作乙個位址標號看待了)。就像我們平常用的強制型別轉換一樣,(int)temp,只不過這裡是將乙個數轉換為另一型別,而那是將乙個位址轉換為乙個函式罷了。

關於arm中:#define enterpwdn(clkcon)((void (*)(int))0x00000020)(clkcon)

1。首先要知道帶引數的巨集定義。定義方法如下:

#define 巨集名(引數) 字串

如下:#define add(a,b) (a)+(b)

那麼:程式中的 b=add((2*3),(4*5)) 等價於 b=(2*3)+(4*5) 。

具體請參考c語言書籍。

2。下面看:enterpwdn(0x7fff4);

會被替換成:((void (*)(int))0x00000020)(0x7fff4);

這個涉及到函式指標,函式指標的用法如下:

void fun(int x);

void(*pfun) (int x);

pfun=fun;

那麼呼叫函式fun可以這樣寫:a. fun(5); b. (*pfun)(5); 

則:((void(*)(int))0x00000020)(0x7fff4);中藍色0x7fff4是傳遞的引數,紅色(void(*)(int))0x00000020是函式名。其實函式名本身就是乙個位址。像上式中,如果你知道函式fun存在位址0x012345,那麼理論上你可以這麼寫:0x012345(5),當然實際上不能這麼寫,因為你雖然fun在0x012345位址,但是你編譯器不知到這個函式的引數、返回值等資訊。所以要強制轉換一下。

如果把 char a ; 中的a轉換成 int  形變數,要這樣:b=(int)a;

同樣要把0x00000020位址轉換為函式名,也要強制轉換,只要在0x00000020的前面加上要轉換的型別。(void(*)(int))就是函式型別,和void (*pfun) (int x); // 宣告乙個函式指標 是相對應的。

3。再看一下enterpwdn(0x7fff4);呼叫之後具體的引數傳遞,這個涉及到c和彙編的相互呼叫。

看__entry

resetentry

b resethandler

b handlerundef ;handler forundefined mode

b handlerswi ;handler for swiinterrupt

b handlerpabort ;handler forpabort

b handlerdabort ;handler fordabort

b .  ;reserved

b handlerirq ;handler for irqinterrupt

b handlerfiq ;handler for fiqinterrupt

;@0x20

b enterpwdn ; must be @0x20.

跳到0x00000020位址後會接著跳轉到標號enterpwdn 處。

enterpwdn

mov r2,r0  ;r2=rclkcon

tst r0,#0x8  ;sleep mode?

bne enter_sleep

看這裡第一句:mov r2,r0  。其實 r0中存的就是引數0x7fff4,這個涉及到c呼叫彙編,自己找一下參考書吧,我就不獻醜了。

arm中#define enterpwdn(clkcon) ((void(*)(int))0x20)(clkcon))代表什麼意思

1.首先,這是乙個帶引數巨集定義,所以,如果有語句enterpwdn(0x7fff4)則展開以後就是如下語句 ((void(*)(int))0x20)(0xffff4))

2.(void(*)(int))0x20的意思

(void(*)(int))作為乙個強制型別轉換,將0x20轉換為乙個函式指標型別,這個函式的原型為 voidfun(int); 而後面的0xffff4就是代表的函式的引數,所以 ((void(*)(int))0x20)(0xffff4))整句話的意思就是呼叫位址在0x20處的函式,引數為0xffff4.其實看後面實際上就是跳轉到位址0x20處執行**,並且把引數傳到r0,atpcs中規定1-4個引數對應的儲存暫存器為r0-r3。

3.再看在2440init.s中對該部分的定義

b resethandler

b handlerundef ;handler for undefined mode

b handlerswi ;handler for swi interrupt

b handlerpabort ;handler for pabort

b handlerdabort ;handler for dabort

b .   ;reserved

b handlerirq ;handler for irq interrupt

b handlerfiq ;handler for fiq interrupt

;@0x20

b enterpwdn ; must be @0x20.

enterpwdn

mov r2,r0   ;r2=rclkcon

tst r0,#0x8   ;sleep mode?

bne enter_sleep

b enterpwdn ; must be @0x20.這條語句,對應的指令位址為0x20,這就是前面((void(*)(int))0x20)中0x20的意義,然後語句跳到enterpwdn執行,單然,引數值就傳給了r0,後面的東西檢視硬體說明。

函式 入口位址 返回位址

2009 07 30 17 41 44 分類 c語言學習 標籤 字型大小 大中小訂閱 陣列的名字代表的是陣列的首位址 函式的名字代表的就是函式的入口位址。可以列印一下 include void hello world void printf hello world n void hello baby...

python通過字串來呼叫函式

有時候我們想要通過字串來直接呼叫函式,方便通過輸入的引數來直接控制呼叫的函式 常規操作def function1 print function1 def function2 print function2 def function3 print function3 def call fun by s...

通過函式指標呼叫函式

指標可以不但可以指向乙個整形,浮點型,字元型,字串型的變數,也可以指向相應的陣列,而且還可以指向乙個函式。乙個函式在編譯的時候會被分配給乙個入口位址。這個函式入口位址稱為函式的指標。可以用乙個指標變數指向函式,然後通過該指標變數呼叫此函式。定義指向函式的指標變數的方法是 int p int int ...