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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章