目錄
文章目錄
值語義和引用語義
在 C 語言中,所有變量都是值語義的。變量名所指向的內存位置,就是變量值所儲存的地方。指針變量亦如此,指針變量名所指向的內存位置,就是儲存指針變量值(一個內存地址)的地方。
在 Python 中,所有變量都是引用語義的。變量名所指向的內存位置所存儲的內容,實際上是變量值在內存中的地址。
而 Golang 則顯式的值語義和引用語義都引入了程序設計中,布爾型、數字類型、字符串類型都屬於值語義,變量名指向變量值。當使用 = 賦值運算符將一個變量的值賦值給另一個變量時,如:j = i,實際上是在內存中將變量 i 的數值進行了拷貝:
而 Golang 中更復雜的數據類型通常使用引用語義。一個引用類型的變量 r1 存儲的是 r1 的值所在的內存地址,或內存地址中的首地址。當使用賦值語句 r2 = r1 時,只有引用(地址)被複制,如果 r1 的值被改變了,那麼這個值的所有引用都會指向被修改後的內容,在這個例子中,r2 也會受到影響:
變量(var)
Golang 使用關鍵字 var 來聲明一個變量。格式如下:
var identifier type
也可以一次聲明多個變量,稱爲 “並行賦值”,也可用於接受多返回值函數的返回值:
var identifier1, identifier2 type
聲明全局變量:
// 這種因式分解關鍵字的寫法一般用於聲明全局變量
var (
identifier1 type
identifier2 type
)
注意:
- 不可以在同一個代碼塊中重複聲明一個同名變量。
- 在函數塊中,不可以僅聲明,但不可以一個變量。
- 全局變量是可以僅聲明,而不使用的。
- 並行賦值時,可以在賦值運算符的左側使用空白標識符 “_”,表示拋棄值,常用接受多返回值的函數調用,進行對齊補位。空白標識符的本質是一個只寫(wo)變量,你不能得到它的值。這樣做是因爲 Golang 中你必須使用所有被聲明的變量,但有時你並不需要使用從一個函數返回中得到的所有返回值。
指定數據類型的變量聲明
與 C 語言一般,可以在一條語句中完成變量的聲明、定義以及初始化。如果沒有初始化,則變量默認爲零值。
示例:
package main
import "fmt"
func main() {
var num int
fmt.Println(num)
var name string = "fanguiju"
fmt.Println(name)
}
注意,不同數據類型的零值亦不相同。例如:布爾類型的零值爲 false,數值類型的零值爲 0,字符串的零值爲空(""),以下幾種類型爲 nil:
var a *int // 指針
var a []int // 數組
var a map[string] int // Map 集合
var a chan int // channel
var a func(string) int // 函數
var a error // 接口
根據初始化數值自動判定數據類型的變量定義
示例:
package main
import "fmt"
func main() {
var b = true
fmt.Println(b)
}
簡短形似,使用 := 賦值運算符的變量聲明
上述可知,Golang 支持在初始化變量的時候省略顯式書寫數據類型。基於這樣的前提,聲明語句寫上 var 關鍵字就顯得有些多餘了。所以,Golang 可以使用 := 賦值運算符來省略書寫 var 關鍵字和數據類型的預定義標識符,是一種簡易的寫法,通過初始化的數值來確定變量的數據類型。
注意,:= 的簡短形式只能在函數中使用,否則編譯錯誤。
示例:
package main
import "fmt"
func main() {
f := "fanguiju" // 相對於:var f string = "fanguiju"
fmt.Println(f)
}
但需要注意的是,:= 運算符的左值必須是新聲明的變量。不能使用 := 來爲已經聲明或定義過的變量進行賦值,會導致編譯錯誤。
var intVal int
intVal := 1 // 編譯錯誤
同時聲明多個變量
例如:
var vname1, vname2, vname3 string = "A", "B", "C"
// or
var vname1, vname2, vname3 = "A", "B", "C"
// or
vname1, vname2, vname3 := "A", "B", "C"
常量(const)
Golang 中使用 const 關鍵字來聲明常量,常量的數據類型只可以是:布爾類型、數字類型和字符串類型。
聲明格式:
const identifier type = value
與變量聲明類似的,可以省略顯式書寫數據類型:
const b = "abc"
也可以進行並行賦值:
const c_name1, c_name2 = value1, value2
Golang 內置的標準函數中:len()、cap()、unsafe.Sizeof() 返回的都是一個常量,所以應該直接賦值與一個常量:
package main
import "unsafe"
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
func main() {
println(a, b, c)
}
使用 iota 常量來實現 “枚舉類型”
Golang 原生不具備 C 語言中的枚舉類型關鍵字 enum,但可以使用 const 關鍵字和 iota 常量來實現。
通常的,使用因式分解的方式聲明多個常量時,後一個常量成員會繼承前一個常量成員的數值(若前一個成員有初始化,後一個成員沒有初始化的情況下),如下:
package main
import "fmt"
const (
Unknown = 1
Female
Male
)
func main() {
fmt.Println(Unknown, Female, Male)
}
結果:
1 1 1
再者,iota 是一個特殊的常量,其數值可以被編譯器所修改。iota 在 const 關鍵字出現時會被重置爲 0,const 中每新增一行常量聲明會使 iota 計數一次。所以,iota 的本質可理解爲 const 語句塊中的行索引。
package main
import "fmt"
const (
a = iota
b = iota
c = iota
)
func main() {
fmt.Println(a, b, c)
}
結果:
0 1 2
也可以簡寫爲如下形式:
const (
a = iota
b
c
)
示例:
package main
import "fmt"
func main() {
const (
a = iota // 0
b // 1
c // 2
d = "ha" // 獨立值,iota += 1
e // "ha" iota += 1
f = 100 // iota +=1
g // 100 iota +=1
h = iota // 7,恢復計數
i // 8
)
fmt.Println(a, b, c, d, e, f, g, h, i)
}
運行結果:
0 1 2 ha ha 100 100 7 8
再看個有趣的的 iota 示例:
package main
import "fmt"
const (
i = 1 << iota // << 爲位左移運算符
j = 3 << iota
k
l
)
func main() {
fmt.Println("i=", i)
fmt.Println("j=", j)
fmt.Println("k=", k)
fmt.Println("l=", l)
}
結果:
i= 1
j= 6
k= 12
l= 24
iota 表示從 0 開始自動加 1,所以 i=1<<0, j=3<<1(<< 表示左移的意思),即:i=1, j=6,這沒問題,關鍵在 k 和 l,從輸出結果看 k=3<<2,l=3<<3。
簡單表述:
- i=1:左移 0 位,二進制 001,不變仍爲 1;
- j=3:左移 1 位,二進制 110,即 6;
- k=3:左移 2 位,二進制 1100,即 12;
- l=3:左移 3 位,二進制 11000,即 24。
作用域
作用域是已聲明標識符所表示的常量、類型、變量、函數或包在源代碼中的作用範圍。
局部變量
在函數體內聲明的變量稱之爲局部變量,它們的作用域只在函數體內,形式參數和返回值變量也是局部變量。
package main
import "fmt"
func main() {
/* 聲明局部變量 */
var a, b, c int
/* 初始化參數 */
a = 10
b = 20
c = a + b
fmt.Printf ("結果: a = %d, b = %d and c = %d\n", a, b, c)
}
全局變量
在函數體外聲明的變量稱之爲全局變量,全局變量可以在整個包甚至外部包(被導出後)使用。全局變量可以在任何函數中使用。
package main
import "fmt"
/* 聲明全局變量 */
var g int
func main() {
/* 聲明局部變量 */
var a, b int
/* 初始化參數 */
a = 10
b = 20
g = a + b
fmt.Printf("結果: a = %d, b = %d and g = %d\n", a, b, g)
}
注意:當全局變量和局部變量重名時,優先考慮局部變量。
package main
import "fmt"
/* 聲明全局變量 */
var g int = 20
func main() {
/* 聲明局部變量 */
var g int = 10
fmt.Printf ("結果: g = %d\n", g)
}