GO学习笔记——GO语言常量与枚举(6)

看完了变量的定义,再来看常量。

GO语言中常量定义使用关键字const,这个关键字倒是和C++相像。

常量的定义方法和变量定义差不多,这里就一笔带过了。

1. 定义一个简单常量,同样可以把它定义成局部变量或者是全局变量

const a int = 5

func main() {
	const b int = 5
	fmt.Println(a,b)
}

2.分组定义常量

const (
	a int = 5
	b int = 5
)

func main() {
	fmt.Println(a,b)
}

3.一行定义多个常量

func main() {
	const a,b int = 5,5
	fmt.Println(a,b)
}

4.省略类型的常量定义

func main() {
	const a,b  = 5,5
	fmt.Println(a,b)
	fmt.Println(reflect.TypeOf(a),reflect.TypeOf(b))
}

//输出结果

5 5
int int

可以看到常量的定义和变量是差不多的,只不过有几个地方需要注意

  • 常量定义的时候一定要加上const关键字。之前说变量定义的时候,如果是局部变量我们可以省略var关键字,但是如果在函数体内定义常量,不能省略const关键字,很简单,我们省略了const,那不就和变量一样了吗?所以一句话,常量定义一定要加上const关键字
  • 常量定义的同时必须赋值,也就是说我们不能够在常量的定义的时候不可以像变量那样不赋值,只声明,从而使用默认零值。常量定义的时候必须同时赋值
  • 因为必须使用const关键字,所以常量也没有想变量定义那样的简化方法,即使用冒号 ':' 定义,因为使用冒号 ':' 定义,是因为我们省略了var关键字,这边必须使用const关键字,所以也没有这种定义的方法。
func main() {
	a,b :=  5,5
	fmt.Println(a,b)
}

//上面这就是变量的定义了,不是常量

func main() {
	const a int
	fmt.Println(a)
}

//错误提示
.\main.go:9:10: missing value in const declaration

func main() {
	const a,b := 5,5
	fmt.Println(a,b)
}

//错误提示
.\main.go:9:8: missing value in const declaration
.\main.go:9:12: syntax error: unexpected := at end of statement

另外,还有一个点需要注意,这个是编译器方面的推断类型导致的,看如下代码

func main() {
	a,b :=  3,4
	var c int
	c = int (math.Sqrt(a*a + b*b))
	fmt.Println(c)
}

程序会报错,因为sqrt函数的参数浮点类型,而这里a和b是变量,编译器将a和b推断成了int类型,要变成浮点类型必须强转

.\main.go:12:25: cannot use a * a + b * b (type int) as type float64 in argument to math.Sqrt

但是如果a和b是常量,就可以了

func main() {
	const a,b  = 3,4
	var c int
	c = int (math.Sqrt(a*a + b*b))
	fmt.Println(c)
}

//输出结果
5

这是因为,常量其实本质上做的是一个文本替换,这里的a和b它既可以是int,也可以是浮点型,编译器不管这个,编译器只将a和b出现的地方用3和4代替了,实际上,那句sqrt相当于下面的表达式

c = int (math.Sqrt(25))

这是常量和变量在省略类型时的一点参数,只要注意常量的话它会是一个文本替换,可以作为任意类型使用,而变量是推断出具体类型。

5.使用内置表达式来定义厂常量

还可以使用一些内置表达式来定义常量,如:len(),unsafe.Sizeof()等

const name = "pigff"
const length = len(name)
const size = unsafe.Sizeof(name)    //用于求变量大小

func main() {
	fmt.Println(name,length,size)
}

输出结果

pigff 5 16

注意看,这里为什么Sizeof一个字符串的大小会是16?原因是GO中的sring内部实现由两部分组成,一部分是指向字符串起始地址的指针,另一部分是字符串的长度,两部分各是8字节,所以一共16字节。这部分具体会在后面的文章说到,这里提一下。

和C++做个对比,len其实相当于C++中string类的length接口,而C++中对于字符串求sizeof的大小是根据编译器决定的,不同平台下求出的结果也不一样,vs2017下求出来时28,而Linux下用g++求出来就是8,具体要看平台,编译器,以及编译器版本。

自己实现的函数,不可以这样用来赋值给常量

func fun(name string) string{
	return name
}
const name = fun("pigff")
func main() {
	fmt.Println(name)
}

程序会报错

.\main.go:11:7: const initializer fun("pigff") is not a constant

所以只有内置的表达式和函数才可以这么定义。


使用常量定义枚举类型

GO语言中有一个特殊常量——枚举类型,C++中有enum关键字来定义枚举类型,而GO语言中则没有这样的关键字,GO中使用一组const常量来表示枚举类型。

我们知道,枚举类型的数值是从0开始的,为了对比,我们先来看一下一段C++代码。

#include <iostream>
using namespace std;

enum{
    EAST,
    SOUTH,
    WEST,
    NORTH
};

int main(){
    int a[] = {EAST,SOUTH,WEST,NORTH};
    for(int i = 0; i < 4; ++i)
        cout << a[i] << " ";
    cout << endl;
    return 0;
}

我们在main函数中对这几个枚举值一一输出它们的值,结果如下

0 1 2 3

可以看到确实是从0开始的,那么这样的枚举类型在GO语言中是如何实现的呢?


GO使用一组const来表示枚举类型,并使用了iota这个常量计数器

先试着像C++那样定义枚举类型

const (
	EAST
	SOUTH
	WEST
	NORTH
)

func main() {
	fmt.Println(EAST,SOUTH,WEST,NORTH)
}

诚然,因为这些常量没有定义,编译器会报错

.\main.go:6:2: missing value in const declaration

那么我们就可以给它们来手动定义

const (
	EAST = 0
	SOUTH = 1
	WEST = 2
	NORTH = 3
)

func main() {
	fmt.Println(EAST,SOUTH,WEST,NORTH)
}

输出的结果就是预期的

0 1 2 3

那么如果这个我们想要表达的枚举类型很多怎么办?我们都手动定义吗,不能像C++那样从0开始自动增长吗?

为了解决这个问题,引入了常量计数器iota,来看下GO语言是怎么做到自动增长的

const (
	EAST = iota
	SOUTH
	WEST
	NORTH 
)

func main() {
	fmt.Println(EAST,SOUTH,WEST,NORTH)
}

输出结果和上面一样

0 1 2 3

好了,下面来具体了解一下这个iota到底是个啥


常量计数器iota

iota是GO语言的常量计数器,只可以在常量表达式中使用(给常量赋值)

  • iota在const关键字出现时将被重置为0
  • const中每新增一行常量声明将使iota计数+1(注意在同一行计数不+1)

上面就是使用iota这个常量计数器的关键。

1. iota只可以给常量比表达式使用,其余地方不可使用

func main() {
	fmt.Println(iota)
}

程序会报错,以为iota是一个变量名

.\main.go:13:14: undefined: iota

2.const中每新增一行常量声明将使iota计数+1(这一般出现在分组定义常量的时候)

func main() {
	const (
		a = iota      //iota为0,a为0
		b             //新增了一行常量定义,iota++,b为1
		c             //新增了一行常量定义,iota++,c为2
	)
	fmt.Println(a,b,c)
}

输出结果如下 

0 1 2

3.出现关键字const时,iota就会被重置为0(可能一个程序要声明多个枚举类型,就需要这个条件)

func main() {
	const (
		a = iota
		b
		c
	)

	const (
		d = iota
		e
		f
	)
	fmt.Println(a,b,c)
	fmt.Println(d,e,f)
}

输出结果如下

0 1 2
0 1 2

iota有多种使用方法,一起来看一下


1. 跳值使用法

func main() {
	const (
		a = iota 	//a是0
		b		//b是1
		_		//2被跳过了
		c		//c是3
	)

	fmt.Println(a,b,c)
}

输出结果

0 1 3

2. 插队使用法

在a和c之间插入一个不是iota赋值的常量b,这个时候iota还是会+1

func main() {
	const (
		a = iota 	//a是0
		b = 3		//b是3,iota仍旧++
		c = iota	//c是2,不是0,此时iota是2
	)

	fmt.Println(a,b,c)
}

输出结果

0 3 2

3. 表达式隐式使用法

如果后面的常量没有赋值,那么会向上找第一个不为空的赋值表达式

func main() {
	const (
		a = iota * 2	//a是0,iota++
		b = iota	//b是1,iota++
		c = iota	//c是2
	)

	fmt.Println(a,b,c)
}

这个时候,把b和c的赋值都省略

func main() {
	const (
		a = iota * 2	//a是0,iota++
		b 		//b是2,iota++
		c 		//c是4
	)

	fmt.Println(a,b,c)
}

输出结果如下

0 2 4

验证一下,会向上使用第一个非空的赋值表达式

func main() {
	const (
		a = iota * 2	//a是0,iota++
		b = iota * 3	//b是3,iota++
		c 			//c是6,沿用了b的赋值表达式,而不是a的
		d			//d是9,沿用了b的赋值表达式,而不是a的
	)

	fmt.Println(a,b,c,d)
}

输出结果如下

0 3 6 9 

因此,通过这种方法,可以有多元化的常量赋值,不一定是自增长的,也可以是二倍自增的,这也是GO语言枚举类型的一个巧妙之处

4.单行使用法

格式一致,沿用之前对应的赋值格式

func main() {
	const (
		a,b = iota,iota + 2    //a为0,b为2,iota++
		c,d                    //等价于c = iota,d = iota + 2,所以c为1,b为3
	)

	fmt.Println(a,b,c,d)
}

输出结果

0 2 1 3

注意格式必须一致,格式不一致就会导致出错的问题(即第一行是两个变量,后面的也必须是两个变量)

func main() {
	const (
		a,b = iota,iota + 2
		c,d
		e    //格式不一致
	)

	fmt.Println(a,b,c,d)
}

上面程序格式不一致会出错

.\main.go:16:3: extra expression in const declaration

猜你喜欢

转载自blog.csdn.net/lvyibin890/article/details/83145359