常量(Constants)和iota
常量包含不會發生更改的資料。常量的資料型別只能是boolean、number(int/float/complex)或string。
定義方式:
const NAME [TYPE] = VALUE
TYPE基本可以省略,因為常量都是簡單資料型別,編譯器可以根據值推斷出它的資料型別。
例如:
const Pi = 3.14159
常量在編譯期間被評估,因此定義的常量必須是在編譯期間就能計算出來的結果。例如呼叫一些執行期間的函式來生成常量的值就是錯誤的,因為在編譯期間無法呼叫這些執行期間的函式。常量的值定義好後,無法在執行期間更改,否則會報錯。
const c = 3+2 // 正確
const d = getNumber() // 錯誤
常量的精度可以隨意長,Go不會出現精度溢位的問題。且常量賦值時,如果值太長,可以使用續行符:
const Ln2= 0.693147180559945309417232121458
176568075500134360255254120680009
const Log2E= 1/Ln2
const Billion = 1e9
Go中只有將超出變數精度的值賦值給變數時才會出現溢位問題。
可以一次性定義多個常量:
const beef, two, c = "meat", 2, "veg"
const Monday, Tuesday, Wednesday = 1, 2, 3
const (
Monday, Tuesday, Wednesday = 1, 2, 3
Thursday, Friday, Saturday = 4, 5, 6
)
常量可以用列舉。定義了下面的常量後,Female就代表了數值1。
const (
Unknown = 0
Female = 1
Male = 2
)
可以使用iota
實現列舉,iota
自身是builtin包中定義的一個常量,其值為0,它用於在常量中定義序列數,從0開始增加:
const (
a = iota
b = iota
c = iota
)
當iota
第一次呼叫時,產生數值0,在新行中再次呼叫iota,將自動增加1,所以上面的a=0,b=1,c=2
。上面的常量列舉可以簡寫成等價形式:
const (
a = iota
b
c
)
iota不能用於執行期間,因為它是小寫字母開頭的常量,不會被匯出。下面的程式碼會報錯:iota未定義
var a int = iota
iota
也可以用於表示式中,例如iota+50
表示將當前的iota值加上50。
每個常量塊(const block)結構都會重置和初始化iota的值為0。
func main() {
const a = iota // a=0
const b = iota + 3 // b=3
const c,d = iota,iota+3 // c=0,d=3
const (
e = iota // e=0
f = iota + 4 // f=5
g // g=6
)
println(a,b,c,d,e,f,g)
}
變數
在使用變數之前,有兩個過程:宣告變數、變數賦值。宣告變數也常被稱為”定義變數”。變數宣告後必須使用,否則會報錯。
定義變數的常用方式:
var identifier type
例如:
var a int
var b bool
var str string
// 或者
var (
a int
b bool
str string
)
當變數宣告的時候,會做預設的賦0初始化,每種資料型別的預設賦0初始化的0值不同。例如int型別的0值為數值0,float的0值為0.0,string型別的0值為空””,bool型別的0值為false,資料結構的0值為nil,struct的0值為欄位全部賦0。
變數在編譯期間就可以獲取到它的值,但如果賦值給變數的值需要經過執行期間的計算,則需要延遲到執行期間才能獲取對應的值。
var a int = 15 // 編譯期間賦值好
var b int = 15/3 // 編譯期間賦值好
var c = getNumber() // 執行期間才賦值
宣告和賦值可以結合:
var a int = 15
var i = 5
var b bool = false
var str string = "Hello World"
宣告和賦值結合的時候,對於簡單資料型別的值,可以省略type部分,因為Go可以根據值自己推斷出型別:
var a = 15
var b = false
var str = "Hello World"
var (
a = 15
b = false
str = "Hello World"
numShips = 50
city string
)
因為要推斷資料型別,所以型別推斷操作是在執行期間完成的。
在使用推斷型別的賦值時,如果想要指定特定型別,需要顯式指定。例如整數數值推斷的型別為int,要想讓它儲存到int64中,則必須顯式指定型別:
var a int64 = 2
要推斷型別必須是宣告和賦值一起的,否則沒有值,無法根據值去推斷。例如var a
是錯的。
除了上面的推斷方式,通過:=
符號也能實現宣告和賦值結合,它也會根據資料型別進行推斷,連var關鍵字都省略了:
a := 50
但是:=
只能在函式程式碼塊內部使用,在全域性作用域下使用將報錯,因為型別推斷是在執行期執行的,而全域性範圍內的變數宣告部分是在編譯期間就決定好的。例如,下面的將報錯:
a := 10
func main() { println(a) }
變數宣告之後不能再次宣告(除非在不同的作用域),之後只能使用=
進行賦值。例如,執行下面的程式碼將報錯:
package main
import ("fmt")
func main(){
x:=10
fmt.Println("x =",x)
x:=11
fmt.Println("x =",x)
}
錯誤如下:
# command-line-arguments
. est.go:8:3: no new variables on left side of :=
報錯資訊很明顯,:=
左邊沒有新變數。
如果仔細看上面的報錯資訊,會發現no new variables
是一個複數。實際上,Go允許我們使用:=
一次性宣告、賦值多個變數,而且只要左邊有任何一個新變數,語法就是正確的。
func main(){
name,age := "longshuai",23
fmt.Println("name:",name,"age:",age)
// name重新賦值,因為有一個新變數weight
weight,name := 90,"malongshuai"
fmt.Println("name:",name,"weight:",weight)
}
需要注意,name第二次被:=
賦值,Go第一次推斷出該變數的資料型別之後,就不允許:=
再改變它的資料型別,因為只有第一次:=
對name進行宣告,之後所有的:=
對name都只是簡單的賦值操作。
例如,下面將報錯:
weight,name := 90,80
錯誤資訊:
. est.go:11:14: cannot use 80 (type int) as type string in assignment
另外,變數宣告之後必須使用,否則會報錯,因為Go對規範的要求非常嚴格。例如,下面定義了weight
但卻沒使用:
weight,name := 90,"malongshuai"
fmt.Println("name:",name)
錯誤資訊:
. est.go:11:2: weight declared and not used
變數作用域(scope)
Go語言的作用域採用的是詞法作用域,意味著文字段定義所在位置決定了可看見的值範圍。關於詞法作用域和動態作用域,詳細內容參見:一文搞懂:詞法作用域、動態作用域、回撥函式、閉包
- 定義在函式內部的變數為區域性變數,只在函式內部可見
- 定義在程式碼塊內(如
{...CODE...}
)的變數也是區域性變數,除了程式碼塊就消失
- 定義在程式碼塊外、函式外的變數為包變數或者全域性變數,它們可以被同一個目錄下同一個包的多個檔案訪問(因為Go中一個目錄下只能定義一個包,但一個包可以分成多個檔案)
- 如果變數的名稱以小寫字母開頭,則其它包不能訪問該變數
- 如果變數的名稱以大寫字母開頭,則其它包可以訪問該變數
不同scope的變數名可以衝突,但建議採取名稱唯一的方式為變數命名。