C51函數(shù)的遞歸調用
前幾天在寫C51程序時用到了遞歸,簡單程序如下:
voidWRITE_ADD(ucharaddr,ucharwbyte){START();//先發(fā)送起始信號WRITE_BYTE(0xa0);//設備地址+W命令if(!ERROR_Flag)//正確收到應答{WRITE_BYTE(addr);//寫入地址}else{ERROR_Flag=0;//清錯誤標志WRITE_ADD(addr,wbyte);//重新寫入}if(!ERROR_Flag)//地址收到正確應答{WRITE_BYTE(wbyte);//發(fā)送要寫入的數(shù)據(jù)}else{ERROR_Flag=0;//清錯誤標志WRITE_ADD(addr,wbyte);//重新寫入}if(!ERROR_Flag)//正確收到應答{STOP();//停止}else{ERROR_Flag=0;//清錯誤標志WRITE_ADD(addr,wbyte);//重新寫入}}
編譯時出現(xiàn)如下警告:
warning C265: '_WRITE_ADD': recursive call to non-reentrant function(循環(huán)調用了非可重入函數(shù))。
經過查找資料之后,解決方法是在函數(shù)后加入關鍵字,使函數(shù)變成可重入函數(shù):
返回值 函數(shù)名(形參) reentrant
上面的函數(shù)是有錯誤的,可重入函數(shù)不能傳遞bit類型的變量。在多任務系統(tǒng)中,可重入函數(shù)也不要用全局變量,多個函數(shù)同時調用時可能會使變量出現(xiàn)多個值,但是在單任務系統(tǒng)中,個人認為某些時候下是可以利用的。只要不出現(xiàn)改變變量值的情況。
一、可重入函數(shù)
首先對重入函數(shù)進行一下說明。
可重入函數(shù)主要應用在多任務環(huán)境中,一個可重入的函數(shù)簡單來說就是可以被中斷的函數(shù)。也就是說這個函數(shù)執(zhí)行的任何時刻中斷它,轉入另一段代碼,返回控制時不會出現(xiàn)什么錯誤,而不可重入的函數(shù)由于使用了系統(tǒng)資源,比如全局向量、中斷向量表等,如果函數(shù)被中斷的話可能會發(fā)生錯誤。
在Keil手冊中對可重入函數(shù)的解釋為:一個可重入函數(shù)可以在同一時間被幾個進程共享。當一個函數(shù)可重入運行時,別的進程可中斷執(zhí)行,并開始執(zhí)行相同的可重入函數(shù)。正常情況,C51編譯器重的函數(shù)不能重入。原因是函數(shù)的參數(shù)和局部變量保存在固定的存儲區(qū)中。通過reentrant函數(shù)屬性允許聲明函數(shù)可重入,因此可重復調用??芍厝牒瘮?shù)可以被遞歸調用,可同時被兩個或多個進程調用??芍厝牒瘮?shù)經常在實時應用或者在中斷和非中斷必須共用一個函數(shù)的情況下被使用。如果函數(shù)定義為屬性reentrant,那么這個可重入函數(shù),一個可重入的堆棧區(qū)同時在內部和外部存儲區(qū)模擬,這是由存儲模式來決定的。如果是SMALL模式,則在idata存儲區(qū)模擬可重入堆棧。如果是COMPACT模式,那么在pdata存儲區(qū)模擬可重入函數(shù)堆棧。如果是LARGE模式可重入函數(shù)在xdata存儲區(qū)模擬可重入堆棧。
二、可重入函數(shù)與函數(shù)的可重入
對此的詳細解釋引自:http://www.keil.com/support/docs/1873.htm
可重入函數(shù)與函數(shù)的可重入是兩個不同的概念。
在C51中如果我們定義以下函數(shù):
int function(int a, int b, int c) compact reentrant
{
long x, y, z;
....
}
由于聲明為reentrant屬性,因此函數(shù)為可重入函數(shù),其參數(shù)(a,b,c)和局部變量(x,y,z)存儲在模擬堆棧(simulated stack),由于是compact模式,因此在pdata區(qū)。
如果沒有特意的聲明compact,則會默認的為small,會在idata區(qū)模擬堆棧。如果是large則會在xdata區(qū)。這是可重入函數(shù)。(單片機的“硬件?!?,其實只有一個,就是我們通常說的SP,它是在內部RAM中的)
但有些函數(shù)未聲明為reentrant,但是可重入的。大多是以匯編來編寫的,其參數(shù)和局部變量存儲在寄存器中(data),在C51的庫函數(shù)中有很多這樣的函數(shù),它們是可重入的,但未用reentrant聲明。
三、解釋
普通的函數(shù)的形參和局部變量的存儲是存在全局變量區(qū)(在《全局變量和局部變量存儲》中詳細的講解),在遞歸調用的時候上一層次的局部變量會被本層次調用沖掉。通過reentrant,編譯器會形成模擬棧為形參和局部變量分配內存。如果函數(shù)遞歸或者嵌套的次數(shù)太多,也會發(fā)生棧溢出(對于該模擬棧的大小可以在STARTUP.A51中修改)。
對于重入函數(shù)的模擬棧與單片機內的棧不同,模擬棧是由最頂端往下遞減的,而sp則是grow up的。
在函數(shù)的遞歸或者通過函數(shù)指針調用函數(shù)時,如果被調用的函數(shù)中有字符串常量,有時會提示“WARNING13:RECURSIVECALLTOSEGMENT”
其具體的解決方法見轉載文章“Keil"RECURSIVECALLTOSEGMENT"徹底解決”。