新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Keil C51 中的函數(shù)指針和再入函數(shù)

        Keil C51 中的函數(shù)指針和再入函數(shù)

        作者: 時間:2016-11-27 來源:網(wǎng)絡(luò) 收藏

        下面兩個源文件將解答這個問題,使問題容易明白。第一個源文件FPCALLER.C,包括一個函數(shù),它通過一個函數(shù)指針(fptr)調(diào)用另一個函數(shù)。

        voidfunc_caller(long (code *fptr) (unsigned int))

        {

        unsigned char i;

        for(i=0;i<10;i++)

        {

        (*ftpr)(i);

        }

        }

        第二個源文件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) ;

        }

        上面的兩個的源文件編譯和鏈接都沒有錯誤。通過連接器,調(diào)用樹的映射文件如下:

        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

        在這個簡單的例子中,許多信息可以從調(diào)用樹里挖掘出來。?C_C51STARTUP段調(diào)用main函數(shù)的?PR?MAIN?FPMAIN,段名各部分解析:PR是代碼存儲區(qū),MAIN是函數(shù)名,F(xiàn)PMAIN是定義函數(shù)所在的源文件名。

        MAIN函數(shù)調(diào)用FUNC和FUNC_CALLER(根據(jù)調(diào)用樹)。注意這是錯誤的。MAIN函數(shù)沒有調(diào)用FUNC函數(shù),但是它傳遞FUNC函數(shù)的地址給FUNC_CALLER函數(shù)。同時注意,根據(jù)調(diào)用樹FUNC_CALLER沒有調(diào)用FUNC。這是因為FUNC_CALLER是通過函數(shù)指針間接調(diào)用FUNC。

        FPMAIN文件中的FUNC函數(shù)使用從0008H開始,長000AH字節(jié)的數(shù)據(jù)。FPCALLER文件中的FUNC_CALLER函數(shù)也使用從0008H開始,長0003H字節(jié)的數(shù)據(jù)。這是重要的。

        FUNC_CALLER函數(shù)使用的存儲區(qū)從0008H開始,F(xiàn)UNC函數(shù)使用的存儲區(qū)也是從0008H開始。因為FUNC_CALLER函數(shù)調(diào)用FUNC函數(shù),又因為兩個函數(shù)使用相同的存儲區(qū),這樣就產(chǎn)生了問題。當FUNC函數(shù)被FUNC_CALLER函數(shù)調(diào)用時,存儲區(qū)將被FUNC_CALLER破壞。這個問題是怎樣產(chǎn)生的?是由Keil 51編譯器產(chǎn)生還是由連接器產(chǎn)生?

        這個問題的原因是函數(shù)指針。當你使用函數(shù)指針時,你將總是遇到這樣的問題。幸運的是,他們是容易被修改的。“OVERLAY”指令讓你指定在調(diào)用樹中,函數(shù)與其他函數(shù)是怎樣連接的。

        為了修正上面顯示的調(diào)用樹,F(xiàn)UNC函數(shù)必須從MAIN函數(shù)中刪除,同時FUNC函數(shù)必須插入到FUNC_CALLER函數(shù)中。下面用“OVERLAY”指令修改后如下:

        OVERLAY (?PR?MAIN?FPMAIN ~ ?PR?_FUNC?FPMAIN,

        ?PR?_FUNC_CALLER?FPCALLER ! ?PR?_FUNC?FPMAIN)

        為了刪除或插入相關(guān)的進入調(diào)用樹,指定第一調(diào)用和第二調(diào)用。“~”符號用于刪除相關(guān)的函數(shù),“!”用于插入一個外部函數(shù)。例如?PR?MAIN?FPMAIN ~ ?PR?_FUNC?FPMAIN,意義是從MAIN函數(shù)中刪除FUNC函數(shù)的調(diào)用。

        經(jīng)過調(diào)整連接命令,包括用“OVERLAY”指令修正調(diào)用樹,調(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)用樹中,F(xiàn)UNC_CALLER函數(shù)和FUNC函數(shù)使用獨立存儲空間。

        函數(shù)指針列表

        下面是一個典型的函數(shù)指針列表的定義:

        long (code *fp_tab []) (void) = { func1, func2, func3 };

        如果你的MAIN函數(shù)中通過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

        三個函數(shù)通過列表被調(diào)用,F(xiàn)UNC1,F(xiàn)UNC2 和FUNC3被C_INITSEG調(diào)用。但是這是錯誤的,C_INITSEG按照常規(guī)的方式在程序中初始化。這些函數(shù)被引入初始化代碼中,因為函數(shù)指針列表被初始化成這些函數(shù)的地址值。

        注意這些變量(FUNC1,F(xiàn)UNC2 和FUNC13)和MAIN函數(shù)的起始地址都是0008H。這樣不能正常工作,因為MAIN函數(shù)調(diào)用FUNC1,F(xiàn)UNC2 和FUNC3(通過函數(shù)指針類表)。

        C51編譯器和BL51連接器聯(lián)合工作,當使用函數(shù)指針列表時,使得函數(shù)變量空間覆蓋很容易。但是,你必須合理的聲明指針列表。如果你這樣做了,就可以避免使用“OVERLAY”指令。下面的函數(shù)指針列表的定義,C51和BL51可以自動處理:

        code long (code *fp_tab []) (void) = { func1, func2, func3 };

        注意唯一不同的是存儲列表在CODE空間。現(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)在,初始化代碼中沒有引入FUNC1,F(xiàn)UNC2 和FUNC3。但是,MAIN函數(shù)中引入一個常數(shù)段FP_TAB。這是一個函數(shù)指針列表。因為函數(shù)指針列表引入了FUNC1,F(xiàn)UNC2 和FUNC3,所以調(diào)用樹是正確的。

        只要把函數(shù)指針列表放在一個獨立的源文件中,在調(diào)用樹中,C51和BL51就能正確的連接。



        評論


        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 治县。| 揭东县| 甘孜县| 张掖市| 定边县| 大石桥市| 连州市| 翼城县| 公主岭市| 怀集县| 定边县| 策勒县| 南郑县| 葵青区| 陇西县| 萨迦县| 苍溪县| 宜州市| 衡山县| 玉环县| 汝州市| 北辰区| 福海县| 雷州市| 奉贤区| 城固县| 萍乡市| 东乡族自治县| 龙南县| 阜城县| 新密市| 清河县| 新宁县| 焉耆| 洞头县| 余庆县| 策勒县| 伊金霍洛旗| 临澧县| 顺义区| 仪征市|