陳浩師兄03年的一篇博客《用C寫有面向對象特點的程序》描述了用C語言來實現類似C++類繼承的方法,這樣方法的核心要點就是結構體的強制類型轉換,讓我來簡單分析分析C語言中的結構體強制類型轉換,還是用陳浩師兄原博的結構體來舉例吧。兩個結構體如下:
/* 雙向鏈表 (類似於父類)*/ typedef struct hLinks{ struct hLinks *bwLink; struct hLinks *fwLink; } hLinks;
/* * 一個使用雙向鏈表的結構 * (類似於子類) */ typedef struct hEnt{ hLinks links; int hData; char key[10]; } hEnt;
首先,我們要搞清楚的一點是:C語言中的結構體並不能直接進行強制類型轉換,只有結構體的指針可以進行強制類型轉換。因此你可以在原博中看到在函數調用的時候有一些比較別扭的參數形式,我們來看看。
/* * 打印 (類似於子類重載父類的成員函數) */ PrintLink( hLinks *h ) { hEnt *p ; for( p = ( hEnt* ) h->fwLink; /* <-----------把hLink再轉回來 */ p != ( hEnt* ) h; p = ( hEnt* )( (hLinks*)p )->fwLink ) { printf("hData=[%d], key=[%s]/n", p->hData, p->key); } }
PrintLink函數的參數是一個hLinks類型的指針,因此在調用PrintLink時傳入參數也應該是hLinks類型的指針,如果我們定義的時候用下面這樣的形式。
hLinks head;
那么在調用函數的時候就必須把它轉換成hLinks指針,於是先取地址在強制類型轉換。
PrintLink( (hLinks *) &head );
這樣看起來確實是很別扭,如果我們在聲明結構體的時候這樣做的話就可以避免這么難看的傳遞形式。
typedef hLinks *PtrhLinks;
話說回來,結構體指針的強制類型轉換問題在這里面始終存在。PrintLink中就出現了這樣的情況,那么在將hLinks指針轉換為hEnt類型指針時有兩個問題:
- 結構體中的成員情況是怎么的?
- 結構體中的成員的值的情況是怎么樣的?
首先,結構體是儲存在一塊連續內存中的,計算機只關心的是結構體的大小和操作方式,結構體大小是定義的時候決定的(要進行對齊),而結構體的操作確實和結構體中的成員類型有關的。指針表示的是內存地址,那么在強制類型轉換之后,計算機便以轉換后的結構體來看待這個地址內存中的內容。比如兩個結構體的內存結構如下:
hLinks hEnt
*bwLink | *bwLink |
*fwLink | *fwLink |
——(未定義) | hData |
——(未定義) | key[10] |
可以看出,在前兩個內存單元中兩個結構體存儲的內容是相同的,當然不管相不相同計算機是不管的,當hLinks類型轉換成hEnt類型時,計算機就將原結構體看做是hEnt類型的。轉換后的hEnt類型結構體的前面兩個內存單元的內容就是hLinks中的前兩個單元內容,而hEnt的后兩個內存單元中的內容取得是hLinks后的兩個單元(這兩個單元不是hLinks類型的成員,而是別的內容,所有如果轉換后的hEnt要訪問hData和key的話是不安全的!)。
總之一句話,在轉換之后,計算機就按照轉換后的結構體的組成結構來解釋那么一段內存中存儲的數據!!