C語言定義一個結構體,然後在結構體中定義一個同類型的結構體指針。為什麼不能給這個結構體指針賦空間?
大概就是結構體裡面有一個結構體指針 為什麼不能直接夠這個指針賦值?類似於鏈表創建 但是是直接給next指針賦空間 而不是指向一個鏈表塊
你這個問題的描述真是混亂,我幫你縷出來兩種你可能的問題吧:
1、為什麼不能在聲明結構體的時候直接給成員變數賦值?
因為聲明可以視作只是向計算機說明瞭一個東西,C語言的聲明包括:
//結構體聲明
struct Test{
//結構體內容
int i;
};
//聯合體聲明
union Test{
//聯合體內容
int i;
};
//函數聲明
void test();
extern void test();
//全局變數聲明
extern int i;
//變數類型名聲明
typedef int s32;
在以上聲明的過程中,所有看似定義的東西都不能被賦值,因為它們只是向計算機說明瞭一下,我有這麼個東西,而不是真正的定義。如果你進行了定義,那麼要麼被忽略,要麼就警告甚至報錯。一定要能區分聲明和定義。聲明的時候,計算機沒有為它分配空間,只有定義了,纔有空間。所以你是無法為沒有空間的東西賦值的。例如下面這種寫法:
struct Test{
int p = 3;
};
就是不合法的。因為此時只是聲明,沒有定義變數,自然沒空間,所以這種寫法不合法。你要給p賦值,必須有結構體變數,例如寫成:
//1
struct Test{
int p;
};
struct Test t = {3};
t.p = 3;
//2
struct Test{
int p;
}t = {3};
t.p = 3;
以上兩種寫法,都是合法的。
2、結構體裏為什麼不能定義自己,而只能定義自己的指針?
如果我們寫成了這樣:
struct Test{
int p;
struct Test t;
};
那麼你會發現,這個結構體將會不知道自己有多大。如果我們用它定義了一個變數:
struct Test s;
那麼,這個s裏有個t,也就是s.t,s.t裏還有個t,也就是s.t.t,以此類推,s.t.t.t.t……無窮無盡了。所以這樣的結構會導致計算機無法判斷它的大小。於是這種結構顯然是不成立的。同理,數組也是不行的。
而如果我們寫成指針:
struct Test{
int p;
struct Test *next;
};
那麼任何指針的大小都是一定的(32位編譯器下是4位元組,64位編譯器下是8位元組),所以這個結構體可以確定大小。如果我們用它定義了一個變數:
struct Test s;
那麼此時,s.next是野指針,在你給它分配空間之前,s.next-&>next是不存在的。所以它的存在是合法的。
希望能解決你的疑問。
不過最好你能把你的問題改到一般人能看懂。
為什麼是賦空間?
我定義了一個結構體,我只是定義這結構體應該長什麼樣,提供了一個原型。我定義了一輛車,我只是定義了車有四個輪子和車身。輪子和車身是什麼樣的?我不知道。
四個輪子是圓的?方的?這應該是一個結構體對象所定義的東西。方輪子的車是車嘛?是。圓輪子的車是車嘛?是。
題主可能對於結構體聲明有點誤解。
題主大概在思考的是創建一個結構體對象的時候,為什麼不能直接嵌套自身結構體域。
假設允許你的這種結構體初始化,那麼他要開闢所有對象空間的時候就要出現問題。後果就是,當你準備開闢第一個對象空間,那麼第一個對象就需要開闢第二個自身結構體子對象空間,第二個對象需要開闢第三個自身結構體子對象空間……如此往複,也就是需要無限大的空間……因為本身就是一個無限大的鏈表……
所以你把結構體裡面內置自身結構體是不允許的……但是放指針可以……指針所佔的空間是恆定的……
感覺是我遇到過的問題,但不知道是不是。
類似於:
struct A {
...
struct A * a;
//struct A a;這個不行
};
其實這是一個雞生蛋還是蛋生雞的問題,A還沒有定義好,如果在A的定義中直接定義struct A a;對象而不是A指針,那編譯器沒辦法知道A的空間大小,另外還涉及到一個循環定義的問題,如果加入struct A a;你能計算出A佔用空間是多大嗎?而指針是固定大小,所以是可以定義的。
另外在Visual Studio中是可以這樣寫的
struct A {
...
struct A * a = NULL;
//struct A * a = new A();這個也不行
int data = 10;
};
含有默認值,也就是說的"定義即初始化",其實這是不符合C++規範的(在gcc上面是編譯不過的),VS為了簡便將struct也看做class來處理,所以成員是public,這很方便,但考慮平臺移植性時不建議這麼做。struct本來就只是定義一個數據結構,不包含!任何!函數,VS下面int data = 10;其實是編譯器將A處理成class後,調用默認構造函數A()時將默認值10賦值給data,所以=號賦值也是一種函數,所以標準不支持。那從這個順序也能知道,如果使用struct A * a = new A();,在構造函數中調用自身會發生什麼?結果就是函數遞歸調用造成棧溢出。
給next指針直接分配空間嗎?你malloc一塊內存賦值給next,這個指針指向你malloc出來的內存的首地址,但是你這個next指針是指定了類型的,也就是你這個結構體類型,在賦值給這個next指針時,編譯器會提示你類型不匹配,你必須要將這塊內存轉型成你next指針類型。這還是一個同類型的鏈表塊。
編譯器就是要做這些限制,為了讓你前後邏輯一致,這是編譯器的工作。要不然你聲明瞭你需要一個A類型大小的內存,然後又給它一塊B類型大小的內存,編譯器以為你是精神分裂。當然,在更低層面,例如彙編,你是可以這麼做的。但是邏輯上還是精分。
因為純c語法裏,沒有一種操作可以自動在定義指針時就完成了內存分配。
這和定義其他非指針變數是兩回事。
但其實認真的分辨。我們是應該理解這種區別的。
第一,你定義了一個變數,編譯器為你自動分配了存放這個變數的內存空間。
第二,你定義了一個指針,編譯器為你自動分配了存放這個指針的內存空間。
但是你要把這個指針存放上不同的內容,就等同於把它指向不同的空間。
那編譯器鬼知道你最後會把這個值變出什麼花來。
就像你說你要買10塊錢筆記本,但我不知道你要買什麼筆記本,我鬼知道給你什麼鏈接啊?
所以,你不能怪編譯器。
Cpp java不一樣,是因為他們有一個allocator
你是想問為什麼不能這樣麼(我就按照C++的風格寫了)
struct Node{
Node next;
...
}
你嘗試人肉算一下這個`Node`的大小就知道為什麼了。。。
大概是想在結構體裏的指針指向自己吧,這個不是不能,而是c語言沒做好。
推薦閱讀: