Go筆記-常量與變量

變量

變量聲明

var v1 int
var v2 string
var v3 [10]int // 數組
var v4 []int   // 數組切片
var v5 struct {
  f int
}
var v6 *int
var v7 map[string]int
var v8 func(a int) int // 函數
var i int8 = 0 等同於 i := (int8)(0)
var i *int8 = nil 等同於 j := (*int8)(nil)
var min, max uint = 1, 5
var i, j, k int // 都是int,零值是0
var a, b, c = true, 2.3, "foo" // 自動推導類型
var f, err = os.Open(name) // 經過調用函數賦值

變量初始化

var v1 int = 10
var v2 = 10     // 編譯器自動推導v2的類型
v3 := 10        // 編譯器自動推導v2的類型

出如今 := 左側的變量不該該是已經被聲明過的,不然會致使編譯錯誤,好比下面這個 寫法:數組

var i int 
i := 2

會致使相似以下的編譯錯誤:數據結構

no new variables on left side of :=

:= 不能在函數外使用,函數外的語法塊必須以關鍵字開始ide

val := 4 // 錯誤
var val int32 = 4 // 正確

變量賦值

多重賦值功能,好比下面這個交換i和j變量的語句:函數

i, j = j, i

從新聲明和從新賦值ui

a, err := f1.Close()
b, err := f2.Close()

err在兩條語句中都出現了,第一次的err是聲明,第二次的err只是從新賦值了,第二個語句中必須有至少一個變量是須要聲明的,才能使用:=this


從新賦值與定義新同名變量的區別指針

s := "abs"
fmt.Println(&s)

s, y := "hello", 20 // 從新賦值,與前s在同一層次的代碼塊中,且有新的變量被定義
fmt.Println(&s, y)  // 一般函數多返回值 err 會被重複使用

{
	s, z := 1000, 30 // 定義新同名變量,不在同一層次代碼塊
	fmt.Println(&s, z)
}

輸出:code

0xc08200c340
0xc08200c340 20
0xc08200c380 30

變量被從新賦值時不會改變地址ip

a := 100
b := "a"
fmt.Println(&a, &b)
a = 200
b = "b"
fmt.Println(&a, &b)

輸出:內存

0xc08200c3a0 0xc08200c3b0
0xc08200c3a0 0xc08200c3b0

平行賦值

a[i], a[j] = a[j], a[i]

覆蓋的問題: 在進行多個變量賦值時,編譯器會先計算賦值語句右邊全部的值,以後再進行賦值。

a[i] =1 , a[j] = 2
    a[i], a[j] = a[j], a[i]    <=>   a[i] ,a[j] = 2, 1

在賦值以前, 賦值語句右邊的全部表達式將會先進行求值, 而後再統一更新左邊變量的值.


來自[雨痕筆記]

多變量賦值時,先計算全部相關值,而後再從左到右依次賦值:

data, i := [3]int{0, 1, 2}, 0
i, data[i] = 2, 100 // (i=0) -> (i=2), (data[0] = 100)
fmt.Println(data)   // [100 1 2]

i被賦值成2以前,data[i]已經被計算成data[0]了。

再看一個右邊是否也計算的例子:

data, i := [3]int{0, 2, 4}, 1
i, data[i] = data[i], 100
fmt.Println(i, data) // 2 [0 100 4]

也就是說在賦值以前,左右兩邊都計算過了。

匿名變量

假設GetName()函數的定義以下,它返回3個值,分別爲firstName、lastName和nickName:

func GetName() (firstName, lastName, nickName string) { 
	return"May", "Chan", "Chibi Maruko" 
}

若只想得到nickName,則函數調用語句能夠用以下方式編寫:

_, _, nickName := GetName()

new()和make()

new:並不初始化內存,只是將其置零,並返回地址。如new(T),其返回一個指向新分配的 類型爲T值爲零的 指針。

t1 := new(T) // type *T
var t2 T     // type T

以後就能夠直接使用t1,t2了。

但有時候須要一個初始化構造器:

t := new(T)
t.f1 = v1
t.f2 = v2

也可使用複合文字(composite literal):

t := T{v1, v2} // type T
t := &T{v1, v2} // type *T

複合文字的域按順序排列,而且必須都存在。

經過field:value顯式地爲元素添加標號,則初始化能夠按任何順序出現,沒有出現的則對應爲零值

t := &T{f2:v2, f1:v1}

new(T)和&T{}是等價的,字段的值都是零值。

make:內建函數make(T, args)與new(T)的用途不同。它只用來建立slice,map和channel,而且返回一個初始化的(而不是置零),類型爲T的值(而不是*T)。之因此有所不一樣,是由於這三個類型的背後是象徵着,對使用前必須初始化的數據結構的引用。例如,slice是一個三項描述符,包含一個指向數據(在數組中)的指針,長度,以及容量,在這些項被初始化以前,slice都是nil的。對於slice,map和channel,make初始化內部數據結構,並準備好可用的值. 如make([]int, 10, 100)表明分配一個有100個int的數組,而後建立一個長度爲10,容量爲100的slice結構,並指向數組前10個元素上。而new([]int)返回一個指向新分配的,被置零的slice結構體的指針,即指向nilslice值的指針。

這些例子闡釋了new和make之間的差異。

var p *[]int = new([]int)       // allocates slice structure; *p == nil; rarely useful(不多使用)
var v  []int = make([]int, 100) // the slice v now refers to a new array of 100 ints

// Unnecessarily complex:(沒必要要的組合)
var p *[]int = new([]int)
*p = make([]int, 100, 100)

// Idiomatic:(符合語言習慣的)
v := make([]int, 100)

記住make只用於map,slice和channel,而且不返回指針。要得到一個顯式的指針,使用new進行分配,或者顯式地使用一個變量的地址。


常量

字面常量

-12 
3.14159265358979323846 // 浮點類型的常量
3.2+12i // 複數類型的常量
true // 布爾類型的常量
"foo" // 字符串常量

Go語言的字面常量是無類型的,只要這個常量在相應類型的值域範圍內,就能夠做爲該類型的常量,好比12,它能夠賦值給int、uint、int3二、int6四、float3二、float6四、complex6四、complex128等類型的變量。

常量定義

存儲在常量中的數據類型只能夠是布爾型、數字型(整數型、浮點型和複數)和字符串型. 經過const關鍵字,你能夠給字面常量指定一個友好的名字:

const Pi float64= 3.14159265358979323846 
const zero = 0.0 // 無類型浮點常量
const (
	size int64= 1024 
	eof = -1 // 無類型整型常量
) 
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重賦值
const a, b, c = 3, 4, "foo" 
// a = 3, b = 4, c = "foo", 無類型整型和字符串常量

Go的常量定義能夠限定常量類型,但不是必需的。若是定義常量時沒有指定類型,那麼它與字面常量同樣,是無類型常量。 一個沒有指定類型的常量被使用時,會根據其使用環境而推斷出它所須要具有的類型。換句話說,未定義類型的常量會在必要時刻根據上下文來得到相關類型。

var n int
f(n + 5) // 無類型的數字型常量 「5」 它的類型在這裏變成了 int

常量定義的右值也能夠是一個在編譯期運算的常量表達式,好比

const mask = 1 << 3

因爲常量的賦值是一個編譯期行爲,因此右值不能出現任何須要運行期才能得出結果的表達式,好比試圖以以下方式定義常量就會致使編譯錯誤:

const Home = os.GetEnv("HOME")

緣由很簡單,os.GetEnv()只有在運行期才能知道返回結果,在編譯期並不能肯定,因此沒法做爲常量定義的右值。

const (
	a = "abc"
	b = len(a)
	c = unsafe.Sizeof(b)
)
println(b, c) // 3 8

const x = "xxx" // 未使用的局部變量不會引起編譯錯誤

在常量組中,如不提供類型和初始化值,那麼視做與上一常量相同:

const (
	s = "abc"
	x
)
println(x) // abc

枚舉的常量都爲同一類型時, 可使用簡單序列格式

const (
    a = iota    // a int32 = 0
    b            // b int32 = 1
    c            // c int32 = 2
)

枚舉序列中的未指定類型的常量會跟隨序列前面最後一次出現類型定義的類型

const (
    a byte = iota    // a uint8 = 0
    b                // b uint8 = 1
    c                // c uint8 = 2
    d rune = iota    // d int32 = 3
    e                // e int32 = 4
    f                // f int32 = 5
)

跳過一個iota

const (
		a byte = iota
		b
		_ // 跳過
		c
	)
	fmt.Println(a, b, c) // 0 1 3

在同一常量組中,能夠提供多個iota,他們各自增加:

const (
	a, b = iota, iota << 10 // 0, 0<<10
	c, d                    // 1, 1<<10
)
println(a, b, c, d) // 0 0 1 1024

若是iota自增被打斷,須顯示恢復

const (
	a = iota // 0
	b        // 1
	c = "c"  // c
	d        // c,與上一行相同
	e = iota // 4, 顯示恢復,注意計數包含了 c, d 兩行
	f        // 5
)

注意e的值


自定義函數不能爲常量賦值

const (
	a = iota
	//b = getNumber() 錯誤
	c = len([3]int{})
)

func getNumber() int {
	return 1
}

由於在編譯期間自定義函數均屬於未知,所以沒法用於常量的賦值,但內置函數可使用,如:len() 。


數字型的常量是沒有大小和符號的,而且可使用任何精度而不會致使溢出

const Ln2= 0.693147180559945309417232121458\
            176568075500134360255254120680009
const Log2E= 1/Ln2 // this is a precise reciprocal
const Billion = 1e9 // float constant
const hardEight = (1 << 100) >> 97

反斜槓 \ 能夠在常量表達式中做爲多行的鏈接符使用. 不過須要注意的是,當常量賦值給一個精度太小的數字型變量時,可能會由於沒法正確表達常量所表明的數值而致使溢出,這會在編譯期間就引起錯誤.


能夠經過自定義類型來實現枚舉類型限制:

type Color int
const (
	Black Color = iota
	Red
	Blue
)

func test(c Color) {}

func main() {
	c := Black
	test(c)

	//	x := 1
	//	test(x) // Error: cannot use x (type int) as type Color in argument to test

	test(1) // 常量會被編譯器自動轉換
}

預約義常量

Go語言預約義了這些常量:true、false和iota iota比較特殊,能夠被認爲是一個可被編譯器修改的常量,在每個const關鍵字出現時被重置爲0,而後在下一個const出現以前,每出現一次iota,其所表明的數字會自動增1。 從如下的例子能夠基本理解iota的用法:

const(    // iota被重設爲0
	c0 = iota  // c0 == 0 
	c1 = iota  // c1 == 1 
	c2 = iota  // c2 == 2 
) 
const( 
	a = 1 << iota   // a == 1 (iota在每一個const開頭被重設爲0) 
	b = 1 << iota   // b == 2 
	c = 1 << iota   // c == 4 
) 
const( 
	u = iota * 42  // u == 0 
	v float64 = iota * 42  // v == 42.0 
	w = iota * 42  // w == 84 
) 
constx = iota  // x == 0 (由於iota又被重設爲0了) 
consty = iota  // y == 0 (同上)
const (
		e, f, g = iota, iota, iota //e=0,f=0,g=0 iota在同一行值相同
	)

若是兩個const的賦值語句的表達式是同樣的,那麼能夠省略後一個賦值表達式。所以,上面的前兩個const語句可簡寫爲:

const(      // iota被重設爲0
	c0 = iota  // c0 == 0 
	c1       // c1 == 1 
	c2      // c2 == 2 
) 
const( 
	a = 1 << iota    // a == 1 (iota在每一個const開頭被重設爲0) 
	b       // b == 2 
	c       // c == 4 
)

枚舉

Go語言並不支持衆多其餘語言明確支持的enum關鍵字。 下面是一個常規的枚舉表示法,其中定義了一系列整型常量:

const( 
	Sunday = iota
	Monday 
	Tuesday 
	Wednesday 
	Thursday 
	Friday 
	Saturday 
	numberOfDays  // 這個常量沒有導出
)

同Go語言的其餘符號(symbol)同樣,以大寫字母開頭的常量在包外可見。 以上例子中numberOfDays爲包內私有,其餘符號則可被其餘包訪問

相關文章
相關標籤/搜索