2.2 Go語(yǔ)言從入門到精通:Go語(yǔ)言變量


作者:xcbeyond
瘋狂源自夢(mèng)想,技術(shù)成就輝煌!微信公眾號(hào):《程序猿技術(shù)大咖》號(hào)主,專注后端開發(fā)多年,擁有豐富的研發(fā)經(jīng)驗(yàn),樂于技術(shù)輸出、分享,現(xiàn)階段從事微服務(wù)架構(gòu)項(xiàng)目的研發(fā)工作,涉及架構(gòu)設(shè)計(jì)、技術(shù)選型、業(yè)務(wù)研發(fā)等工作。對(duì)于Java、微服務(wù)、數(shù)據(jù)庫(kù)、Docker有深入了解,并有大量的調(diào)優(yōu)經(jīng)驗(yàn)。 









文章目錄

    1、變量的聲明
        1.1 標(biāo)準(zhǔn)聲明格式
        1.2 根據(jù)值自行判定變量類型
        1.3 短變量聲明
        1.4 批量聲明
    2、變量的初始化
    3、變量的多重賦值
    4、匿名變量
    5、變量的作用域
        5.1 局部變量
        5.2 全局變量
        5.3 形式參數(shù)


變量來(lái)源于數(shù)學(xué),是計(jì)算機(jī)語(yǔ)言中能儲(chǔ)存計(jì)算結(jié)果或能表示值的抽象概念。

Go語(yǔ)言是靜態(tài)類型語(yǔ)言,因此變量(variable)是有明確類型的,編譯器也會(huì)檢查變量類型的正確性。

變量可以通過變量名訪問。

Go 語(yǔ)言變量名由字母、數(shù)字、下劃線組成,其中首個(gè)字符不能為數(shù)字。
1、變量的聲明

聲明變量的一般形式是使用 var 關(guān)鍵字:

var name type

    

其中,var 是聲明變量的關(guān)鍵字,name 是變量名,type 是變量的類型。

還可以一次聲明多個(gè)類型相同的變量:

var name1,name2,name3 type

    

需要注意的是,Go語(yǔ)言和許多編程語(yǔ)言不同,它在聲明變量時(shí)將變量的類型放在變量的名稱之后。這樣做的好處就是可以避免像C語(yǔ)言中那樣含糊不清的聲明形式,例如:int* a, b; 。其中只有 a 是指針而 b 不是。如果你想要這兩個(gè)變量都是指針,則需要將它們分開書寫。而在 Go 中,則可以和輕松地將它們都聲明為指針類型:

var a, b *int

    
當(dāng)一個(gè)變量被聲明之后,系統(tǒng)自動(dòng)賦予它該類型的零值:int 為 0,float 為 0.0,bool 為 false,string 為空字符串,指針為 nil 等。所有的內(nèi)存在 Go 中都是經(jīng)過初始化的。

示例如下:

package main

import "fmt"

func main() {
    var a,b int
    fmt.Println(a,b)
}

    

輸出結(jié)果如下:

0 0

    

變量的聲明有多種形式,整理歸納如下。
1.1 標(biāo)準(zhǔn)聲明格式

Go語(yǔ)言的變量聲明的標(biāo)準(zhǔn)格式如下:

var 變量名 變量類型

    



var 變量名1,變量名2 變量類型

    

變量聲明以關(guān)鍵字 var 開頭,后置變量類型,行尾無(wú)須分號(hào)。

如果沒有初始化,則變量默認(rèn)為零值。(零值就是變量沒有做初始化時(shí)系統(tǒng)默認(rèn)設(shè)置的值。)

var 形式的聲明語(yǔ)句往往是用于需要顯式指定變量類型地方,或者因?yàn)樽兞可院髸?huì)被重新賦值而初始值無(wú)關(guān)緊要的地方。

示例如下:

package main

import "fmt"

func main() {
    // 聲明一個(gè)沒有初始化的int類型變量,默認(rèn)為零值0
    var a int
    fmt.Println(a)

    // 聲明一個(gè)沒有初始化的bool類型變量,默認(rèn)為零值false
    var b bool
    fmt.Println(b)
}

    

輸出結(jié)果如下:

0
false


1.2 根據(jù)值自行判定變量類型

var 變量名 = 變量值

    

示例如下:

package main

import "fmt"

func main() {
    var c = true
    fmt.Println(c)
}

    

輸出結(jié)果如下:

true

    

1.3 短變量聲明

在函數(shù)中,一種稱為短變量聲明的可選形式可以用來(lái)聲明和初始化局部變量。格式如下:

變量名 := 變量值

    

這里省略了關(guān)鍵字var,變量名的類型由變量值的類型決定,變量值可以是任何函數(shù)或表達(dá)式計(jì)算的結(jié)果值。

因其短小、靈活,故而在局部變量的聲明和初始化中主要使用。

短變量聲明有以下注意點(diǎn):

    定義變量,同時(shí)顯式初始化。

    不能直接提供數(shù)據(jù)類型。

    只能用在函數(shù)內(nèi)部。

    :=表示聲明,而=表示賦值。

    :=左側(cè)如果是已聲明的變量,就會(huì)產(chǎn)生編譯錯(cuò)誤。如:

    var intVal int
    intVal := 1        // 會(huì)產(chǎn)生編譯錯(cuò)誤
        1
        2

示例如下:

package main

import "fmt"

func main() {
    // 短變量聲明
    d := "xcbeyond"
    fmt.Println(d)
}

 
輸出結(jié)果如下:

xcbeyond

    

1.4 批量聲明

覺得每行都用 var 聲明變量比較煩瑣?沒關(guān)系,還有一種為懶人提供的定義變量的方法:

var (
    a int
    b string
    c float64
)


使用關(guān)鍵字 var 和括號(hào),可以將一組變量定義放在一起。

示例如下:

package main

import "fmt"

func main() {
    // 批量聲明
    var (
        e int
        f string
        g float64
    )
    fmt.Println(e,f,g)
}

 

輸出結(jié)果如下:

0  0

    






2、變量的初始化

變量初始化標(biāo)準(zhǔn)格式如下:

var 變量名 類型 = 變量值

    

如:

var i int = 100

    

i為變量名,類型為int,i的初始值為100。

上面代碼中,100 和 int 同為 int 類型,int 可以認(rèn)為是冗余信息,因此可以進(jìn)一步簡(jiǎn)化初始化的寫法。

在標(biāo)準(zhǔn)格式的基礎(chǔ)上,將 類型int省略后,編譯器會(huì)嘗試根據(jù)等號(hào)右邊的變量值推導(dǎo)出變量的類型。如:

var i = 100

 

短變量聲明并初始化,如:

i := 100

    
這是Go語(yǔ)言的推導(dǎo)聲明寫法,編譯器會(huì)自動(dòng)根據(jù)右值類型推斷出左值的對(duì)應(yīng)類型。
3、變量的多重賦值

賦值是用來(lái)更新變量所指的值,它最簡(jiǎn)單的形式由賦值符=,以及符號(hào)左邊的變量和右邊的表達(dá)式組成。

x = 1
person.name = "xcbeyond"
count[x] = count[x] * 5

另一種形式的賦值是多重賦值,它允許幾個(gè)變量一次性被賦值。

在Go語(yǔ)言語(yǔ)法中,變量初始化和變量賦值是兩個(gè)不同的概念,Go語(yǔ)言的變量賦值與其他語(yǔ)言一樣,但是Go語(yǔ)言提供了其他程序員期待已久的多重賦值功能,可以實(shí)現(xiàn)變量交換。多重賦值讓Go語(yǔ)言比其他語(yǔ)言減少了代碼量。

以簡(jiǎn)單的算法交換變量為例,傳統(tǒng)寫法如下所示:

var a int = 10
var b int = 20

var tmp int
tmp = a
a = b
b = t

fmt.Println(a, b)


新定義的變量tmp是需要內(nèi)存的,于是有人設(shè)計(jì)了新的算法來(lái)取代中間變量,其中一種寫法如下所示:

var a int = 10
var b int = 20

a = a ^ b
b = b ^ a
a = a ^ b

fmt.Println(a, b)

   
Go語(yǔ)言有了多重賦值功能,則簡(jiǎn)單寫法如下所示:

var a int = 10
var b int = 20

b, a = a, b

fmt.Println(a, b)

   
從以上例子來(lái)看,Go語(yǔ)言的寫法明顯簡(jiǎn)潔了許多,需要注意的是,多重賦值時(shí),左值和右值按照從左到右的順序賦值。這種方法在錯(cuò)誤處理和函數(shù)當(dāng)中會(huì)大量使用。
4、匿名變量

在編碼過程中,可能會(huì)遇到?jīng)]有名稱的變量、類型或方法。雖然這不是必須的,但有時(shí)候這樣做可以極大地增強(qiáng)代碼的靈活性,這些變量被統(tǒng)稱為匿名變量。

匿名變量的特點(diǎn)是一個(gè)下畫線_,_本身就是一個(gè)特殊的標(biāo)識(shí)符,被稱為空白標(biāo)識(shí)符。它可以像其他標(biāo)識(shí)符那樣用于變量的聲明或賦值(任何類型都可以賦值給它),但任何賦給這個(gè)標(biāo)識(shí)符的值都將被拋棄,因此這些值不能在后續(xù)的代碼中使用,也不可以使用這個(gè)標(biāo)識(shí)符作為變量對(duì)其它變量進(jìn)行賦值或運(yùn)算。使用匿名變量時(shí),只需要在變量聲明的地方使用下畫線替換即可。

例如,我們?cè)谑褂脗鹘y(tǒng)的強(qiáng)類型語(yǔ)言編程時(shí),經(jīng)常會(huì)出現(xiàn)這種情況,即在調(diào)用函數(shù)時(shí)為了獲取一個(gè)值,卻因?yàn)樵摵瘮?shù)返回多個(gè)值而不得不定義一堆沒用的變量。在Go中這種情況可以通過結(jié)合使用多重返回和匿名變量來(lái)避免這種丑陋的寫法,讓代碼看起來(lái)更加優(yōu)雅。

假設(shè)GetName()函數(shù)的定義如下,它返回3個(gè)值,分別為firstName、lastName和nickName,若只想獲得nickName,則可以用如下方式編寫:

package main

import "fmt"

func main() {
    // 匿名變量
    firstName, _, _ := getName()
    _, lastName, _ := getName()
    _, _, nickName := getName()
    fmt.Println(firstName, lastName, nickName)
}

func getName() (firstName, lastName, nickName string) {
    return "X", "C", "xcbeyond"
}

   

輸出結(jié)果如下:

X C xcbeyond

    1

這種用法可以讓代碼非常清晰,基本上屏蔽掉了可能混淆代碼閱讀者視線的內(nèi)容,從而大幅降低溝通的復(fù)雜度和代碼維護(hù)的難度。

匿名變量不占用內(nèi)存空間,不會(huì)分配內(nèi)存。匿名變量與匿名變量之間也不會(huì)因?yàn)槎啻温暶鞫鵁o(wú)法使用。
5、變量的作用域

一個(gè)變量(常量、類型或函數(shù))在程序中都有一定的作用范圍,稱之為作用域。

了解變量的作用域?qū)ξ覀儗W(xué)習(xí)Go語(yǔ)言來(lái)說是比較重要的,因?yàn)镚o語(yǔ)言會(huì)在編譯時(shí)檢查每個(gè)變量是否使用過,一旦出現(xiàn)未使用的變量,就會(huì)報(bào)編譯錯(cuò)誤。如果不能理解變量的作用域,就有可能會(huì)帶來(lái)一些不明所以的編譯錯(cuò)誤。

根據(jù)變量定義位置的不同,可以分為以下三個(gè)類型:

    局部變量:函數(shù)內(nèi)定義的變量。
    全局變量:函數(shù)外定義的變量。
    形式參數(shù):函數(shù)定義中的變量。

下面就來(lái)分別介紹一下。
5.1 局部變量

在函數(shù)體內(nèi)聲明的變量稱之為局部變量,它們的作用域只在函數(shù)體內(nèi),函數(shù)的參數(shù)和返回值變量都屬于局部變量。

局部變量不是一直存在的,它只在定義它的函數(shù)被調(diào)用后存在,函數(shù)調(diào)用結(jié)束后這個(gè)局部變量就會(huì)被銷毀。

main()函數(shù)中使用到了局部變量a、b、c,示例如下:

package main

import "fmt"

func main() {
    // 聲明局部變量a和b并賦值
    var a int = 3
    var b int = 4
    
    // 聲明局部變量c.并計(jì)算a和b的和
    c := a + b
    fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}

輸出結(jié)果如下:

a = 3, b = 4, c = 7

    1

5.2 全局變量

在函數(shù)體外聲明的變量稱之為全局變量,全局變量只需要在一個(gè)源文件中定義,就可以在所有源文件中使用,當(dāng)然,不包含這個(gè)全局變量的源文件需要使用“import”關(guān)鍵字引入全局變量所在的源文件之后才能使用這個(gè)全局變量。

全局變量聲明必須以var關(guān)鍵字定義,如果想要在外部包中使用全局變量的首字母必須大寫。

定義全局變量c,示例如下:

package main

import "fmt"

// 聲明全局變量
var c int

func main() {
    //聲明局部變量
    var a, b int
    
    //初始化參數(shù)
    a = 3
    b = 4
    c = a + b
    
    fmt.Printf("a = %d, b = %d, c = %d\n", a, b, c)
}

   

輸出結(jié)果如下:

a = 3, b = 4, c = 7

    1

Go語(yǔ)言程序中全局變量與局部變量名稱可以相同,但是函數(shù)體內(nèi)的局部變量會(huì)被優(yōu)先考慮。
5.3 形式參數(shù)

在定義函數(shù)時(shí),函數(shù)名后面括號(hào)中的變量叫做形式參數(shù)(簡(jiǎn)稱形參)。形式參數(shù)只在函數(shù)調(diào)用時(shí)才會(huì)生效,函數(shù)調(diào)用結(jié)束后就會(huì)被銷毀,在函數(shù)未被調(diào)用時(shí),函數(shù)的形參并不占用實(shí)際的存儲(chǔ)單元,也沒有實(shí)際值。

形式參數(shù)會(huì)作為函數(shù)的局部變量來(lái)使用。

函數(shù)sum(a, b int)定義了形式參數(shù)a、b,示例如下:

package main

import "fmt"

func main() {
    sum := sum(1,3)
    fmt.Println(sum)
}

// 兩數(shù)求和
func sum(a, b int) int{
    num := a + b
    return num
}

 
輸出結(jié)果如下:

4