1. 程式人生 > >Go學習(9):結構體

Go學習(9):結構體

一、結構體

1.1 什麼是結構體

Go 語言中陣列可以儲存同一型別的資料,但在結構體中我們可以為不同項定義不同的資料型別。
結構體是由一系列具有相同型別或不同型別的資料構成的資料集合。

1.2 結構體的定義和初始化

type struct_variable_type struct {
   member definition;
   member definition;
   ...
   member definition;
}

一旦定義了結構體型別,它就能用於變數的宣告

variable_name := structure_variable_type {
value1, value2...valuen}

初始化結構體

// 1.按照順序提供初始化值
P := person{"Tom", 25}
// 2.通過field:value的方式初始化,這樣可以任意順序
P := person{age:24, name:"Tom"}
// 3.new方式,未設定初始值的,會賦予型別的預設初始值
p := new(person)
p.age=24

1.3 結構體的訪問

訪問結構體成員(訪問結構的各個欄位)

通過點.操作符用於訪問結構的各個欄位。

package main

import "fmt"

type Books struct {
   title string
author string subject string book_id int } func main() { var Book1 Books /* 宣告 Book1 為 Books 型別 */ var Book2 Books /* 宣告 Book2 為 Books 型別 */ /* book 1 描述 */ Book1.title = "Go 語言" Book1.author = "www.runoob.com" Book1.subject = "Go 語言教程" Book1.book_id = 6495407
/* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 語言教程" Book2.book_id = 6495700 /* 列印 Book1 資訊 */ fmt.Printf( "Book 1 title : %s\n", Book1.title) fmt.Printf( "Book 1 author : %s\n", Book1.author) fmt.Printf( "Book 1 subject : %s\n", Book1.subject) fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id) /* 列印 Book2 資訊 */ fmt.Printf( "Book 2 title : %s\n", Book2.title) fmt.Printf( "Book 2 author : %s\n", Book2.author) fmt.Printf( "Book 2 subject : %s\n", Book2.subject) fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id) }

執行結果:

Book 1 title : Go 語言
Book 1 author : www.runoob.com
Book 1 subject : Go 語言教程
Book 1 book_id : 6495407
Book 2 title : Python 教程
Book 2 author : www.runoob.com
Book 2 subject : Python 語言教程
Book 2 book_id : 6495700

1.4 結構體指標

指標指向一個結構體
也可以建立指向結構的指標。

結構體指標

var struct_pointer *Books

以上定義的指標變數可以儲存結構體變數的地址。檢視結構體變數地址,可以將 & 符號放置於結構體變數前

struct_pointer = &Book1;

使用結構體指標訪問結構體成員,使用 “.” 操作符

struct_pointer.title;
package main

import "fmt"

type Books struct {
   title string
   author string
   subject string
   book_id int
}

func main() {
   var Book1 Books        /* Declare Book1 of type Book */
   var Book2 Books        /* Declare Book2 of type Book */

   /* book 1 描述 */
   Book1.title = "Go 語言"
   Book1.author = "www.runoob.com"
   Book1.subject = "Go 語言教程"
   Book1.book_id = 6495407

   /* book 2 描述 */
   Book2.title = "Python 教程"
   Book2.author = "www.runoob.com"
   Book2.subject = "Python 語言教程"
   Book2.book_id = 6495700

   /* 列印 Book1 資訊 */
   printBook(&Book1)

   /* 列印 Book2 資訊 */
   printBook(&Book2)
}
func printBook( book *Books ) {
   fmt.Printf( "Book title : %s\n", book.title);
   fmt.Printf( "Book author : %s\n", book.author);
   fmt.Printf( "Book subject : %s\n", book.subject);
   fmt.Printf( "Book book_id : %d\n", book.book_id);
}

結構體例項化也可以是這樣的

package main

import "fmt"

type Books struct {
}

func (s Books) String() string {
	return "data"
}
func main() {
	fmt.Printf("%v\n", Books{})
}

1.5 結構體的匿名欄位

結構體的匿名欄位

可以用欄位來建立結構,這些欄位只包含一個沒有欄位名的型別。這些欄位被稱為匿名欄位。

在型別中,使用不寫欄位名的方式,使用另一個型別

type Human struct {
    name string
    age int
    weight int
} 
type Student struct {
    Human // 匿名欄位,那麼預設Student就包含了Human的所有欄位
    speciality string
} 
func main() {
    // 我們初始化一個學生
    mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
    // 我們訪問相應的欄位
    fmt.Println("His name is ", mark.name)
    fmt.Println("His age is ", mark.age)
    fmt.Println("His weight is ", mark.weight)
    fmt.Println("His speciality is ", mark.speciality)
    // 修改對應的備註資訊
    mark.speciality = "AI"
    fmt.Println("Mark changed his speciality")
    fmt.Println("His speciality is ", mark.speciality)
    // 修改他的年齡資訊
    fmt.Println("Mark become old")
    mark.age = 46
    fmt.Println("His age is", mark.age)
    // 修改他的體重資訊
    fmt.Println("Mark is not an athlet anymore")
    mark.weight += 60
    fmt.Println("His weight is", mark.weight)
}

可以使用"."的方式進行呼叫匿名欄位中的屬性值

實際就是欄位的繼承

其中可以將匿名欄位理解為欄位名和欄位型別都是同一個

基於上面的理解,所以可以mark.Human = Human{"Marcus", 55, 220}mark.Human.age -= 1

若存在匿名欄位中的欄位與非匿名欄位名字相同,則最外層的優先訪問,就近原則

通過匿名訪問和修改欄位相當的有用,但是不僅僅是struct欄位哦,所有的內建型別和自定義型別都是可以作為匿名欄位的。

1.6 結構體巢狀

巢狀的結構體
一個結構體可能包含一個欄位,而這個欄位反過來就是一個結構體。這些結構被稱為巢狀結構。

示例程式碼:

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)
}

1.7 提升欄位

在結構體中屬於匿名結構體的欄位稱為提升欄位,因為它們可以被訪問,就好像它們屬於擁有匿名結構欄位的結構一樣。理解這個定義是相當複雜的。

示例程式碼:

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
}

執行結果

Name: Naveen  
Age: 50  
City: Chicago  
State: Illinois

1.8 匯出結構體和欄位

如果結構體型別以大寫字母開頭,那麼它是一個匯出型別,可以從其他包訪問它。類似地,如果結構體的欄位以大寫開頭,則可以從其他包訪問它們。

示例程式碼:

1.在computer目錄下,建立檔案spec.go

package computer

type Spec struct { //exported struct  
    Maker string //exported field
    model string //unexported field
    Price int //exported field
}

2.建立main.go 檔案

package main

import "structs/computer"  
import "fmt"

func main() {  
    var spec computer.Spec
    spec.Maker = "apple"
    spec.Price = 50000
    fmt.Println("Spec:", spec)
}

目錄結構如下:

src  
	structs
		computer
			spec.go
		main.go

1.9 結構體比較

結構體是值型別,如果每個欄位具有可比性,則是可比較的。如果它們對應的欄位相等,則認為兩個結構體變數是相等的。

示例程式碼:

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")
    }
}

執行結果

name1 and name2 are equal  
name3 and name4 are not equal  

如果結構變數包含的欄位是不可比較的,那麼結構變數是不可比較的

示例程式碼:

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")
    }
}

2.0 結構體作為函式的引數

結構體作為函式引數使用

ackage main

import "fmt"

type Books struct {
   title string
   author string
   subject string
   book_id int
}

func main() {
   var Book1 Books        /* 宣告 Book1 為 Books 型別 */
   var Book2 Books        /* 宣告 Book2 為 Books 型別 */

   /* book 1 描述 */
   Book1.title = "Go 語言"
   Book1.author = "www.runoob.com"
   Book1.subject = "Go 語言教程"
   Book1.book_id = 6495407

   /* book 2 描述 */
   Book2.title = "Python 教程"
   Book2.author = "www.runoob.com"
   Book2.subject = "Python 語言教程"
   Book2.book_id = 6495700

   /* 列印 Book1 資訊 */
   printBook(Book1)

   /* 列印 Book2 資訊 */
   printBook(Book2)
}

func printBook( book Books ) {
   fmt.Printf( "Book title : %s\n", book.title);
   fmt.Printf( "Book author : %s\n", book.author);
   fmt.Printf( "Book subject : %s\n", book.subject);
   fmt.Printf( "Book book_id : %d\n", book.book_id);
}

make、new操作

make用於內建型別(map、slice 和channel)的記憶體分配。new用於各種型別的記憶體分配
內建函式new本質上說跟其它語言中的同名函式功能一樣:new(T)分配了零值填充的T型別的記憶體空間,並且返回其地址,即一個*T型別的值。用Go的術語說,它返回了一個指標,指向新分配的型別T的零值。有一點非常重要:new返回指標

內建函式make(T, args)與new(T)有著不同的功能,make只能建立slice、map和channel,並且返回一個有初始值(非零)的T型別,而不是*T。本質來講,導致這三個型別有所不同的原因是指向資料結構的引用在使用前必須被初始化。例如,一個slice,是一個包含指向資料(內部array)的指標、長度和容量的三項描述符;在這些專案被初始化之前,slice為nil。對於slice、map和channel來說,make初始化了內部的資料結構,填充適當的值。

make返回初始化後的(非零)值。


整理自菜鳥教程