這裏主要介紹一下檢查循環定義的結構體、聯合體。是對成員中包含本身自己的結構體、聯合體進行檢查。所謂「成員中包含本身自己」,舉例來講,就是指下面這樣的定義。java
struct point { struct point p; };
這裏所說的「成員中包含本身自己」是指直接包含本身自己,經過指針來應用本身自己是沒有問題的。例如剛纔的例子,若是是下面這樣的話就沒有問題了。算法
struct point { struct point *ptr; };
剛纔的例子中存在直接的循環定義,所以一眼就能看出來。還有以下所示的間接循環定義的狀況,也須要注意。數組
struct point_x { struct point_y y; }; typedef struct point_x my_point_x; struct point_y { my_point_x x; };
上述例子中還夾雜着使用typedef 定義的類型,所以調查起來更爲繁瑣。ui
檢查「循環定義的類型」的方法。進行這樣的類型檢查須要將類型定義的總體看成圖(graph)來思考。spa
將類型的定義抽象爲圖時,能夠將類型做爲節點,將該類型對其餘類型的引用做爲邊。例如結構體的定義,將該結構體的類型做爲節點,向成員的類型的節點鏈接一條邊。使用typedef 的狀況下,將新定義的類型做爲節點,向原來的類型節點引一條邊。指針
再來看一個例子。如今假設有以下所示的定義。對象
struct st { struct point pnt; long len; }; typedef unsigned int uint; struct point { uint x; uint y; };
將上述定義轉化爲圖,如圖10.2 所示。blog
若是發生循環定義,那麼在生成類型定義的圖時,圖中某處一定存在閉環。循環定義狀況下的圖如圖10.3 所示。get
可見圖中存在閉環。檢查是否存在循環定義,只需檢查類型定義的圖中是否存在閉環便可。it
檢測有向圖中的閉環的算法
由於邊存在方向性,因此類型定義的圖屬於有向圖。要檢測有向圖中是否存在閉環,可使用以下算法。
1. 選擇任意一個節點(類型)並標註爲「查找中」
2. 沿着邊依次訪問全部與該節點相鄰的節點
3. 若是訪問到的節點沒有標註任何狀態,則將該節點標註爲「查找中」;若是標註了「查找結束」,則不作任何處理,返回以前的節點;若是已經標註爲「查找中」,則說明存在閉環
4. 從當前的節點重複步驟2 和3,若是已經沒有可訪問的相鄰節點,則將該節點標註爲「查找結束」,並沿原路返回
5. 按照上述流程對全部節點進行處理,若是查找過程當中沒有遇到「查找中」狀態的節點,就說明不存在閉環
上述算法中使用了「有向圖的深度優先檢索」來檢測閉環。簡單地說,該算法的概要就是「只要節點有未訪問的相鄰節點就試着訪問,調查是否會回到原來的節點」。從算法執行過程當中的某一時刻來看,就是在爲從起始節點到某一節點的路徑上的全部節點標註上「查找中」的狀態。
具體算法以下:
protected void checkRecursiveDefinition(Type t, ErrorHandler h) { _checkRecursiveDefinition(t, new HashMap<Type, Object>(), h); } static final protected Object checking = new Object(); static final protected Object checked = new Object(); /*結構體、聯合體的循環定義檢查 * 結構體、聯合體、數組、typedef 所定義的類型之外的類型只有整數類型和指針,所以除 了上述4 個類型之外,其餘狀況下都不可能出現邊。包含某類型的指針的狀況下,由於不會產 生循環依賴,因此不會有問題。 算法說明中的「標註狀態」的實現方式是「將Type 對象和它的狀態做爲一組保存在 Map 對象marks 中」,這是上述算法的重點。 */ protected void _checkRecursiveDefinition(Type t, Map<Type, Object> marks, ErrorHandler h) { /* * 若是t 的狀態爲「查找中」,輸出錯誤並return */ if (marks.get(t) == checking) { h.error(((NamedType)t).location(), "recursive type definition: " + t); return; } /* * t 的狀態爲「查找結束」 */ else if (marks.get(t) == checked) { return; } /* * 訪問的節點尚未被標註狀態。 * 將t 標註爲「查找中」, * 訪問全部和t 相鄰的節點(調用_checkRecursiveDefinition), * 將t 標註爲「查找結束」。 */ else { marks.put(t, checking); if (t instanceof CompositeType) { CompositeType ct = (CompositeType)t; for (Slot s : ct.members()) { _checkRecursiveDefinition(s.type(), marks, h); } } else if (t instanceof ArrayType) { ArrayType at = (ArrayType)t; _checkRecursiveDefinition(at.baseType(), marks, h); } else if (t instanceof UserType) { UserType ut = (UserType)t; _checkRecursiveDefinition(ut.realType(), marks, h); } marks.put(t, checked); } }