新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Keil C51 中的函數指針和再入函數

        Keil C51 中的函數指針和再入函數

        作者: 時間:2016-11-27 來源:網絡 收藏
        概述

        函數指針是C語言中幾個難點之一。由于8051的C編譯器的獨特要求,函數指針和再入函數有更多的挑戰需要克服。主要由于函數變量的傳遞。

        本文引用地址:http://www.104case.com/article/201611/322385.htm

        典型的(絕大部分8051芯片)函數變量通過堆棧的入棧和出棧命令來傳遞。因為8051只有有限的堆棧空間(128字節或更少的64字節),函數變量必須通過不同的方式進行傳遞。

        8051的PL/M-51編譯器,介紹在固定的存儲空間存儲變量的方式。當使用連接器時,程序建立一個調用樹,計算出函數變量的互斥空間,然后覆蓋它們。這就是連接器的“OVERLAY”指令。

        因為PL/M-51不支持函數指針,所以不能實現間接函數調用。然而,C語言中存在這樣的問題。連接器知道哪塊空間用于存儲間接函數的變量。怎樣間接加入函數進入調用樹?

        本文解釋在C51編程中,怎樣有效使用函數指針。特別地,討論如下幾個話題:

        分配常量地址給一個指針;

        定義函數指針;

        C51中函數指針問題;

        使用OVERLAY指令確定調用樹;

        再入函數的指針;

        固定地址的指針

        你很容易的給函數指針分配一個數字地址。有許多原因需要這樣做。例如,你需要復位目標。你可以設置函數指針為0000H去實現。

        你可以使用標準C語言的類型映射特點,映射0X0000指針指向地址0的函數。例如,當你編譯如下C代碼….

        ((void (code *) (void))0x0000) ();

        …編譯器產生如下如下代碼:

        ;FUNCTION main (BEGIN)

        ;SOURCELINE#3

        0000120000LCALL00H

        ;SOURCELINE#4

        000322RET

        ; FUNCTION main (END)

        這正是我們期望的:LCALL0

        把一個數字常量映射成一個函數指針是一件很復雜的事情。下面關于上面的函數調用的各部分的描述,將幫助你怎樣更好的使用它們。

        在上面的函數調用中,(void ( *) (void))是數據類型:一個不帶參數且返回void的函數指針。

        0x0000是一個映射地址。經過類型映射,函數指針指向地址0x0000。注意我們把一個圓括號放在數據類型和0x0000后面。如果我們僅僅想映射0x0000成為函數指針,這是不必要的。然而,因為我們將引用這個函數,這些圓括號是必要的。

        映射一個數值常量成為指針和通過指針調用函數是不同的。為了實現這個,我們必須指定一個變量表。這就是為什么在此行的后面有一個()。

        注意上面表達式中的所有圓括號都是必須的。分組和優先級是很重要的。

        上面不帶參數的函數指針和帶參數的函數指針的唯一不同是數據類型和變量列表。例如,下面的函數調用…..

        ((long (code *) (int int int ) 0x8000)(1,2,3);

        聲明一個函數,地址在0x8000,接收3個int型參數,返回long型結果。

        不帶參數的函數指針

        指向函數的函數指針是可變的。函數的地址是一個可變的數值。例如,下面的函數指針的聲明….

        void (*function_ptr) (void);

        是一個調用function_ptr的指針。使用下面的代碼調用function_ptr函數。

        (*function_ptr ) ();

        因為函數沒有參數傳送,所以參數列表時空的。

        當定義變量的時候,函數指針可以被分配地址:void (*function_ptr) (void) = another_fuction;或者在程序執行過程中被分配,function_ptr = another_fuction;

        注意,必須分配一個地址給函數指針。如果沒有分配,函數指針將有一個0值(如果你運氣好),或者有一些你完全不知道的數值,依賴于你的數據存儲區的使用情況。當你間接的調用一個函數通過函數指針,如果函數指針沒有初始化,你的程序將是混亂的。

        為了聲明一個帶返回值的函數指針,在聲明過程中你必須指定返回值的數據類型。例如,下面的聲明改變了上面的函數指針的聲明,返回一個float 數據。

        float(*function_ptr) (void) = another_fuction;

        帶參數的函數指針

        帶參數的函數指針與不帶參數的函數指針是相似的。例如:

        void (*function_ptr) (int, long,char); 一個函數指針,帶一個int參數,帶一個long參數,帶一個char參數。使用下面的代碼調用函數。

        (*function_ptr) (12, 34L,‘A’);

        注意,函數指針僅僅可以指向小于等于3個參數的函數。這是因為,間接調用函數時,參數必須保存在寄存器中。關于超過3個參數的函數指針的信息,在再入函數中介紹。

        使用函數指針的附加說明

        如果你在C51中使用函數指針編程,有幾個附加的說明你必須注意。

        參數列表的限制

        通過函數指針傳遞參數給函數必須把所有的參數存入寄存器。在大部分情況下,3個參數能夠自動通過寄存器傳遞。在C51的用戶手冊中能找到傳遞參數進入寄存器的運算法則。但是并不保證,任何的3個數據類型可以傳遞。

        因為C51在寄存器中傳遞3個參數,用于傳遞參數的存儲空間是不被分配的,除非函數指向一個要求更多參數的函數。如果在那樣的情況下,可以把參數混入一個結構體中,然后通過一個結構體指針傳遞參數。如果這樣不可接受,你可以使用再入函數(看下面)。

        調用樹的保存

        C51不把函數參數壓棧(除非使用再入函數)。函數參數和全局變量被存入寄存器或固定的存儲空間。這樣阻止函數的再入。例如,一個函數調用它自己,它將覆蓋它自己的參數或存儲空間。函數的再入問題通過關鍵字“reentrant”來解決。函數指針的非再入函數的副作用,在執行中出現問題。

        為了保護盡量多的數據空間,連接器執行調用樹的性能分析,決定一些存儲空間被安全的覆蓋。例如,如果你的應用中包含main 函數,函數a,函數b,函數c,并且main函數調用a,b,c,但是a,b,c之間沒有互相調用。在你應用中的調用樹見出現如下:

        MAIN

        +→ A

        +→ B

        +→ C

        這樣A,B,C的存儲空間可以被安全的覆蓋。

        當調用樹不能正確的建立,函數指針將帶來問題。因為連接器不能決定函數之間的引用。在這個問題上,沒有自動的解決方法。


        上一頁 1 2 3 下一頁

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 海口市| 永兴县| 紫云| 定远县| 通辽市| 麟游县| 高淳县| 探索| 新建县| 长宁县| 精河县| 长汀县| 石家庄市| 芜湖市| 辽宁省| 德惠市| 玛纳斯县| 淅川县| 宁阳县| 芜湖县| 锡林浩特市| 普陀区| 清流县| 沧源| 泸州市| 峡江县| 海淀区| 团风县| 图们市| 宜兰市| 江门市| 武强县| 黄冈市| 江达县| 伊金霍洛旗| 南澳县| 城口县| 堆龙德庆县| 乌海市| 瑞安市| 双城市|