结构体怎么赋值_Go 经典入门系列 16:结构体
歡迎來到 Golang 系列教程的第 16 個(gè)教程。
什么是結(jié)構(gòu)體?
結(jié)構(gòu)體是用戶定義的類型,表示若干個(gè)字段(Field)的集合。有時(shí)應(yīng)該把數(shù)據(jù)整合在一起,而不是讓這些數(shù)據(jù)沒有聯(lián)系。這種情況下可以使用結(jié)構(gòu)體。
例如,一個(gè)職員有 firstName、lastName 和 age 三個(gè)屬性,而把這些屬性組合在一個(gè)結(jié)構(gòu)體 employee 中就很合理。
結(jié)構(gòu)體的聲明
type?Employee?struct?{????firstName?string
????lastName??string
????age???????int
}
在上面的代碼片段里,聲明了一個(gè)結(jié)構(gòu)體類型 Employee,它有 firstName、lastName 和 age 三個(gè)字段。通過把相同類型的字段聲明在同一行,結(jié)構(gòu)體可以變得更加緊湊。在上面的結(jié)構(gòu)體中,firstName 和 lastName 屬于相同的 string 類型,于是這個(gè)結(jié)構(gòu)體可以重寫為:
type?Employee?struct?{????firstName,?lastName?string
????age,?salary?????????int
}
上面的結(jié)構(gòu)體 Employee 稱為 命名的結(jié)構(gòu)體(Named Structure)。我們創(chuàng)建了名為 Employee 的新類型,而它可以用于創(chuàng)建 Employee 類型的結(jié)構(gòu)體變量。
聲明結(jié)構(gòu)體時(shí)也可以不用聲明一個(gè)新類型,這樣的結(jié)構(gòu)體類型稱為 匿名結(jié)構(gòu)體(Anonymous Structure)。
var?employee?struct?{????firstName,?lastName?string
????age?int
}
上述代碼片段創(chuàng)建一個(gè)匿名結(jié)構(gòu)體 employee。
創(chuàng)建命名的結(jié)構(gòu)體
通過下面代碼,我們定義了一個(gè)命名的結(jié)構(gòu)體 Employee。
package?mainimport?(
????"fmt"
)
type?Employee?struct?{
????firstName,?lastName?string
????age,?salary?????????int
}
func?main()?{
????//creating?structure?using?field?names
????emp1?:=?Employee{
????????firstName:?"Sam",
????????age:???????25,
????????salary:????500,
????????lastName:??"Anderson",
????}
????//creating?structure?without?using?field?names
????emp2?:=?Employee{"Thomas",?"Paul",?29,?800}
????fmt.Println("Employee?1",?emp1)
????fmt.Println("Employee?2",?emp2)
}
在線運(yùn)行程序[1]
在上述程序的第 7 行,我們創(chuàng)建了一個(gè)命名的結(jié)構(gòu)體 Employee。而在第 15 行,通過指定每個(gè)字段名的值,我們定義了結(jié)構(gòu)體變量 emp1。字段名的順序不一定要與聲明結(jié)構(gòu)體類型時(shí)的順序相同。在這里,我們改變了 lastName 的位置,將其移到了末尾。這樣做也不會(huì)有任何的問題。
在上面程序的第 23 行,定義 emp2 時(shí)我們省略了字段名。在這種情況下,就需要保證字段名的順序與聲明結(jié)構(gòu)體時(shí)的順序相同。
該程序?qū)⑤敵?#xff1a;
Employee?1?{Sam?Anderson?25?500}Employee?2?{Thomas?Paul?29?800}
創(chuàng)建匿名結(jié)構(gòu)體
package?mainimport?(
????"fmt"
)
func?main()?{
????emp3?:=?struct?{
????????firstName,?lastName?string
????????age,?salary?????????int
????}{
????????firstName:?"Andreah",
????????lastName:??"Nikola",
????????age:???????31,
????????salary:????5000,
????}
????fmt.Println("Employee?3",?emp3)
}
在線運(yùn)行程序[2]
在上述程序的第 3 行,我們定義了一個(gè)匿名結(jié)構(gòu)體變量 emp3。上面我們已經(jīng)提到,之所以稱這種結(jié)構(gòu)體是匿名的,是因?yàn)樗皇莿?chuàng)建一個(gè)新的結(jié)構(gòu)體變量 em3,而沒有定義任何結(jié)構(gòu)體類型。
該程序會(huì)輸出:
Employee?3?{Andreah?Nikola?31?5000}結(jié)構(gòu)體的零值(Zero Value)
當(dāng)定義好的結(jié)構(gòu)體并沒有被顯式地初始化時(shí),該結(jié)構(gòu)體的字段將默認(rèn)賦為零值。
package?mainimport?(
????"fmt"
)
type?Employee?struct?{
????firstName,?lastName?string
????age,?salary?????????int
}
func?main()?{
????var?emp4?Employee?//zero?valued?structure
????fmt.Println("Employee?4",?emp4)
}
在線運(yùn)行程序[3]
該程序定義了 emp4,卻沒有初始化任何值。因此 firstName 和 lastName 賦值為 string 的零值("")。而 age 和 salary 賦值為 int 的零值(0)。該程序會(huì)輸出:
Employee?4?{?0?0}當(dāng)然還可以為某些字段指定初始值,而忽略其他字段。這樣,忽略的字段名會(huì)賦值為零值。
package?mainimport?(
????"fmt"
)
type?Employee?struct?{
????firstName,?lastName?string
????age,?salary?????????int
}
func?main()?{
????emp5?:=?Employee{
????????firstName:?"John",
????????lastName:??"Paul",
????}
????fmt.Println("Employee?5",?emp5)
}
在線運(yùn)行程序[4]
在上面程序中的第 14 行和第 15 行,我們初始化了 firstName 和 lastName,而 age 和 salary 沒有進(jìn)行初始化。因此 age 和 salary 賦值為零值。該程序會(huì)輸出:
Employee?5?{John?Paul?0?0}訪問結(jié)構(gòu)體的字段
點(diǎn)號(hào)操作符 . 用于訪問結(jié)構(gòu)體的字段。
package?mainimport?(
????"fmt"
)
type?Employee?struct?{
????firstName,?lastName?string
????age,?salary?????????int
}
func?main()?{
????emp6?:=?Employee{"Sam",?"Anderson",?55,?6000}
????fmt.Println("First?Name:",?emp6.firstName)
????fmt.Println("Last?Name:",?emp6.lastName)
????fmt.Println("Age:",?emp6.age)
????fmt.Printf("Salary:?$%d",?emp6.salary)
}
在線運(yùn)行程序[5]
上面程序中的 emp6.firstName 訪問了結(jié)構(gòu)體 emp6 的字段 firstName。該程序輸出:
First?Name:?SamLast?Name:?Anderson
Age:?55
Salary:?$6000
還可以創(chuàng)建零值的 struct,以后再給各個(gè)字段賦值。
package?mainimport?(
????"fmt"
)
type?Employee?struct?{
????firstName,?lastName?string
????age,?salary?????????int
}
func?main()?{
????var?emp7?Employee
????emp7.firstName?=?"Jack"
????emp7.lastName?=?"Adams"
????fmt.Println("Employee?7:",?emp7)
}
在線運(yùn)行程序[6]
在上面程序中,我們定義了 emp7,接著給 firstName 和 lastName 賦值。該程序會(huì)輸出:
Employee?7:?{Jack?Adams?0?0}結(jié)構(gòu)體的指針
還可以創(chuàng)建指向結(jié)構(gòu)體的指針。
package?mainimport?(
????"fmt"
)
type?Employee?struct?{
????firstName,?lastName?string
????age,?salary?????????int
}
func?main()?{
????emp8?:=?&Employee{"Sam",?"Anderson",?55,?6000}
????fmt.Println("First?Name:",?(*emp8).firstName)
????fmt.Println("Age:",?(*emp8).age)
}
在線運(yùn)行程序[7]
在上面程序中,emp8 是一個(gè)指向結(jié)構(gòu)體 Employee 的指針。(*emp8).firstName 表示訪問結(jié)構(gòu)體 emp8 的 firstName 字段。該程序會(huì)輸出:
First?Name:?SamAge:?55
Go 語言允許我們?cè)谠L問 firstName 字段時(shí),可以使用 emp8.firstName 來代替顯式的解引用 (*emp8).firstName。
package?mainimport?(
????"fmt"
)
type?Employee?struct?{
????firstName,?lastName?string
????age,?salary?????????int
}
func?main()?{
????emp8?:=?&Employee{"Sam",?"Anderson",?55,?6000}
????fmt.Println("First?Name:",?emp8.firstName)
????fmt.Println("Age:",?emp8.age)
}
在線運(yùn)行程序[8]
在上面的程序中,我們使用 emp8.firstName 來訪問 firstName 字段,該程序會(huì)輸出:
First?Name:?SamAge:?55
匿名字段
當(dāng)我們創(chuàng)建結(jié)構(gòu)體時(shí),字段可以只有類型,而沒有字段名。這樣的字段稱為匿名字段(Anonymous Field)。
以下代碼創(chuàng)建一個(gè) Person 結(jié)構(gòu)體,它含有兩個(gè)匿名字段 string 和 int。
type?Person?struct?{????string
????int
}
我們接下來使用匿名字段來編寫一個(gè)程序。
package?mainimport?(
????"fmt"
)
type?Person?struct?{
????string
????int
}
func?main()?{
????p?:=?Person{"Naveen",?50}
????fmt.Println(p)
}
在線運(yùn)行程序[9]
在上面的程序中,結(jié)構(gòu)體 Person 有兩個(gè)匿名字段。p := Person{"Naveen", 50} 定義了一個(gè) Person 類型的變量。該程序輸出 {Naveen 50}。
雖然匿名字段沒有名稱,但其實(shí)匿名字段的名稱就默認(rèn)為它的類型。比如在上面的 Person 結(jié)構(gòu)體里,雖說字段是匿名的,但 Go 默認(rèn)這些字段名是它們各自的類型。所以 Person 結(jié)構(gòu)體有兩個(gè)名為 string 和 int 的字段。
package?mainimport?(
????"fmt"
)
type?Person?struct?{
????string
????int
}
func?main()?{
????var?p1?Person
????p1.string?=?"naveen"
????p1.int?=?50
????fmt.Println(p1)
}
在線運(yùn)行程序[10]
在上面程序的第 14 行和第 15 行,我們?cè)L問了 Person 結(jié)構(gòu)體的匿名字段,我們把字段類型作為字段名,分別為 "string" 和 "int"。上面程序的輸出如下:
{naveen?50}嵌套結(jié)構(gòu)體(Nested Structs)
結(jié)構(gòu)體的字段有可能也是一個(gè)結(jié)構(gòu)體。這樣的結(jié)構(gòu)體稱為嵌套結(jié)構(gòu)體。
package?mainimport?(
????"fmt"
)
type?Address?struct?{
????city,?state?string
}
type?Person?struct?{
????name?string
????age?int
????address?Address
}
func?main()?{
????var?p?Person
????p.name?=?"Naveen"
????p.age?=?50
????p.address?=?Address?{
????????city:?"Chicago",
????????state:?"Illinois",
????}
????fmt.Println("Name:",?p.name)
????fmt.Println("Age:",p.age)
????fmt.Println("City:",p.address.city)
????fmt.Println("State:",p.address.state)
}
在線運(yùn)行程序[11]
上面的結(jié)構(gòu)體 Person 有一個(gè)字段 address,而 address 也是結(jié)構(gòu)體。該程序輸出:
Name:?NaveenAge:?50
City:?Chicago
State:?Illinois
提升字段(Promoted Fields)
如果是結(jié)構(gòu)體中有匿名的結(jié)構(gòu)體類型字段,則該匿名結(jié)構(gòu)體里的字段就稱為提升字段。這是因?yàn)樘嵘侄尉拖袷菍儆谕獠拷Y(jié)構(gòu)體一樣,可以用外部結(jié)構(gòu)體直接訪問。我知道這種定義很復(fù)雜,所以我們直接研究下代碼來理解吧。
type?Address?struct?{????city,?state?string
}
type?Person?struct?{
????name?string
????age??int
????Address
}
在上面的代碼片段中,Person 結(jié)構(gòu)體有一個(gè)匿名字段 Address,而 Address 是一個(gè)結(jié)構(gòu)體。現(xiàn)在結(jié)構(gòu)體 Address 有 city 和 state 兩個(gè)字段,訪問這兩個(gè)字段就像在 Person 里直接聲明的一樣,因此我們稱之為提升字段。
package?mainimport?(
????"fmt"
)
type?Address?struct?{
????city,?state?string
}
type?Person?struct?{
????name?string
????age??int
????Address
}
func?main()?{
????var?p?Person
????p.name?=?"Naveen"
????p.age?=?50
????p.Address?=?Address{
????????city:??"Chicago",
????????state:?"Illinois",
????}
????fmt.Println("Name:",?p.name)
????fmt.Println("Age:",?p.age)
????fmt.Println("City:",?p.city)?//city?is?promoted?field
????fmt.Println("State:",?p.state)?//state?is?promoted?field
}
在線運(yùn)行程序[12]
在上面代碼中的第 26 行和第 27 行,我們使用了語法 p.city 和 p.state,訪問提升字段 city 和 state 就像它們是在結(jié)構(gòu)體 p 中聲明的一樣。該程序會(huì)輸出:
Name:?NaveenAge:?50
City:?Chicago
State:?Illinois
導(dǎo)出結(jié)構(gòu)體和字段
如果結(jié)構(gòu)體名稱以大寫字母開頭,則它是其他包可以訪問的導(dǎo)出類型(Exported Type)。同樣,如果結(jié)構(gòu)體里的字段首字母大寫,它也能被其他包訪問到。
讓我們使用自定義包,編寫一個(gè)程序來更好地去理解它。
在你的 Go 工作區(qū)的 src 目錄中,創(chuàng)建一個(gè)名為 structs 的文件夾。另外在 structs 中再創(chuàng)建一個(gè)目錄 computer。
在 computer 目錄中,在名為 spec.go 的文件中保存下面的程序。
package?computertype?Spec?struct?{?//exported?struct
????Maker?string?//exported?field
????model?string?//unexported?field
????Price?int?//exported?field
}
上面的代碼片段中,創(chuàng)建了一個(gè) computer 包,里面有一個(gè)導(dǎo)出結(jié)構(gòu)體類型 Spec。Spec 有兩個(gè)導(dǎo)出字段 Maker 和 Price,和一個(gè)未導(dǎo)出的字段 model。接下來我們會(huì)在 main 包中導(dǎo)入這個(gè)包,并使用 Spec 結(jié)構(gòu)體。
package?mainimport?"structs/computer"
import?"fmt"
func?main()?{
????var?spec?computer.Spec
????spec.Maker?=?"apple"
????spec.Price?=?50000
????fmt.Println("Spec:",?spec)
}
包結(jié)構(gòu)如下所示:
src???structs
????????computer
????????????spec.go
????????main.go
在上述程序的第 3 行,我們導(dǎo)入了 computer 包。在第 8 行和第 9 行,我們?cè)L問了結(jié)構(gòu)體 Spec 的兩個(gè)導(dǎo)出字段 Maker 和 Price。執(zhí)行命令 go install structs 和 workspacepath/bin/structs,運(yùn)行該程序。
如果我們?cè)噲D訪問未導(dǎo)出的字段 model,編譯器會(huì)報(bào)錯(cuò)。將 main.go 的內(nèi)容替換為下面的代碼。
package?mainimport?"structs/computer"
import?"fmt"
func?main()?{
????var?spec?computer.Spec
????spec.Maker?=?"apple"
????spec.Price?=?50000
????spec.model?=?"Mac?Mini"
????fmt.Println("Spec:",?spec)
}
在上面程序的第 10 行,我們?cè)噲D訪問未導(dǎo)出的字段 model。如果運(yùn)行這個(gè)程序,編譯器會(huì)產(chǎn)生錯(cuò)誤:spec.model undefined (cannot refer to unexported field or method model)。
結(jié)構(gòu)體相等性(Structs Equality)
結(jié)構(gòu)體是值類型。如果它的每一個(gè)字段都是可比較的,則該結(jié)構(gòu)體也是可比較的。如果兩個(gè)結(jié)構(gòu)體變量的對(duì)應(yīng)字段相等,則這兩個(gè)變量也是相等的。
package?mainimport?(
????"fmt"
)
type?name?struct?{
????firstName?string
????lastName?string
}
func?main()?{
????name1?:=?name{"Steve",?"Jobs"}
????name2?:=?name{"Steve",?"Jobs"}
????if?name1?==?name2?{
????????fmt.Println("name1?and?name2?are?equal")
????}?else?{
????????fmt.Println("name1?and?name2?are?not?equal")
????}
????name3?:=?name{firstName:"Steve",?lastName:"Jobs"}
????name4?:=?name{}
????name4.firstName?=?"Steve"
????if?name3?==?name4?{
????????fmt.Println("name3?and?name4?are?equal")
????}?else?{
????????fmt.Println("name3?and?name4?are?not?equal")
????}
}
在線運(yùn)行程序[13]
在上面的代碼中,結(jié)構(gòu)體類型 name 包含兩個(gè) string 類型。由于字符串是可比較的,因此可以比較兩個(gè) name 類型的結(jié)構(gòu)體變量。
上面代碼中 name1 和 name2 相等,而 name3 和 name4 不相等。該程序會(huì)輸出:
name1?and?name2?are?equalname3?and?name4?are?not?equal
如果結(jié)構(gòu)體包含不可比較的字段,則結(jié)構(gòu)體變量也不可比較。
package?mainimport?(
????"fmt"
)
type?image?struct?{
????data?map[int]int
}
func?main()?{
????image1?:=?image{data:?map[int]int{
????????0:?155,
????}}
????image2?:=?image{data:?map[int]int{
????????0:?155,
????}}
????if?image1?==?image2?{
????????fmt.Println("image1?and?image2?are?equal")
????}
}
在線運(yùn)行程序[14]
在上面代碼中,結(jié)構(gòu)體類型 image 包含一個(gè) map 類型的字段。由于 map 類型是不可比較的,因此 image1 和 image2 也不可比較。如果運(yùn)行該程序,編譯器會(huì)報(bào)錯(cuò):main.go:18: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared)。
github[15] 上有本教程的源代碼。
上一教程 - 指針
下一教程 - 方法[16]
推薦閱讀
Go 經(jīng)典入門系列 15:指針
總結(jié)
以上是生活随笔為你收集整理的结构体怎么赋值_Go 经典入门系列 16:结构体的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python的scramy架构_Pyth
- 下一篇: 金融行业信息系统信息安全等级保护实施指引