結(jié)構(gòu)體
本文我們著重講解一下Go語言中的結(jié)構(gòu)體愤兵。
什么是結(jié)構(gòu)體 肋坚?
結(jié)構(gòu)體是用戶定義的數(shù)據(jù)類型,它表示了一組字段的集合悲雳。當(dāng)我們需要把一組數(shù)據(jù)匯集成單一的邏輯單元而不是把這些數(shù)據(jù)各自處理成單獨(dú)的類型。例如香追,雇員有姓氏合瓢、名稱、年齡透典,那我們就可以把這三個(gè)字段構(gòu)造成一個(gè)單獨(dú)的結(jié)構(gòu)體employee晴楔。
聲明一個(gè)結(jié)構(gòu)體
type Employee struct{
firstName string
lastName string
age int
}
上面的代碼聲明了一個(gè)結(jié)構(gòu)體類型Employee,它有firstName峭咒,lastName, age這三個(gè)字段税弃。這個(gè)結(jié)構(gòu)體還可以更緊湊一些,我們可以把相同類型的字段放在同一行代碼里面聲明凑队。例如则果,firstName和lastName都屬于string類型,因此我們可以把上面的程序?qū)懗扇缦拢?/p>
type Employee struct{
firstName,lastName string
age, salary int
}
上面的Employee結(jié)構(gòu)體漩氨,我們稱它為命名結(jié)構(gòu)體西壮,因?yàn)樗鼊?chuàng)建了一個(gè)名為Employee的新類型。今后我們就可以使用Employee這個(gè)新類型創(chuàng)建一個(gè)Employee類型的結(jié)構(gòu)體叫惊。
除此之外款青,在聲明結(jié)構(gòu)體時(shí),還可以不聲明一個(gè)新類型霍狰,這種類型的結(jié)構(gòu)體被稱為匿名結(jié)構(gòu)體抡草。
var employee struct {
firstName, lastName string
age int
}
上面的代碼段創(chuàng)建了一個(gè)匿名結(jié)構(gòu)體employee。
創(chuàng)建一個(gè)命名結(jié)構(gòu)體
我們使用下面這個(gè)簡單的程序來創(chuàng)建一個(gè)命名的結(jié)構(gòu)體類型Employee蚓耽。
package main
import (
"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)
}
在上面的程序中渠牲,我們定義了一個(gè)結(jié)構(gòu)體類型Employee。我們又定義了一個(gè)結(jié)構(gòu)體變量emp1,給它的每個(gè)字段都賦予了值步悠。像這樣的情況签杈,字段名稱的順序沒必要一定要和結(jié)構(gòu)體聲明時(shí)的順序保持一致。
同時(shí)鼎兽,我們還定義了一個(gè)結(jié)構(gòu)體變量emp2,在定義emp2時(shí)答姥,我們省略了字段名稱,在這種情況谚咬,就必須使字段的順序和聲明結(jié)構(gòu)體時(shí)的順序保持一致鹦付。
程序的輸出結(jié)構(gòu)如下:
Employee 1 {Sam Anderson 25 500}
Employee 2 {Thomas Paul 29 800}
創(chuàng)建匿名結(jié)構(gòu)體
package main
import (
"fmt"
)
func main() {
emp3 := struct {
firstName, lastName string
age, salary int
}{
firstName: "Andreah",
lastName: "Nikola",
age: 31,
salary: 5000,
}
fmt.Println("Employee 3", emp3)
}
在上面的程序中,我們定義了一個(gè)匿名結(jié)構(gòu)體變量emp3择卦。像我們之前提到的敲长,這種結(jié)構(gòu)體類型稱為匿名結(jié)構(gòu)體郎嫁。因?yàn)樗鼉H創(chuàng)建了一個(gè)結(jié)構(gòu)體變量emp3,而并沒有定義任何的新結(jié)構(gòu)體類型。
程序的輸出如下:
Employee 3 {Andreah Nikola 31 5000}
結(jié)構(gòu)體的零值
當(dāng)定義了一個(gè)結(jié)構(gòu)體類型祈噪,并且沒有顯示地進(jìn)行初始化時(shí)泽铛,結(jié)構(gòu)體的所有字段都會(huì)被默認(rèn)賦予零值。
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
var emp4 Employee //zero valued structure
fmt.Println("Employee 4", emp4)
}
在上面的程序中辑鲤,我們定義了一個(gè)結(jié)構(gòu)體變量emp4,并且沒有初始化任何值盔腔。因此,firstName和lastName就會(huì)被賦予字符串的零值即:“”月褥。而age和salary則會(huì)被賦予int的零值弛随,即0。程序的最終輸出如下:
Employee 4 { 0 0}
當(dāng)然也可以為結(jié)構(gòu)體中的一些字段指定值宁赤,而其余的字段不指定频丘。這時(shí)光坝,沒有被指定初始值的字段就會(huì)被賦予零值娄涩。
package main
import (
"fmt"
)
type Employee struct {
firstName, lastName string
age, salary int
}
func main() {
emp5 := Employee{
firstName: "John",
lastName: "Paul",
}
fmt.Println("Employee 5", emp5)
}
程序的輸出如下:
Employee 5 {John Paul 0 0}
訪問結(jié)構(gòu)體中的單個(gè)字段
"."操作符可用于訪問結(jié)構(gòu)體中的單個(gè)字段榔幸。
package main
import (
"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)
}
emp6.firstName訪問了結(jié)構(gòu)體變量emp6的firstName字段途蒋。
程序的輸出如下:
First Name: Sam
Last Name: Anderson
Age: 55
Salary: $6000
除此之外瓷马,還可以創(chuàng)建一個(gè)零值結(jié)構(gòu)體糠涛,之后再給它的字段賦值社搅。
package main
import (
"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)
}
在上面的程序中厉斟,定義了一個(gè)結(jié)構(gòu)體變量emp7,之后又為它的firstName和lastName字段賦予值挚躯。程序的輸出如下:
Employee 7: {Jack Adams 0 0}
指向結(jié)構(gòu)體的指針
創(chuàng)建指向一個(gè)結(jié)構(gòu)體變量的指針。
package main
import (
"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)
}
emp8是一個(gè)指向結(jié)構(gòu)體變量的指針擦秽。(*emp8).firstName是用于訪問emp8結(jié)構(gòu)體的firstName字段码荔。
程序的輸出如下:
First Name: Sam
Age: 55
除了使用(*emp8).firstName訪問firstName字段外,還可以使用emp8.firstName來訪問firstName字段感挥。
package main
import (
"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)
}
輸出結(jié)果如下:
First Name: Sam
Age: 55
匿名字段
在創(chuàng)建結(jié)構(gòu)體時(shí)缩搅,我們也可以只指定類型而不指定字段名。這些字段被稱為匿名字段触幼。
下面的代碼片就創(chuàng)建了一個(gè)Person硼瓣,該結(jié)構(gòu)體有倆個(gè)匿名字段string和int。
package main
import (
"fmt"
)
type Person struct {
string
int
}
func main() {
p := Person{"Naveen", 50}
fmt.Println(p)
}
程序的輸出如下:
{Naveen 50}.
盡管匿名字段并沒有名稱置谦,默認(rèn)情況下堂鲤,它的數(shù)據(jù)類型就是它的字段名稱。例如Person結(jié)構(gòu)體媒峡,雖然字段是匿名的瘟栖,但是默認(rèn)地它會(huì)拿字段的類型作為名稱。因此谅阿,Person結(jié)構(gòu)體擁有倆個(gè)字段半哟,其名稱分別為:string和int酬滤。
package main
import (
"fmt"
)
type Person struct {
string
int
}
func main() {
var p1 Person
p1.string = "naveen"
p1.int = 50
fmt.Println(p1)
}
在上面的程序中,我們分別使用字段的數(shù)據(jù)類型作為它的字段名稱來訪問字段寓涨,程序的輸出結(jié)果為:
{naveen 50}
嵌套結(jié)構(gòu)體
結(jié)構(gòu)體的字段除了可以是基本類型之外敏晤,還可以是結(jié)構(gòu)體類型。這樣的結(jié)構(gòu)體缅茉,我們稱為嵌套結(jié)構(gòu)體嘴脾。
package main
import (
"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)
}
在上面的程序中,Person有個(gè)名為address的字段蔬墩,address字段也是一個(gè)結(jié)構(gòu)體译打,該程序的輸出如下:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
promoted fields
結(jié)構(gòu)體中的匿名的結(jié)構(gòu)體字段被稱為promoted字段,我們用一段代碼來理解下拇颅。
type Address struct {
city, state string
}
type Person struct {
name string
age int
Address
}
在上面的代碼片段中奏司,Person結(jié)構(gòu)體有一個(gè)匿名字段Address,Address同時(shí)也是一個(gè)結(jié)構(gòu)體類型≌敛澹現(xiàn)在Address結(jié)構(gòu)體中的字段city和state被稱為promoted字段韵洋,因?yàn)樗鼈兛梢灾苯釉L問,就好像它們是直接在Person結(jié)構(gòu)體中聲明的一樣黄锤。
package main
import (
"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
}
在上面的程序中搪缨,city和state是promoted字段,它們可以直接使用p.city和p.state訪問鸵熟,就像它們直接是在結(jié)構(gòu)體p中聲明一樣副编。程序的輸出如下:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
導(dǎo)出結(jié)構(gòu)體和字段
如果一個(gè)結(jié)構(gòu)體類型以大寫字母開頭的話,那么它就是一個(gè)導(dǎo)出類型流强,導(dǎo)出類型可以在其他包中訪問痹届。相似地,如果結(jié)構(gòu)體的字段以大寫字母開頭的話打月,他們可以在其他包中訪問队腐。
我們來寫一個(gè)代碼,理解這個(gè)奏篙。
在src下面創(chuàng)建一個(gè)文件夾structs柴淘,在structs里面再創(chuàng)建另一個(gè)文件夾computer。
在computer文件夾下报破,我們把下面的程序保存為spec.go悠就。
package computer
type Spec struct { //exported struct
Maker string //exported field
model string //unexported field
Price int //exported field
}
在上面的代碼片中,我們創(chuàng)建了一個(gè)computer包充易,它包含了一個(gè)結(jié)構(gòu)體類型Spec梗脾,Spec里面有倆個(gè)導(dǎo)出類型Maker和Price和一個(gè)非導(dǎo)出字段model。我們?cè)趍ain包里面引入此包盹靴,并使用Spec結(jié)構(gòu)體炸茧。
在structs目錄下瑞妇,創(chuàng)建一個(gè)main.go文件,并把下面的程序?qū)懭雖ian.go文件中梭冠。
package main
import "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
在上面的程序中辕狰,我們引入了computer包。同時(shí)我們?cè)L問了Spec結(jié)構(gòu)體的導(dǎo)出字段Maker以及Price控漠。
在workspacepath/bin/structs下面執(zhí)行g(shù)o install structs命令蔓倍。
程序?qū)⑤敵鋈缦拢?/p>
Spec: {apple 50000}
如果我們?cè)噲D訪問非導(dǎo)出字段model時(shí),編譯器會(huì)報(bào)錯(cuò)盐捷。
package main
import "structs/computer"
import "fmt"
func main() {
var spec computer.Spec
spec.Maker = "apple"
spec.Price = 50000
spec.model = "Mac Mini"
fmt.Println("Spec:", spec)
}
上面的程序中偶翅,報(bào)錯(cuò)如下:spec.model undefined (cannot refer to unexported field or method model。
結(jié)構(gòu)的相等性
結(jié)構(gòu)體是值類型并且其中的字段都是可比的話碉渡,在他們對(duì)應(yīng)的字段都相等的情況下聚谁,這倆個(gè)結(jié)構(gòu)體變量就是相等的。
package main
import (
"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")
}
}
在上面的程序中滞诺,結(jié)構(gòu)體類型name包含了倆個(gè)string字段形导。因?yàn)閟tring是可比較的數(shù)據(jù)類型,所以倆個(gè)name類型的結(jié)構(gòu)體變量可以做比較习霹。
在上面程序中朵耕,name1和name2是相等的,而name3和name4并不相等序愚。程序?qū)⑤敵鋈缦陆Y(jié)果:
name1 and name2 are equal
name3 and name4 are not equal
如果一個(gè)結(jié)構(gòu)體中含有不可比的字段憔披,那么該結(jié)構(gòu)體也是不可比的等限。
package main
import (
"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")
}
}
在上述程序中爸吮,image結(jié)構(gòu)體包含了一個(gè)map類型的字段data。由于map是不可比的望门,所以image1和image2也是不可比的形娇。如果你運(yùn)行此程序的話,編譯器會(huì)報(bào)錯(cuò):main.go:18: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared).
源代碼筹误,請(qǐng)查看github 代碼地址.
以上就是關(guān)于結(jié)構(gòu)體的所有內(nèi)容桐早,感謝您的閱讀。