大概就是結構體裡面有一個結構體指針 為什麼不能直接夠這個指針賦值?類似於鏈表創建 但是是直接給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語言沒做好。


推薦閱讀:
相關文章