原文鏈接:https://www.cnblogs.com/jqdy/p/14476285.html
代碼中用到了函數指針,該函數指針有一個參數,參數類型也是一個指針,見下面代碼片段的第5行紅色部分。使用Keil編譯時出現了“error C212: indirect call: parameters do not fit within registers”錯誤。
1 //代碼片段 2 typedef struct thisDisplay{ 3 unsigned char *buffer; 4 //其他屬性和方法。。。。 5 void(* Updata)(struct thisDisplay*, unsigned char *);//一次性更新緩沖區所有內容 6 }Display_c; 7 8 unsigned char buffer[10]; 9 10 Display_c xdata g_Display = 11 { 12 buffer, 13 //其他屬性方法的初始化。。。。 14 DisplayClass_Updata 15 }; 16 17 static void DisplayClass_Updata(Display_c* _this, unsigned char* p) 18 { 19 unsigned char i = 0; 20 do{ 21 _this->buffer[i] = *p; 22 }while (++i < DISPLAY_BUFFER_SIZE); 23 } 24 25 //以上為類定義部分,下面為該類的一個實例,執行updata方法 26 g_Display.Updata(&g_Display, pStr); 27 28 //編譯時出現的錯誤提示,指向第26行的代碼 29 //error C212: indirect call: parameters do not fit within registers
Keil官網中有一篇文章詳細解釋了該問題。解決方法有兩種,一種是將這個函數指針說明為可重入函數(reentrant);另一種是根據參數指針指向的存儲空間類型,給該參數指針加上相應的類型限制,以便編譯器正確翻譯成相應的匯編代碼。
經過嘗試,這兩種方法都可解決該問題。
但是,方法一會導致另一種錯誤,並且錯誤的位置不固定,去掉幾條代碼編譯時指示某個函數錯誤,加上幾條代碼又指示另一個函數錯誤,反復檢查代碼又不存在問題。錯誤提示為:error l121: improper fixup。檢索后大概原因是將函數標記位 reentrant 后,會導致調用函數時將更多的信息壓入棧區,而8051的棧空間過小,從而導致該錯誤。
方法二是個不錯的選擇,如果該參數指針指向 xdata 區,只需要在函數聲明時,給該指針參數前加上 xdata 修飾即可。上面代碼片段第5行相應位置添加 xdata 即可:
void(* Updata)(struct thisDisplay xdata *, unsigned char xdata *);//一次性更新緩沖區所有內容
Mark:又發現當函數指針的參數是 bit 類型時也會出現這種錯誤提示,將 bit 類型改成 unsigned char 類型即可解決該問題。有興趣可以讀一讀 《C51/Cx51編譯器用戶指南》第162、163頁的 Parameter Passing in Registers 和 Parameter Passing in Fixed Memory Locations。
下面是該文章的原文,對應的翻譯標記為黃色。
C51: Passing Parameters to Indirectly Called Functions
C51:向間接調用函數傳遞參數
Information in this article applies to:本文信息適用於C51和Cx51所有版本
- C51 All Versions
- Cx51 All Versions
SYMPTOMS 現象
I'm using function pointers and object-oriented programming techniques in my application. Most of the time my program works as expected. But when I try to pass several parameters to functions that are called via pointers, I get the following compiler error message:
我使用面向對象技術使用了函數指針。大多數時間程序運行正常。但是,當試圖向函數傳遞幾個指針類型的參數時,編譯出現以下錯誤信息:
Error 212: Indirect call: Parameters do not fit within registers.
The program example below demonstrates this:舉例說明:
1 void (*CallBack1) (void *, unsigned char); 2 void (*CallBack2) (void *, void *); 3 void (*CallBack3) (char, char, char); 4 void (*CallBack4) (char, char, char, char); 5 6 unsigned char c, d, e, f; 7 char *ptr; 8 9 void test (void) { 10 CallBack1 (ptr, c); // works 11 CallBack2 (ptr, ptr); // fails - C51 generates an error message 12 // indirect call: parameters do not fit within 13 registers */ 14 CallBack3 (c, d, e); // works 15 CallBack4 (c, d, e, f); // fails - C51 generates an error message 16 // indirect call: parameters do not fit within registers */ 17 }
CAUSE 原因
Unlike most 16-bit and 32-bit microcontrollers, the 8051 is not a stack based architecture. When parameters do not fit into the CPU registers, the Keil Cx51 Compiler by default uses direct memory locations for parameter passing. This technique generates very efficient code but limits the parameters that can be passed to indirectly called functions. When parameters passed to a function via a function pointer will not fit into registers, the compiler cannot determine where in memory to place the parameters since the function is not known at call-time.
與大多數16位和32位微控制器不同,8051不是基於堆棧的體系結構。當參數不適合CPU寄存器時,Keil Cx51編譯器默認使用直接內存定位的方式傳遞參數。這種技術生成高效的代碼,但限制了傳遞給間接調用函數的參數。當通過函數指針傳遞給函數的參數無法裝入寄存器時,編譯器無法確定在內存中放置參數的位置,因為函數在調用時是未知的。
RESOLUTION 解決方法
There are two ways to solve your programming problem.
有兩種解決辦法。
-
Create reentrant functions using the reentrant function attribute. The compiler simulates a stack-based architecture which makes it possible to pass a virtually unlimited number of parameters to indirectly called functions. For example:
使用可重入函數屬性 reentrant 創建可重入函數。編譯器模擬了一種基於堆棧的體系結構,它可以將幾乎無限數量的參數傳遞給間接調用的函數。例如:
1 void (*CallBack1) (void *, unsigned char); 2 void (*CallBack2) (void *, void *) reentrant; 3 void (*CallBack3) (char, char, char); 4 void (*CallBack4) (char, char, char, char) reentrant; 5 6 unsigned char c, d, e, f; 7 char *ptr; 8 void test (void) { 9 CallBack1 (ptr, c); // works 10 CallBack2 (ptr, ptr); // works, but now the function that gets called 11 // need to have the reentrant attribute 12 CallBack3 (c, d, e); // works 13 CallBack4 (c, d, e, f); // works, but now the function that gets called 14 // need to have the reentrant attribute 15 }
2. Limit the number and types of parameters so that they all fit into CPU registers. Do this when you need utmost performance or when program size is critical. For example:
限制參數的數量和類型,使它們都適合CPU寄存器。當您需要最佳性能或程序大小非常重要時,請執行此操作。例如:
1 void (*CallBack1) (void *, unsigned char); 2 void (*CallBack2) (void xdata *, void xdata *); 3 void (*CallBack3) (char, char, char); 4 void (*CallBack4) (char, char, int); 5 6 unsigned char c, d, e, f; 7 char xdata *ptr; 8 9 void test (void) { 10 CallBack1 (ptr, c); // works 11 CallBack2 (ptr, ptr); // works, but pointers are memory typed now 12 CallBack3 (c, d, e); // works 13 CallBack4 (c, d, e | (f<<8)); // works, but two chars are packed into 14 // one int parameter 15 }
The parameter passing method is described in the C51/Cx51 Compiler User's Guide. Refer to this to determine how to change your function parameters to fit into registers.
《C51/Cx51編譯器用戶指南》中描述了參數傳遞方法。請參閱此項以確定如何更改函數參數以適應寄存器。
