常量与枚举(6)"/>
GO学习笔记——GO语言常量与枚举(6)
看完了变量的定义,再来看常量。
GO语言中常量定义使用关键字const,这个关键字倒是和C++相像。
常量的定义方法和变量定义差不多,这里就一笔带过了。
1. 定义一个简单常量,同样可以把它定义成局部变量或者是全局变量
const a int = 5func main() {const b int = 5fmt.Println(a,b)
}
2.分组定义常量
const (a int = 5b int = 5
)func main() {fmt.Println(a,b)
}
3.一行定义多个常量
func main() {const a,b int = 5,5fmt.Println(a,b)
}
4.省略类型的常量定义
func main() {const a,b = 5,5fmt.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,5fmt.Println(a,b)
}//上面这就是变量的定义了,不是常量func main() {const a intfmt.Println(a)
}//错误提示
.\main.go:9:10: missing value in const declarationfunc main() {const a,b := 5,5fmt.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,4var c intc = 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,4var c intc = 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 (EASTSOUTHWESTNORTH
)func main() {fmt.Println(EAST,SOUTH,WEST,NORTH)
}
诚然,因为这些常量没有定义,编译器会报错
.\main.go:6:2: missing value in const declaration
那么我们就可以给它们来手动定义
const (EAST = 0SOUTH = 1WEST = 2NORTH = 3
)func main() {fmt.Println(EAST,SOUTH,WEST,NORTH)
}
输出的结果就是预期的
0 1 2 3
那么如果这个我们想要表达的枚举类型很多怎么办?我们都手动定义吗,不能像C++那样从0开始自动增长吗?
为了解决这个问题,引入了常量计数器iota,来看下GO语言是怎么做到自动增长的
const (EAST = iotaSOUTHWESTNORTH
)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为0b //新增了一行常量定义,iota++,b为1c //新增了一行常量定义,iota++,c为2)fmt.Println(a,b,c)
}
输出结果如下
0 1 2
3.出现关键字const时,iota就会被重置为0(可能一个程序要声明多个枚举类型,就需要这个条件)
func main() {const (a = iotabc)const (d = iotaef)fmt.Println(a,b,c)fmt.Println(d,e,f)
}
输出结果如下
0 1 2
0 1 2
iota有多种使用方法,一起来看一下
1. 跳值使用法
func main() {const (a = iota //a是0b //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是0b = 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 + 2c,de //格式不一致)fmt.Println(a,b,c,d)
}
上面程序格式不一致会出错
.\main.go:16:3: extra expression in const declaration
更多推荐
GO学习笔记——GO语言常量与枚举(6)
发布评论