注:
轉自https://www.cnblogs.com/renyuan/archive/2012/11/30/2796792.html
結構體的嵌套問題
結構體的自引用(self reference),就是在結構體內部,包含指向自身類型結構體的指針。
結構體的相互引用(mutual reference),就是說在多個結構體中,都包含指向其他結構體的指針。
1. 自引用結構體
1.1 不使用typedef時
錯誤的方式:
struct tag_1{ struct tag_1 A; int value; };
這種聲明是錯誤的,因為這種聲明實際上是一個無限循環,成員A是一個結構體,A的內部還會有成員是結構體,依次下去,無線循環。在分配內存的時候,由於無限嵌套,也無法確定這個結構體的長度,所以這種方式是非法的。
正確的方式: (使用指針)
struct tag_1{ struct tag_1 *A; int value; };
由於指針的長度是確定的(在32位機器上指針長度為4),所以編譯器能夠確定該結構體的長度。
1.2 使用typedef 時
錯誤的方式:
typedef struct { int value; NODE *link; } NODE;
這里的目的是使用typedef為結構體創建一個別名NODEP。但是這里是錯誤的,因為類型名的作用域是從語句的結尾開始,而在結構體內部是不能使用的,因為還沒定義。
正確的方式:有三種,差別不大,使用哪種都可以。
typedef struct tag_1{ int value; struct tag_1 *link; } NODE; struct tag_2; typedef struct tag_2 NODE; struct tag_2{ int value; NODE *link; }; struct tag_3{ int value; struct tag_3 *link; }; typedef struct tag_3 NODE;
2. 相互引用 結構體
錯誤的方式:
typedef struct tag_a{ int value; B *bp; } A; typedef struct tag_b{ int value; A *ap; } B;
錯誤的原因和上面一樣,這里類型B在定義之前 就被使用。
這里補充一點,如果是在tag_b 里直接用A定義*ap是可以的,而且這里也可直接用ap而不是指針的形式。這是因為tag_a在tag_b之前定義的,所以可以。但是上面的tag_a里面聲明B就不可以了。
正確的方式:(使用“不完全聲明”)
struct tag_a{ struct tag_b *bp; int value; }; struct tag_b{ struct tag_a *ap; int value; }; typedef struct tag_a A; typedef struct tag_b B; struct tag_a; struct tag_b; typedef struct tag_a A; typedef struct tag_b B; struct tag_a{ struct tag_b *bp; int value; }; struct tag_b{ struct tag_a *ap; int value; };
嵌套結構體時應注意:
結構體的自引用中,如下這種情況是非法的
struct s_ref {
int a;
struct s_ref b;
char c;
};
因為結構體內部又包含自身結構體類型b,這個長度不能確定,只能向下再查找,又包含自身結構體類型b,又再向下查找,如此循環,類似於永無出口的遞歸調用,是非法的。
但很多時候,的確需要使用到自引用,有個技巧,如下:
struct s_ref {
int a;
struct s_ref *b; //注意這句與上面相同位置的區別
char c;
};
這是合法的,因為此處是定義了一個指向結構體的指針,指針的大小在具體的機器平台和編譯器環境中都是已知的(即使不同的平台環境的定義不完全相同)。所以不會導致上述的遞歸死循環。是合法和可行的。但是要提醒的是:這個指針看似指向自身,其實不是,而是指向同一類型的不同結構。
鏈表和樹的數據結構就都使用到此技巧。自身的結構體指針指向下一節點或者下一子樹的地址。
這里有一種情況值得注意: typedef struct { //這里是結構體類型定義 int a; s_ref *b; //注意這句引用了結構體類型名 char c; }s_ref ; 這個結構體類型定義是為了定義類型名s_ref,但卻失敗了。因為結構體中就引用了結構類型名,而此時還沒定義類型名。 可以改為如下: typedef struct s_ref_t{ //這里是結構體類型定義和結構體標簽 int a; struct s_ref_t *b; //注意這句與上面相同位置的區別,使用了標簽 char c; }s_ref ;
看到上面這段,瞬間秒懂,謝博主~