go語言入門-常量(const和itoa)
定義
常量:常量標識恆定不變的值,區別於變量var。var-變量不賦值存在默認值,但是常量聲明是必須顯示賦值。
常量關鍵字:const
常量不能使用 “:=” 語法定義。
聲明方式
普通聲明方式
const E float64 = 1.602
const EE = 1.602 // 類型可以省略
批量聲明方式
常用聲明使用方式
const (
A = 'A'
B = 'B'
C = 'C'
)
func main() {
//fmt.Println(E)
fmt.Println(A)
fmt.Println(B)
fmt.Println(C)
/***
output:
65
66
67
*/
}
批量聲明語法糖
- 如果沒有指定變量類型和初始化值,那麼同上一個常量值一樣(如果上一個常量沒值-則編譯報錯)
package main
import "fmt"
//const E float64 = 1.602
const (
//O //報錯,O前面沒有值
A = 'A'
B //使用常量A的值
C //使用常量A的值
)
func main() {
//fmt.Println(E)
fmt.Println(A)
fmt.Println(B)
fmt.Println(C)
/***
output:
65
65
65
*/
}
常量計數器
iota是go語言的常量計數器,因此只能用於常量,也就是域const關鍵字綁定,可以理解是iota是const語句中行索引【非聲明const的行忽略】。iota作用於itoa使用前的最近的const,如果itoa後續後又出現const,則會重置
const AA = iota // 針對當前const 行索引爲0
const BB = iota // 新const 行索引爲0
const CC = iota // 又是新的const 行索引0
fmt.Println(AA)
fmt.Println(BB)
fmt.Println(CC)
/**
output:
0
0
0
*/
以上案例可以看出 const CONSTNAME [type] = itoa 常量定義只能爲0。所以itoa只能在const ( xxx xxx xx) 批量常量聲明裏有用。如下:
const (
O = iota // 0
P // 1
Q // 2
)
fmt.Println(O)
fmt.Println(P)
fmt.Println(Q)
/**
output:
0
1
2
*/
以下批量常量聲明中存在空行,不做itoa計數累計
const (
O = iota // 0
// 沒有const變量聲明的空行是作爲itoa航索引累加的【非常量聲明的行忽略】
P // 1
Q // 2
)
fmt.Println(O)
fmt.Println(P)
fmt.Println(Q)
/**
output:
0
1
2
*/
特殊用法
- 使用 ‘_’忽略某一生成的計數器值
const (
_ = iota
b
c
_
e
)
fmt.Println(b, c, e)
/**
output:
1 2 4
*/
- itoa插隊(個人理解爲覆蓋掉itoa的計數值,計數器還在繼續)
const (
A = iota
B
C = 100 //插隊,
D
)
fmt.Println(A, B, C, D)
/**
output:
0 1 100 100
*/
爲啥 D不是3,而是100呢,因爲itoa計數在C被賦值100後,後續常量的值以字面值常量100爲準,但是itoa計數器還在計數,只是沒有賦值給常量,需要顯示的通過iota恢復
const (
A = iota
B
C = 100 //插隊,覆蓋掉iota的 值而已
C1 //使用100賦值,跟隨上一個常量C的顯示賦值 iota計數並有中指
D = iota //顯示恢復iota-賦值
E //因爲itoa被沒有終止所以5
)
fmt.Println(A, B, C, C1, D, E)
/**
output:
0 1 100 100 4 5
*/
- 多常量並列定義
在同一個常量組裏,可以再多個常量定義中使用iota,多個常量按照列單獨計數。互不干涉。
const (
aa, a = iota, iota // aa = 0 b=0
bb, _ //bb = 0, _忽略當前計數 增加
cc, c //計數增加
dd, d = iota, 100 //dd = 3, d 被重新賦值爲100,繼續計數
ee, e = 100, 100 // ee = 100, e = 100, 打斷計數複製動作,但是並沒有中短計數,
ff, f = iota, iota //ff = 5, f = 5 顯式恢復計數賦值
)
fmt.Println(aa, a)
fmt.Println(bb)
fmt.Println(cc, c)
fmt.Println(dd, d)
fmt.Println(ee, e)
fmt.Println(ff, f)
/**
output:
0 0
1
2 2
3 100
100 100
5 5
*/
- 從常量可以是編譯時能夠確定的表達式
/**
output:
0 0
1
2 2
3 100
100 100
5 5
*/
const (
hello = "hellooooooooooo" //字面值常量
worldLen = len("word") //字面值常量長度編譯時確定 4
ptrSize = unsafe.Sizeof(hello) //字符串底層爲一個值指向字符串的指正和字符串的長度。因此爲16.
/**
type stringStruct struct {
str unsafe.Pointer
len int
}
*/
)
var tmp int = 1
fmt.Println(hello, worldLen, ptrSize)
fmt.Println(unsafe.Sizeof(ptrSize), unsafe.Sizeof(tmp)) //unsafe.Pointer 8個字節, int 8個字節--》所以字符串的unsafe.Sizeof(str) = 16
/**
output:
hellooooooooooo 4 16
8 8
*/
用法
const和itoa模擬枚舉
go語言沒有關鍵字enum,一般是通過一組常量(等差、等比-有規則)來模擬實現枚舉類型,如下:
const (
Su = iota
Mo
Tu
We
Th
Fr
Sa
)
fmt.Println(Su, Mo, Tu, We, Th, Fr, Sa)
/**
output:
0 1 2 3 4 5 6
*/
實際編碼中,建議使用自定義類型實現枚舉類型,如下:
const (
SU Week = iota
MO
TU
WE
TH
FR
SA
)
func WeekPrinter(day Week) {
fmt.Printf("type:%T %#v\n", day, day)
}
func main() {
WeekPrinter(1) //1轉爲Week類型 隱式
WeekPrinter(SU) //
WeekPrinter(101) //101 不能識別取值範圍 101也是week的範圍內,字面值常量轉爲Week類型
a := 2
//WeekPrinter(a) //非字面值常量 int類型無法隱式轉爲week 必須顯示轉換
WeekPrinter(Week(a))
}
存在的問題:
- itoa模擬枚舉,無法限定範圍,如上圖的WeekPrinter(101) ,也需要了解字面值常量隱式轉換,普通變量必須手工顯示轉換類型。
常量和變量的區別
- 常量是隻讀,聲明賦值後無法修改,變量可以重複修改其內容值。常量通產會被編譯器在預處理階段直接展開,作爲指令數據使用。(參考《Go語言學習筆記》)
- 常量在運行時不分配存儲地址,變量會分配地址
var m = 1
const n = 10
fmt.Println(&m, m)
fmt.Println(&n, n) //報錯:cannot take the address of n