在go中,所有的字面值都是常量,被称为“无名常量”,false和true是预声明的两个具名常量。自定义具名常量使用关键字 const 定义,用于存储不会改变的数据。和C/C++中宏定义类似。
常量声明中的等号=表示“绑定”而非“赋值”。 每个常量描述将一个或多个字面量绑定到各自对应的具名常量上。 或者说,每个具名常量其实代表着一个字面常量。因为在编译阶段,所有的标识符将被它们各自绑定的字面量所替代。如果一个运算中的所有运算数都为常量,则此运算的结果也为常量。
存储在常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型,其它的类型不允许以常量的形式存在。go语言定义具名常量的格式如下:
const 常量名[数据类型] = 值
其中,数据类型是可选的,你可以省略类型说明符,因为编译器可以根据常量的值来推断其类型。例如:
package main
import "fmt"
func main() {
const n = 10 // 隐式类型定义
const m int = 100 // 显式类型定义
fmt.Println(n)
fmt.Println(m)
}
包级常量声明中的常量描述的顺序并不重要。比如在上面的例子中, 常量描述No和Yes的顺序可以掉换一下。
类型不确定常量的默认类型和它们各自代表的字面量的默认类型是一样的。
类型推断是指在某些场合下,程序员可以在代码中使用一些类型不确定值, 编译器会自动推断出这些类型不确定值在特定情景下应被视为某些特定类型的值。
上述两条类型推断规则可以被视为隐式转换规则。
常量声明中的自动补全
const (
X float32 = 3.14
Y // 这里必须只有一个标识符
Z // 这里必须只有一个标识符
A, B = "Go", "language"
C, _
// 上一行中的空标识符是必需的(如果
// 上一行是一个不完整的常量描述的话)。
)
关于这个空白标识符,需要专门写一篇来讲一下。《go语言的空白标识符》
iota是Go中预声明(内置)的一个特殊的具名常量。 iota被预声明为0,但是它的值在编译阶段并非恒定。 当此预声明的iota出现在一个常量声明中的时候,它的值在第n个常量描述中的值为n(从0开始)。实际编程中的一个例子如下所示:
const (
Mon = iota + 1
Tue
Wed
Thu
Fri
Sat
Sun
)
结合常量声明自动补全,上面的代码相当于。
const (
Mon = iota + 1 // iota == 0
Tue = iota + 1 // iota == 1
Wed = iota + 1 // iota == 2
Thu = iota + 1 // iota == 3
Fri = iota + 1 // iota == 4
Sat = iota + 1 // iota == 5
Sun = iota + 1 // iota == 6
)
在一个包含多个常量描述的常量声明中,除了第一个常量描述,其它后续的常量描述都可以只包含标识符列表部分。 Go编译器将通过照抄前面最紧挨的一个完整的常量描述来自动补全不完整的常量描述。因此,上面的代码等价于下面的代码:
const (
X float32 = 3.14
Y float32 = 3.14
Z float32 = 3.14
A, B = "Go", "language"
C, _ = "Go", "language"
)
如果一个运算中的所有运算数都为常量,则此运算的结果也为常量。或者说,此运算将在编译阶段就被估值。
常量的类型转换T(v)结果一般仍然是一个常量。
const a uint16 = 12345 // 字面值12345类型是不确定值且可以表示为uint16,被隐式类型转换为uint16
const b float32 = 123.123 // 字面值123.123的类型是不确定值且可以表示为float32,被隐式类型转换为float32
const c float64 = float64(a) // 显式类型转换,a是uint16类型,和float64类型不一致,必须通过显式类型转
var 变量名[数据类型] = 值
和常量定义一样,其中的数据类型是可选的,你可以省略类型说明符,因为编译器可以做类型推断。
为了更加方便程序员书写代码,下面两段代码是等价的,但是第二段少写了两个var。
// 第一段代码
var a int
var b bool
var str string
// 第二段代码
var (
a int
b bool
str string
)
在一个函数体内声明的变量称为局部变量。 在任何函数体外声明的变量称为包级或者全局变量。Go语言有两种变量声明形式。一种称为标准形式,另一种称为短声明形式。 短声明形式只能用来声明局部变量。例如:
package main
import "fmt"
var g = 100 // 全局变量
func main() {
fmt.Println(g)
g := 200 // 局部变量, 短声明形式。
g, f: = g, 100
fmt.Println(g)
}
需要说明的是,go是编译器在编译时进行数据类型的推断,而不是在运行时。python这样的语言是在运行时(实际上,python只拥有运行时)进行数据类型推断。
go虽然是编译型语言,但是go被设计为拥有垃圾回收等机制,因此go还需要一个运行时。这个也需要专门开一篇文章讲述。[《go语言的运行时》](https://zy010101.blog.csdn.net/article/details/125839606)
还有一点需要说明的是,go的变量声明形式将类型说明符放在了最后面,它能在一定程度上减少C/C++的那种错误。例如:
package main
import "fmt"
var g = 100 // 全局变量
func main() {
var a, b, c int = 1, 2, 3 //a,b,c都是int类型
fmt.Println(a, b, c)
var j, k, l *int = &a, &b, &c // j,k,l都是int类型的指针
fmt.Println(j, k, l)
var (
q int = 1
w float32 = 3.3
e string = "qwe"
)
_, _, _ = q, w, e
}
最后一行使用了空白标识符,这是在go语言中常见的手段之一。因为go语言中的常量在绑定之后,如果不使用,是不会产生编译报错的。但是go中的局部变量则至少需要被有效使用(有效使用是指除了被赋值之外的地方使用)一次,才不会导致编译器报错。包级变量无此限制。变量j, k, l都是int类型的指针,如果在C/C++中,进行如下的定义。
int a, *b
那么a是int类型,而b是int类型的指针。
个人的一些废话,go是静态语言,但是不知道为什么搞了这个类型推断,是为了让写JavaScript的人快速上手吗?但是实际开发的时候,你就几乎一定会遇到类型转换,最后还是会回到这个问题。因为你的短声明实际上一个类型确定值,他不像是动态语言中的变量。所以我觉得在使用go的时候,还是需要指明类型。
变量是非常量,听起来像是废话。但是变量确实是“非常量“。
非常量复数值不能被转换为浮点数或整数类型。
非常量浮点数和整数值不能被转换为复数类型。
在非常量数值的转换过程中,溢出和舍入是允许的。当一个浮点数被转换为整数时,小数部分将被舍弃(向零靠拢)。