下面兩個(gè)源文件將解答這個(gè)問(wèn)題,使問(wèn)題容易明白。第一個(gè)源文件FPCALLER.C,包括一個(gè)函數(shù),它通過(guò)一個(gè)函數(shù)指針(fptr)調(diào)用另一個(gè)函數(shù)。
voidfunc_caller(long (code *fptr) (unsigned int))
{
unsigned char i;
for(i=0;i<10;i++)
{
(*ftpr)(i);
}
}
第二個(gè)源文件FPMAIN.C,包含C主函數(shù)和被func_caller調(diào)用的函數(shù)func。注意main函數(shù)調(diào)用func_caller,把func的地址作為參數(shù)傳遞給func_caller。
extern void func_caller (long (code *) (unsigned int));
int func (unsigned int count)
{
long j;
long k;
k = 0;
for (j = 0; j < count; j++)
{
k += j;
}
return (k);
}
void main (void)
{
func_caller (func);
while (1) ;
}
上面的兩個(gè)的源文件編譯和鏈接都沒(méi)有錯(cuò)誤。通過(guò)連接器,調(diào)用樹(shù)的映射文件如下:
SEGMENTDATA_GROUP
+--> CALLED SEGMENTSTARTLENGTH
-------------------------------------------------
?C_C51STARTUP----------
+--> ?PR?MAIN?FPMAIN
?PR?MAIN?FPMAIN----------
+--> ?PR?_FUNC?FPMAIN
+--> ?PR?_FUNC_CALLER?FPCALLER
?PR?_FUNC?FPMAIN0008H000AH
?PR?_FUNC_CALLER?FPCALLER0008H0003H
在這個(gè)簡(jiǎn)單的例子中,許多信息可以從調(diào)用樹(shù)里挖掘出來(lái)。?C_C51STARTUP段調(diào)用main函數(shù)的?PR?MAIN?FPMAIN,段名各部分解析:PR是代碼存儲(chǔ)區(qū),MAIN是函數(shù)名,F(xiàn)PMAIN是定義函數(shù)所在的源文件名。
MAIN函數(shù)調(diào)用FUNC和FUNC_CALLER(根據(jù)調(diào)用樹(shù))。注意這是錯(cuò)誤的。MAIN函數(shù)沒(méi)有調(diào)用FUNC函數(shù),但是它傳遞FUNC函數(shù)的地址給FUNC_CALLER函數(shù)。同時(shí)注意,根據(jù)調(diào)用樹(shù)FUNC_CALLER沒(méi)有調(diào)用FUNC。這是因?yàn)镕UNC_CALLER是通過(guò)函數(shù)指針間接調(diào)用FUNC。
FPMAIN文件中的FUNC函數(shù)使用從0008H開(kāi)始,長(zhǎng)000AH字節(jié)的數(shù)據(jù)。FPCALLER文件中的FUNC_CALLER函數(shù)也使用從0008H開(kāi)始,長(zhǎng)0003H字節(jié)的數(shù)據(jù)。這是重要的。
FUNC_CALLER函數(shù)使用的存儲(chǔ)區(qū)從0008H開(kāi)始,F(xiàn)UNC函數(shù)使用的存儲(chǔ)區(qū)也是從0008H開(kāi)始。因?yàn)镕UNC_CALLER函數(shù)調(diào)用FUNC函數(shù),又因?yàn)閮蓚€(gè)函數(shù)使用相同的存儲(chǔ)區(qū),這樣就產(chǎn)生了問(wèn)題。當(dāng)FUNC函數(shù)被FUNC_CALLER函數(shù)調(diào)用時(shí),存儲(chǔ)區(qū)將被FUNC_CALLER破壞。這個(gè)問(wèn)題是怎樣產(chǎn)生的?是由Keil 51編譯器產(chǎn)生還是由連接器產(chǎn)生?
這個(gè)問(wèn)題的原因是函數(shù)指針。當(dāng)你使用函數(shù)指針時(shí),你將總是遇到這樣的問(wèn)題。幸運(yùn)的是,他們是容易被修改的。“OVERLAY”指令讓你指定在調(diào)用樹(shù)中,函數(shù)與其他函數(shù)是怎樣連接的。
為了修正上面顯示的調(diào)用樹(shù),F(xiàn)UNC函數(shù)必須從MAIN函數(shù)中刪除,同時(shí)FUNC函數(shù)必須插入到FUNC_CALLER函數(shù)中。下面用“OVERLAY”指令修改后如下:
OVERLAY (?PR?MAIN?FPMAIN ~ ?PR?_FUNC?FPMAIN,
?PR?_FUNC_CALLER?FPCALLER ! ?PR?_FUNC?FPMAIN)
為了刪除或插入相關(guān)的進(jìn)入調(diào)用樹(shù),指定第一調(diào)用和第二調(diào)用。“~”符號(hào)用于刪除相關(guān)的函數(shù),“!”用于插入一個(gè)外部函數(shù)。例如?PR?MAIN?FPMAIN ~ ?PR?_FUNC?FPMAIN,意義是從MAIN函數(shù)中刪除FUNC函數(shù)的調(diào)用。
經(jīng)過(guò)調(diào)整連接命令,包括用“OVERLAY”指令修正調(diào)用樹(shù),調(diào)整后的映射文件如下:
SEGMENTDATA_GROUP
+--> CALLED SEGMENTSTARTLENGTH
-------------------------------------------------
?C_C51STARTUP----------
+--> ?PR?MAIN?FPMAIN
?PR?MAIN?FPMAIN----------
+--> ?PR?_FUNC_CALLER?FPCALLER
?PR?_FUNC_CALLER?FPCALLER0008H0003H
+--> ?PR?_FUNC?FPMAIN
?PR?_FUNC?FPMAIN000BH000AH
修正后的調(diào)用樹(shù)中,F(xiàn)UNC_CALLER函數(shù)和FUNC函數(shù)使用獨(dú)立存儲(chǔ)空間。
函數(shù)指針列表
下面是一個(gè)典型的函數(shù)指針列表的定義:
long (code *fp_tab []) (void) = { func1, func2, func3 };
如果你的MAIN函數(shù)中通過(guò)fp_tab調(diào)用歌函數(shù),連接映射文件出現(xiàn)如下:
SEGMENTDATA_GROUP
+--> CALLED SEGMENTSTARTLENGTH
----------------------------------------------
?C_C51STARTUP----------
+--> ?PR?MAIN?FPT_MAIN
+--> ?C_INITSEG
?PR?MAIN?FPT_MAIN0008H0001H
?C_INITSEG----------
+--> ?PR?FUNC1?FP_TAB
+--> ?PR?FUNC2?FP_TAB
+--> ?PR?FUNC3?FP_TAB
?PR?FUNC1?FP_TAB0008H0008H
?PR?FUNC2?FP_TAB0008H0008H
?PR?FUNC3?FP_TAB0008H0008H
三個(gè)函數(shù)通過(guò)列表被調(diào)用,F(xiàn)UNC1,F(xiàn)UNC2 和FUNC3被C_INITSEG調(diào)用。但是這是錯(cuò)誤的,C_INITSEG按照常規(guī)的方式在程序中初始化。這些函數(shù)被引入初始化代碼中,因?yàn)楹瘮?shù)指針列表被初始化成這些函數(shù)的地址值。
注意這些變量(FUNC1,F(xiàn)UNC2 和FUNC13)和MAIN函數(shù)的起始地址都是0008H。這樣不能正常工作,因?yàn)镸AIN函數(shù)調(diào)用FUNC1,F(xiàn)UNC2 和FUNC3(通過(guò)函數(shù)指針類表)。
C51編譯器和BL51連接器聯(lián)合工作,當(dāng)使用函數(shù)指針列表時(shí),使得函數(shù)變量空間覆蓋很容易。但是,你必須合理的聲明指針列表。如果你這樣做了,就可以避免使用“OVERLAY”指令。下面的函數(shù)指針列表的定義,C51和BL51可以自動(dòng)處理:
code long (code *fp_tab []) (void) = { func1, func2, func3 };
注意唯一不同的是存儲(chǔ)列表在CODE空間?,F(xiàn)在,連接映射文件如下:
SEGMENTDATA_GROUP
+--> CALLED SEGMENTSTARTLENGTH
----------------------------------------------
?C_C51STARTUP----------
+--> ?PR?MAIN?FPT_MAIN
?PR?MAIN?FPT_MAIN0008H0001H
+--> ?CO?FP_TAB
?CO?FP_TAB----------
+--> ?PR?FUNC1?FP_TAB
+--> ?PR?FUNC2?FP_TAB
+--> ?PR?FUNC3?FP_TAB
?PR?FUNC1?FP_TAB0009H0008H
?PR?FUNC2?FP_TAB0009H0008H
?PR?FUNC3?FP_TAB0009H0008H
現(xiàn)在,初始化代碼中沒(méi)有引入FUNC1,F(xiàn)UNC2 和FUNC3。但是,MAIN函數(shù)中引入一個(gè)常數(shù)段FP_TAB。這是一個(gè)函數(shù)指針列表。因?yàn)楹瘮?shù)指針列表引入了FUNC1,F(xiàn)UNC2 和FUNC3,所以調(diào)用樹(shù)是正確的。
只要把函數(shù)指針列表放在一個(gè)獨(dú)立的源文件中,在調(diào)用樹(shù)中,C51和BL51就能正確的連接。
評(píng)論