當我第一次接觸到C語言時,就對結構體投入了極大的興趣,認為這個東西以後大有作為,後來接觸Java、C++,面向物件程式設計中的物件進入我的視線,經過了這麼多年的磨練,回過頭來再看結構體依舊是那麼親切;同時從另一個角度上看結構體與面向物件中的成員物件是多麼的相像 :)
一、結構體元素
結構體由關鍵字、結構體型別名稱和具體成員構成,如下:
二、結構體初步認識
下面透過對比陣列(複合型別)來了解一下結構體:
1、從儲存型別來看
陣列只能儲存相同的型別:
s := []string{"a", "b", "c", "d", "e"}
結構體可以儲存不同的型別
// 宣告結構體 type employee struct { name,address string // 姓名、住址 age int // 年齡 height,weight float64 // 身高、體重 } |
2、從記憶體來看
它們都是在記憶體中佔據連續的記憶體空間,但對於陣列來說,每一個元素所佔用的記憶體大小是相同的,而結構體每一個項所佔用的記憶體大小不一定相同
3、從型別組合角度來看
陣列沒有組合的用法,例如一個一維陣列,一旦陣列型別確定就不可以再把另一個一維陣列設定為元素值,例如
s := []string{"a", "b", "c", "d", "e"}
s[0] = []string{"f", "g"}
此時執行該程式會出現類似此提示:cannot use []string literal (type []string) as type string in assignment;
結構體支援組合,我們知道一維空間是一條線,二維空間是一個平面,三維空間是一個空間
type line struct { x int } type plane struct { line y int } type space struct { plane z int } |
我們很自然地透過組合的方式,把一維擴充套件到二維,把二維擴充套件到三維,把三維擴充套件到四維,依次類推......
4、從操作角度上來看
陣列元素的操作是透過下標來完成的:
s := []string{"a", "b", "c", "d", "e"} for i := 0; i < len(s); i++ { fmt.Println(s[i]) // 列印陣列中每一個元素,透過s[i]下標的方式來獲取 } |
而結構體是透過項名來完成的:
t := space{plane{line{3}, 5}, 7} fmt.Println(t.x, t.y, t.z) // 透過操作結構體的項名t.x、t.y、t.z來獲取 |
5、從比較角度上來看
陣列與結構體類似,若判斷兩個陣列是否相同,需要看陣列的儲存型別、陣列長度、每一個元素是否相等,同樣判斷兩個結構體是否相同,需要看結構體的型別是否相同,然後看項的順序、項的名稱、項的型別等等
三、結構體的初始化
關於陣列的初始化可參見《【6】GO語言的陣列》,相對陣列結構體的初始化有點繁雜,下面一一道來:
1、空結構體
所謂空結構體,即結構體的成員為空,如下:
// 宣告空結構體employee type employee struct { } func main(){ emp := employee{} // 結構體的初始化,直接使用結構體型別名稱後面跟一個大括號 fmt.Println(emp) } |
其中employee{}就表示初始化一個結構體,然後賦值給emp,執行就會打印出結果{},因為該結構體成員為空;可能有讀者想,若結構體有成員,同樣這樣初始化會有什麼結果呢?
type employee struct { name, address string // 姓名、住址 age int // 年齡 height, weight float64 // 身高、體重 } func main(){ emp := employee{} // 這樣有什麼結果呢? fmt.Println(emp) } |
執行一下就會發現,結果是{ 0 0 0},因為字串的預設值為空串,不會顯示出來,而int和float64的預設值為0,所以打印出該結果。其實說白了這就是結構體成員的預設值問題,具體如下圖:
2、結構體的初始化
結構體的成員初始化是透過操作成員物件來完成
func main() { emp := employee{} fmt.Println(emp) emp.name = "駭客eagle" emp.age = 38 fmt.Println(emp) } |
採用變數+"."+成員名=值的形式對結構體進行初始化,例如emp.age=38。這種初始化形式很類似C++、Java,那麼是否還有其它形式呢?當然,以前說過GO語言就是人的正常思維語言,只要你能想到,基本上就可以正常執行 :)
emp1 := employee{"keji", "hangzhou", 19, 175, 65} fmt.Println(emp1) |
這種初始化形式看起來更直觀。上面的執行結果如下:
讀者可能還會問,我只想對其中的某幾個成員賦值,而上面是對所有成員賦值,該如何辦呢?
emp2 := employee{address: "hangzhou", age: 20} fmt.Println(emp2) |
這樣只有被指定賦值的成員才能得到真實的值,而未指定賦值的成員則被系統賦予預設值,這種情況也被稱為採用字面值進行初始化
3、巢狀結構體
這個比較好理解,即結構體裡面巢狀結構體,我們把“身高”、“體重”定義為一個結構體,而“身高”、“體重”是一個human(結構體)的成員,所以可以採用巢狀結構體:
// 體形 type figure struct { height, weight float64 } type human struct { name, address string figure } |
即human結構體中包含figure結構體,我們可以採用下面的初始化
man := human{}
fmt.Println(man)
執行結果為{ {0 0}}
結合上面講的結構體初始化,我們很容易透過字面值對name和address初始化
man.name = "siyu"
man.address = "tianjin"
但是怎麼對巢狀的結構體成員height、weight進行初始化呢?用過面向物件程式設計的人很容易想到,採用如下方式:
func main() { man := human{} man.name = "siyu" man.address = "tianjin" man.figure.height = 172.8 man.figure.weight = 175.3 fmt.Println(man) } |
即一層層向下找:先找man的成員figure,然後透過figure的成員height對身高進行賦值,這樣沒有問題,其實GO給我們提供了一種更便捷的賦值方式:
man.height = 172.8 man.weight = 175.3 |
即直接對其成員賦值,這種方式簡單直接,但會引入“成員可見性”的概念
4、結構體成員的可見性
任何語言在程式碼面前都是蒼白的,敏捷有一個思想就是程式碼勝過文件,費話少說,用程式碼來解釋什麼是“結構體成員的可見性”
// 生物會笑、會哭,所以有哭、笑成員 type biology struct { cry, laugh string } // 人會笑、會哭,所以也有哭、笑成員;但同時人嵌套了生物結構體 type human struct { biology cry, laugh string } |
下面採用如下方式對結構體初始化
man := human{}
man.cry = "cry"
man.laugh = "laugh"
fmt.Println(man)
那麼這裡的man.cry,man.laugh是對human的成員賦值呢?還是對biology的成員賦值呢?執行一下結果便可以知道,這裡是對human的成員賦值
因為內層大括號是空的,為什麼這樣呢?
可以按剝洋蔥的思維來理解,若最外層有此成員名(cry、laugh)則不用再向裡面剝了,若最外層沒有該成員名,則進一步向裡面剝,直到找到為止;
這也就是說,若外層有成員名(cry、laugh),則內層的同名成員是不可見的,若外層沒有成員名(cry、laugh),內層的成員才變的可見
5、再談巢狀結構體的初始化
以上例來說,我們可以採用字面值的形式初始化:
man := human{}
man.cry = "cry"
man.laugh = "laugh"
man.biology.cry = "biology cry"
man.biology.laugh = "biology laugh"
其實還可以採用如下形式:
woman := human{biology: biology{cry: "biology cry", laugh: "biology laugh"}, cry: "cry", laugh: "laugh"}
還可以簡化為:
woman := human{biology: biology{"biology cry", "biology laugh"}, cry: "cry", laugh: "laugh"}
是否還可以簡化為?
woman := human{{"biology cry", "biology laugh"}, "cry", "laugh"}
此時就會丟擲如下異常:
你如果夠仔細的話,就能發現巢狀結構體就只寫了一個結構體型別名,而沒有采用value valueType的形式,所以針對這種情況,GO語言認為內部巢狀結構體名稱和型別名是同一個