常量
上节,我们讲到 变量,这节我来看看常量,常量就是不变的量,恒定的量。
Go 支持常量。常量可以保证在编译阶段就计算出表达式的值,并不需要等到运行时,从而使得编译器可以在任何时候就知道这个值。
常量的本质其实属于基本类型,如布尔型、字符串或数字。
常量的创建方式与创建变量的方式相同,但我们使用 const 关键字代替 var 关键字:
package main import "fmt" func main() { const hello string = "Hello World" fmt.Printlb(x) }
如果我们更改一个常量的话,比如这样:
const hello string = "Say Hello to you" hello = "Hello, Bro"
会得到这样的提示:
Variables\main.go:6:8: cannot assign to hello (declared const)
常量是在程序中复用而不用每次都写出来的好方法。 例如 math 包中的圆周率 Pi 和自然常数 E 被定义为一个常数。
fmt.Println(math.E) // 结果为:2.718281828459045
让我们来计算一个圆的面积和周长,看如下代码:
package main import ( "fmt" "math" ) func main() { const ( hello string = "Hello," pi float64 = math.Pi ) var ( name string r float64 ) fmt.Print("Enter your name: ") fmt.Scanf("%s", &name) fmt.Println(hello + name) fmt.Print("让我们来学习圆,请输入圆的半径: ") fmt.Scanf("%f", &r) fmt.Printf("半径为%f的圆周长: %f\n", r, 2*pi*r) fmt.Printf("半径为%f的圆面积: %f", r, pi*r*r) }
运行后,得到结果如下:
Hello, Bro 半径为3的圆周长: 18.84955592153876 半径为3的圆面积: 28.274333882308138 半径为3的圆面积: 28.274333882308138
定义多个变量
Go 提供了定义多个变量的简写方式:
var ( a = 3 b = 4 c = 5 )
使用关键字 var(或 const)后跟括号,每个变量都定义在单独行上。
Demo
package main import ( "fmt" "math" ) func main() { const hello string = "Hello," const pi float64 = math.Pi var ( name string r float64 ) fmt.Print("Enter your name: ") fmt.Scanf("%s", &name) fmt.Println(hello + name) fmt.Print("让我们来学习圆,请输入圆的半径: ") fmt.Scanf("%f", &r) fmt.Printf("半径为%f的圆周长: %f\n", r, 2*pi*r) fmt.Printf("半径为%f的圆面积: %f", r, pi*r*r) }
运行结果:
$ go run main.go Enter your name: Rain Hello,Rain 让我们来学习圆,请输入圆的半径: 4 半径为4.000000的圆周长: 25.132741 半径为4.000000的圆面积: 50.265482
常量生成器 iota
常量的声明可以使用常量生成器 iota
,可以创建一系列相关的值,而不用逐个值显式写出。
在 iota
中,从 0 开始取值,逐个加 1,例如定义一星期,从 Sunday 开始,其值为 0。
package main import "fmt" type Weekday int const ( Sunday Weekday = iota Monday Tuesday Wednesday Thursday Friday Saturday ) func main() { fmt.Printf("Sunday 是一周的第 %d 天\n", Sunday) fmt.Printf("Monday 是一周的第 %d 天\n", Monday) fmt.Printf("Tuesday 是一周的第 %d 天\n", Tuesday) fmt.Printf("Wednesday 是一周的第 %d 天\n", Wednesday) fmt.Printf("Thursday 是一周的第 %d 天\n", Thursday) fmt.Printf("Friday 是一周的第 %d 天\n", Friday) fmt.Printf("Saturday 是一周的第 %d 天", Saturday) }
$ go run main.go Sunday 是一周的第 0 天 Monday 是一周的第 1 天 Tuesday 是一周的第 2 天 Wednesday 是一周的第 3 天 Thursday 是一周的第 4 天 Friday 是一周的第 5 天 Saturday 是一周的第 6 天
无类型常量
Go 的常量有一些特别之处,上面我们定义的常量都是基本数据类型,如 string
或 float64
,但许多常量并不从属于某一具体类型。
编译器将这些从属类型待定的常量表示成某些值,这些值比基本类型的数字精度更高,且算数精度高于原生的机器精度。
从属待定的常量共有6种:
- 无类型 布尔
- 无类型 整数
- 无类型 文字符号
- 无类型 浮点数
- 无类型 复数
- 无类型字符串
const ( deadbeef = 0xdeadbeef // 无类型整数,值为 3735928559 a = uint32(deadbeef) // uint32, 值为 3735928559 b = float32(deadbeef) // float32, 值为 3735928576(向上取整) c = float64(deadbeef) // float64, 值为 3735928559 d = int32(deadbeef) // 编译错误:溢出,int32无法容纳常量值 e = float64(1e309) // 编译错误:溢出,float64,无法容纳常量值 f = uint(-1) // 编译错误:溢出,uint 无法容纳常量值 )
consts\main.go:31:19: constant 3735928559 overflows int32 consts\main.go:32:21: constant 1e+309 overflows float64 consts\main.go:33:18: constant -1 overflows uint
借助于推迟确定从属类型,无类型常量不仅能维持更高的精度,与类型已确定的常量相比,它们还能写出更多表达式而无需转换类型。
总结
常量是一种创建命名标识符的方法,该标识符的值永远不会改变。它们还为语言提供了难以置信的灵活性。在 Go 中实现常量的方式非常独特。
- 常量不同于变量
- 常量只存在于编译期
- 无类型常量可以隐式转换,而类型常量和变量不能
- 无类型常量视为具有种类,而不是类型
- 了解显式和隐式转换