2023年7月的一天,在電子bbs討論區上溜達,看到乙個有趣的帖子,整個帖子內容如下:
純c51復位功能函式:乙個大三學生,讓人又愛又怕
現單列復位部分如下:
main()
; // 復位**
(*((void (*)())(rst)))(); // 執行上一行**,將rst陣列當函式呼叫
} 本來我告訴他嵌入如下**:
clr a
push acc
push acc
reti
結果他卻玩了前面哪一段,而陣列rst中的內容恰恰是上面的彙編機器碼,他的做法是將
rst陣列的資料當作**儲存,然後採用絕對位址方式指向該陣列,將該陣列中的**當作
函式來執行。居然通過了!
我覺得有問題,我說即使如此,那絕對位址呼叫也應該寫成(*((void (*)())(&rst)))()
才對呀,結果他反駁說,那樣的話,rst的位址就會當成引數傳遞給這個絕對位址函式,而
實際ljmp呼叫的位址並非rst的位址,而是乙個不確定的位址。於是我按照自己的說法嘗試
了一下,看看彙編結果,還真的是將rst的位址傳遞給了r1 r2,而絕對函式最終ljmp到了
乙個莫名其妙的位址上去了,死翹!
看來c真是一匹不容易駕馭的野馬,這個大三學生理解力在我之上,我30多歲的人了,幹了
這麼多年還沒他的境界呢,唉,人家才學了幾天啊,翻了幾天書就這麼厲害了,服了!
l 首先分析帖子的c語言**
第一句定義乙個陣列rst,陣列內資料就是完成復位功能的彙編機器碼,具體對應關係
為:clr a == 0xe4、push acc == 0xc0,0xe0、reti ==0x32
第二句是乙個函式指標的用法,函式指標用法稍微有點複雜,可參看本人著的書,:),以
下為快速入門講解。
定義乙個返回值是空函式指標的定義形式如下:
void (*p) ( )
當把函式指標賦值後,就能通過函式指標呼叫函式,呼叫形式如下:
(*p) ( );
或等價的簡化形式:
p ( );
假設rst就是函式指標,則如下呼叫形式就可以令微控制器復位再起。
(*rst ) ( );
但可惜,rst不是函式指標,而是陣列名,雖然兩者都是位址,但不可直接呼叫陣列名。
如同把char型變數a賦值給int型變數b,(int) 表示強制型別轉換:
b = (int) a
函式指標的強制型別轉換公式如下(c語言的哲學是定義形式和使用一致):
(void (*)() ) rst
這樣經過轉換後的rst就可以當作函式指標使用了,簡單的呼叫形式如下:
#define k (void (*)( ) ) rst
(*k) ( )
或: ( * ( void (*)( ) )rst ) ( );
這樣的語句就完成復位再啟功能了。型別轉換符()的優先順序跟指標運算子*的優先順序相同,
二者的結合方向是自右至左,所以上述語句就能完成復位功能了。保險起見有些程式設計師常
常喜歡再加個括號:
#define k ( (void (*)( ) ) rst )
我這裡:
定義處:
uint32 actfunction;
extern void menustartmymenufunc(void);
actfunction = (uint32)menustartmymenufunc;
呼叫處:
void (*actfunctioncall)(void);
actfunctioncall= (void(*)(void))(actfunction );
(*actfunctioncall)();
函式指標的強制型別轉換
先給出結論 函式指標是可以通過強制型別轉換而指向具有不同函式簽名的函式的,但是,轉換後,該函式指標沒辦法正確呼叫所指向的函式 不能給函式傳遞引數。下文給出得出這個結論的小故事。前幾天的任務要求寫乙個logger類,該類對外提供一些靜態的方法,但不生成物件。該類的主要作用是用來輸出訊息,不同的是可以由...
指標強制轉換
c 和 c 的指標強制轉換時不太一樣的,c 提供了4中型別,用於更加精細化控制指標的強制轉換。1 指標轉換原理 討論指標的強制轉換之前,我們先要理解清楚指標的根本含義。最通俗的解釋,指標即是位址,但是,指標絕不僅僅只是位址。1 指標存著所指物件位址 2 指標存著所指物件的型別和型別大小 試想我們定義...
指標強制轉換
指標強制轉換在指令變數賦值上,只是編譯器的乙個善意的提醒,在組合語言中,和不加強制轉換的指令時一樣的,並沒有多產生指令。強制轉換後的影響不是在轉換的時候發生,而是在用轉換後的身份去訪問記憶體時體現到了指令中。例子 6 int i globle variable 7 int pi globle var...