Golang 學習——結構體 struct (一)

相守之路發表於2020-05-05

Golang中結構體struct定義,結構體指標,空結構體和nil區別學習

Golang中陣列可以儲存同一型別的資料,但在結構體中我們可以為不同項定義不同的資料型別。

結構體是由一系列具有相同型別或不同型別的資料構成的資料集合。

類似Java,Python中的class。

1.語法

關鍵字struct表示建立一個結構體,語法如下

type struct_variable_name struct {
   member1 definition
   member2 definition
   ...
   member definition
}

我們定義一個Person結構體,包含name,age,hight三個成員變數:

type Person struct {
    name  string
    age   int
    hight float64
}

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

2.初始化

結構體初始化有多種方式,根據應用場景自由選擇

(1).方式一,簡短宣告初始化:

bob := Person{"Bob", 19, 1.85}
fmt.Printf("Bob 資料型別:%T,值為:%v\n", bob, bob)

輸出為:

Bob 資料型別:main.Person,值為:{Bob 19 1.85}

這個結構體是在main方法中定義的,為了能夠執行main方法,匯入包的地方必須寫為:

package main

所以,在輸出 Bob 資料型別的時候,前面有個 main.點表示隸屬於main包下的結構體。 之後輸出的是Bob結構體的內容

(2).方式二,var 定義:

var alan Person
fmt.Println("alan 結構體:", alan) 
alan.name = "Alan"
alan.age = 20
alan.hight = 1.78
fmt.Println("Alan 結構體內容:", alan)

輸出為:

alan 結構體: { 0 0}   // {  0 0}  注意:第一個值是空字串,控制檯輸出不明顯,看不出來
Alan 結構體內容: {Alan 20 1.78}

通過var宣告結構體(未初始化)alan,alan是一個只有預設值的結構體,不是nil。

我們可以試驗一下:

var jerry Person
fmt.Printf(jerry == nil)  // 這段程式碼編譯會報錯->無效操作:不匹配的型別Person和nil
// 報錯內容:invalid operation: jerry == nil (mismatched types Person and nil)

我們定義了一個名為jerry的結構體,並未進行初始化,在判斷是否為nill時,程式碼報錯了
首先要明白,Golangnil表示什麼,以下是我從原始碼中複製的:

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.

var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

可以看到, nil的型別必須是一個指標,通道,函式,介面,字典,切片型別,他們都是引用型別

而結構體struct是值型別,jerry結構體未初始化,其成員變數的值都會取預設值,所以也可以理解為是有值,只不過是預設值。

(3).方式三:Person{}
結構體後面加 {},表示宣告結構體。

tom := Person{}
tom.name = "Tom"
tom.age = 21
tom.hight = 1.73
fmt.Println("Tom 結構體內容:", tom)

輸出為:

Tom 結構體內容: {Tom 21 1.73}

(4).方式四:Person{}變體
(3)方式中是先建立一個結構體,然後初始化,分兩步操作的。其實我們可以一步到位的:

jack := Person{
    name:  "Jack",
    age:   19,
    hight: 1.69,
}
fmt.Println("Jack 結構體內容:", jack)

輸出:

Jack 結構體內容: {Jack 19 1.69}

它是一個指標,指向了一個結構體

1.結構體前面加’*’

在結構體前面加一個*即可

var jerryPtr *Person  // 定義一個結構體指標,指標指向Person
jerryPtr = &bob       // 將bob的記憶體地址賦值給jerryPtr
fmt.Println("jerryPtr 結構體指標為:", jerryPtr)
fmt.Printf("jerryPtr 結構體指標地址為:%p,型別為:%T\n", &jerryPtr, jerryPtr)

輸出:

jerryPtr 結構體指標為: &{Bob 19 1.85}
jerryPtr 結構體指標地址為:0xc000006030,型別為:*main.Person

2.通過new()建立結構體

或者通過new()建立結構體,返回的也是一個指標

alan := new(Person)
fmt.Println("alan 為:", alan) // 不是nill
fmt.Printf("alan 的地址為:%p,資料型別為;%T\n", alan, alan)
alan.name = "Alan"
alan.age = 19
alan.hight = 1.79
fmt.Println("alan 結構體的內容為:", alan)

輸出:

alan 為: &{ 0 }
alan 的地址為:0xc00006e540,資料型別為;*main.Person
alan 結構體的內容為: &{Alan 19}

觀察輸出,我們發現,alan 的資料型別*main.Person,是一個指標。

3.空結構體和nil區別

寫到這裡,在思考一個問題,那我定義一個沒有任何成員變數的結構體,new的時候,返回的是不是nil呢?

檢視了下new()函式返回的是指標型別,原始碼如下:

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type

帶著疑問,實際操作一下。
我們先定義一個空結構體:

type Student struct {
}

然後我們在main函式中宣告一個空結構體,並判斷是否為nill

student := new(Student)
fmt.Printf("student 的資料型別為:%T,值為:%v\n", student, student)
fmt.Println("student == nill :", student == nil)

輸出:

student 的資料型別為:*main.Student,值為:&{}
student == nill : false

可以看到,空結構體student並不是nil,而且其的值為 &{}

寫到這裡,返回上文看了下nil的原始碼,疑惑瞬間解開了:

  • stuct是一個值型別,即使加了*也只是變成了一個指標,指向結構體了。

  • nil是一個Type,根據原始碼var nil Type,它其實也是Golang中的一中型別,nil的型別必須是一個指標,通道,函式,介面,字典,切片型別

舉個例子,宣告一個slice,不做任何初始化,那麼該slice就是一個nil

Talk is cheap, show me code:

var s []int64
fmt.Println("s :", s)
fmt.Println("s == nil:", s == nil)

輸出:

s : []
s == niltrue

可以看到,s 確實一個nil,和我們思考的一樣。

總結一下,今天記錄了struct的定義和宣告方式,弄清楚了空結構體和nil的區別。

nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。nil是預先說明的識別符號,也即通常意義上的關鍵字。在Golang中,nil只能賦值給指標、channelfuncinterfacemapslice型別的變數

另外,要注意的是,在Golangstruct是值型別,結構體作為引數時,是副本拷貝。如果想引用傳值,加個*即可。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章