go語言入門-常量(const和itoa)

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
	 */
特殊用法
  1. 使用 ‘_’忽略某一生成的計數器值
	const (
		_ = iota
		b
		c
		_
		e
	)
	fmt.Println(b, c, e)
	/**
	output:
	1 2 4
	 */
  1. 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
*/
  1. 多常量並列定義
    在同一個常量組裏,可以再多個常量定義中使用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
	 */
  1. 從常量可以是編譯時能夠確定的表達式
	/**
	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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章