在 Go 语言中,术语”常量”用于表示固定的值 常量是一个简单值的标识符,在程序运行时, 不会被修改的量 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型, 比如5 、-89、67.89、2+2i 、Go、等等
常量的定义格式
const identifier [type] = value
你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型
- 显式类型定义:const b string = "abc"
- 隐式类型定义:const b = "abc"
多个相同类型的声明可以简写为:
const c_name1, c_name2 = value1, value2
常量注意事项
package mainimport "fmt"func main() {const LENGTH int = 10const WIDTH int = 5var area intarea = LENGTH * WIDTHfmt.Printf("面积为 : %d\n", area)const a, b, c = 1, false, "str" //多重赋值println(a, b, c)a = 2 //cannot assign to a}/*面积为 : 501 false str*/
⚠️注意: 常量不能被再次赋值
package mainimport ("fmt""math")func main() {fmt.Println("Hello, playground")var a = math.Sqrt(4) // 允许const b = math.Sqrt(4) // 不允许,const initializer math.Sqrt(4) is not a constant}
字符串常量
双引号中的任何值都是 Go 中的字符串常量。例如像 Hello World 或 Ken 等字符串在 Go 中都是常量
什么类型的字符串属于常量?答案是他们是无类型的
像 Hello World 这样的字符串常量没有任何类型
const hello = "Hello World"
上面的例子,我们把 Hello World 分配给常量 hello。现在常量 hello 有类型吗?答案是没有。常量仍然没有类型
Go 是一门强类型语言,所有的变量必须有明确的类型。那么, 下面的程序是如何将无类型的常量 Sam 赋值给变量 name 的呢?
package mainimport ("fmt")func main() {var name = "Ken"fmt.Printf("type %T value %v", name, name)}/* type string value Ken */
答案是无类型的常量有一个与它们相关联的默认类型,并且当且仅当一行代码需要时才提供它。在声明中 var name = “Sam” , name 需要一个类型,它从字符串常量 Sam 的默认类型中获取
有没有办法创建一个带类型的常量?答案是可以的。以下代码创建一个有类型常量
const typedhello string = "Hello World"
上面代码中, typedhello 就是一个 string 类型的常量。
Go 是一个强类型的语言,在分配过程中混合类型是不允许的。让我们通过以下程序看看这句话是什么意思。
package mainfunc main() {var defaultName = "Sam" // 允许type myString stringvar customName myString = "Sam" // 允许customName = defaultName // 不允许}
在上面的代码中,我们首先创建一个变量 defaultName 并分配一个常量 Sam 。常量 Sam 的默认类型是 string ,所以在赋值后 defaultName 是 string 类型的
下一行,我们将创建一个新类型 myString,它是 string 的别名
然后我们创建一个 myString 的变量 customName 并且给他赋值一个常量 Sam 。因为常量 Sam 是无类型的,它可以分配给任何字符串变量。因此这个赋值是允许的,customName 的类型是 myString。
现在,我们有一个类型为 string 的变量 defaultName 和另一个类型为 myString 的变量 customName。即使我们知道这个 myString 是 string 类型的别名。Go 的类型策略不允许将一种类型的变量赋值给另一种类型的变量。因此将 defaultName 赋值给 customName 是不允许的,编译器会抛出一个错误 main.go:7:20: cannot use defaultName (type string) as type myString in assignmen。
布尔常量
布尔常量和字符串常量没有什么不同。他们是两个无类型的常量 true 和 false。字符串常量的规则适用于布尔常量,所以在这里我们不再重复。以下是解释布尔常量的简单程序
package mainfunc main() {const trueConst = truevar defaultBool = trueConst // 允许type myBool boolvar customBool myBool = trueConst // 允许defaultBool = customBool // 不允许}
数字常量
数字常量包含整数、浮点数和复数的常量。数字常量中有一些微妙之处
package mainimport ("fmt")func main() {const a = 5var intVar int = avar int32Var int32 = avar float64Var float64 = avar complex64Var complex64 = afmt.Println("intVar",intVar, "\nint32Var", int32Var, "\nfloat64Var", float64Var, "\ncomplex64Var",complex64Var)}/*结果输出:intVar 5int32Var 5float64Var 5complex64Var (5+0i)*/
上面的程序,常量 a 是没有类型的,它的值是 5 。您可能想知道 a 的默认类型是什么,如果它确实有一个的话, 那么我们如何将它分配给不同类型的变量。答案在于 a 的语法。下面的程序将使事情更加清晰
package mainimport ("fmt")func main() {var i = 5var f = 5.6var c = 5 + 6ifmt.Printf("i's type %T, f's type %T, c's type %T", i, f, c)}
在上面的程序中,每个变量的类型由数字常量的语法决定。5 在语法中是整数, 5.6 是浮点数,5+6i 的语法是复数。当我们运行上面的程序,它会打印出 i’s type int, f’s type float64, c’s type complex128
数字表达式
数字常量可以在表达式中自由混合和匹配,只有当它们被分配给变量或者在需要类型的代码中的任何地方使用时,才需要类型
package mainimport ("fmt")func main() {var a = 5.9/8fmt.Printf("a's type %T value %v",a, a)}/*输出:a's type float64 value 0.7375*/
常量用作枚举
const (Unknown = 0Female = 1Male = 2)
数字 0、1 和 2 分别代表未知性别、女性和男性
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过
package mainimport("unsafe")const (a = "abc"b = len(a)c = unsafe.Sizeof(a))func main(){println(a, b, c)}/*以上实例运行结果为:abc 3 16*/
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)
iota 可以被用作枚举值
const (a = iotab = iotac = iota)
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
const (a = iotabc)
iota 用法进阶
package mainimport "fmt"func main() {const (a = iota //0b //1c //2d = "ha" //独立值,iota += 1e //"ha" iota += 1f = 100 //iota +=1g //100 iota +=1h = iota //7,恢复计数i //8)fmt.Println(a,b,c,d,e,f,g,h,i)}/*以上实例运行结果为:0 1 2 ha ha 100 100 7 8*/
iota 用法升华
package mainimport "fmt"const (i=1<<iotaj=3<<iotak=3<<iotal)func main() {fmt.Println("i=",i)fmt.Println("j=",j)fmt.Println("k=",k)fmt.Println("l=",l)}/*以上实例运行结果为:i= 1j= 6k= 12l= 24*/
iota 从 0 开始随着变化不断叠加+1, 注:<<n==*(2^n) ,从输出结果看,简单表述:
- i=1 左移 0 位,没有发生变化,即1 (i=1<<0)
- j=3 左移 1 位,变为二进制 110, 即6(j=3<<1)
- k=3 左移 2 位,变为二进制 1100, 即12(k=3<<2)
- l=3 左移 3 位,变为二进制 11000,即 24(l=3<<3)
