Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个
简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意
数量的字母、数字或下划线。大写字母和小写字母是不同的:heapSort和Heapsort是两个不
同的名字。
Go语言中类似if和switch的关键字有25个;关键字不能用于自定义名字,只能在特定语法结构
中使用。
break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var
此外,还有大约30多个预定义的名字,比如int和true等,主要对应内建的常量、类型和函数。
内建常量: true false iota nil 内建类型: int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex128 complex64 bool byte rune string error 内建函数: make len cap new append copy close delete complex real imag panic recover
这些内部预先定义的名字并不是关键字,你可以再定义中重新使用它们。在一些特殊的场景
中重新定义它们也是有意义的,但是也要注意避免过度而引起语义混乱。
如果一个名字是在函数内部定义,那么它的就只在函数内部有效。如果是在函数外部定义,
那么将在当前包的所有文件中都可以访问。名字的开头字母的大小写决定了名字在包外的可
见性。如果一个名字是大写字母开头的(译注:必须是在函数外部定义的包级名字;包级函
数名本身也是包级名字),那么它将是导出的,也就是说可以被外部的包访问,例如fmt包的
Printf函数就是导出的,可以在fmt包外部访问。包本身的名字一般总是用小写字母。
声明
Go语言主要有四种类型的声明
语句:var、const、type和func,分别对应变量、常量、类型和函数实体对象的声明
package main import "fmt" const boilingF = 212.0 func main() { var f = boilingF var c = (f - 32) * 5 / 9 fmt.Printf("boiling point = %g°F or %g°C\n", f, c) // Output: // boiling point = 212°F or 100°C }
其中常量boilingF是在包一级范围声明语句声明的,然后f和c两个变量是在main函数内部声明
的声明语句声明的。在包一级声明语句声明的名字可在整个包对应的每个源文件中访问,而
不是仅仅在其声明语句所在的源文件中访问。相比之下,局部声明的名字就只能在函数内部
很小的范围被访问。
package main import "fmt" func main() { const freezingF, boilingF = 32.0, 212.0 fmt.Printf("%g°F = %g°C\n", freezingF, fToC(freezingF)) // "32°F = 0°C" fmt.Printf("%g°F = %g°C\n", boilingF, fToC(boilingF)) // "212°F = 100°C" } func fToC(f float64) float64 { return (f - 32) * 5 / 9 }
一个函数的声明由一个函数名字、参数列表(由函数的调用者提供参数变量的具体值)、一
个可选的返回值列表和包含函数定义的函数体组成。如果函数没有返回值,那么返回值列表
是省略的。执行函数从函数的第一个语句开始,依次顺序执行直到遇到renturn返回语句,如
果没有返回语句则是执行到函数末尾,然后返回到函数调用者。
变量
var声明语句可以创建一个特定类型的变量,然后给变量附加一个名字,并且设置变量的初始
值。变量声明的一般语法如下:
var 变量名字 类型 = 表达式
其中“类型”或“= 表达式”两个部分可以省略其中的一个。如果省略的是类型信息,那么将根据
初始化表达式来推导变量的类型信息。如果初始化表达式被省略,那么将用零值初始化该变
量。 数值类型变量对应的零值是0,布尔类型变量对应的零值是false,字符串类型对应的零
值是空字符串,接口或引用类型(包括slice、map、chan和函数)变量对应的零值是nil。数
组或结构体等聚合类型对应的零值是每个元素或字段都是对应该类型的零值。
零值初始化机制可以确保每个声明的变量总是有一个良好定义的值,因此在Go语言中不存在
未初始化的变量。这个特性可以简化很多代码,而且可以在没有增加额外工作的前提下确保
边界条件下的合理行为。
Go语言程序员应该
让一些聚合类型的零值也具有意义,这样可以保证不管任何类型的变量总是有一个合理有效
的零值状态。
可以在一个声明语句中同时声明一组变量,或用一组初始化表达式声明并初始化一组变
量。如果省略每个变量的类型,将可以声明多个类型不同的变量(类型由初始化表达式推
导):
var i, j, k int // int, int, int var b, f, s = true, 2.3, "four" // bool, float64, string
初始化表达式可以是字面量或任意的表达式。在包级别声明的变量会在main入口函数执行前
完成初始化(§2.6.2),局部变量将在声明语句被执行到的时候完成初始化。
一组变量也可以通过调用一个函数,由函数返回的多个返回值初始化:
var f, err = os.Open(name)
简短变量声明
在函数内部,有一种称为简短变量声明语句的形式可用于声明和初始化局部变量。它以“名字
:= 表达式”形式声明变量,变量的类型根据表达式来自动推导。
anim := gif.GIF{LoopCount: nframes} freq := rand.Float64() * 3.0 t := 0.0
i, j := 0, 1
f, err := os.Open(name) if err != nil { return err } // ...use f... f.Close()
请记住“:=”是一个变量声明语句,而“=‘是一个变量赋值操作
在下面的代码中,第一个语句声明了in和err两个变量。在第二个语句只声明了out一个变量,
然后对已经声明的err进行了赋值操作。
in, err := os.Open(infile) // ... out, err := os.Create(outfile)
简短变量声明语句中必须至少要声明一个新的变量,下面的代码将不能编译通过:
f, err := os.Open(infile) // ... f, err := os.Create(outfile) // compile error: no new variables
解决的方法是第二个简短变量声明语句改用普通的多重赋值语言。
var形式的声明语句往往是用于需要显式指定变量类型地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。
i := 100 // an int var boiling float64 = 100 // a float64 var names []string var err error var p Point
任何类型的指针的零值都是nil。如果 p != nil 测试为真,那么p是指向某个有效变量。指针
之间也是可以进行相等测试的,只有当它们指向同一个变量或全部是nil时才相等。
var x, y int fmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"
在Go语言中,返回函数中局部变量的地址也是安全的。例如下面的代码,调用f函数时创建局
部变量v,在局部变量地址被返回之后依然有效,因为指针p依然引用这个变量。
var p = f() func f() *int { v := 1 return &v }
每次调用f函数都将返回不同的结果:
fmt.Println(f() == f()) // "false"
如果将指向短生命周期对象的指针保存到具有长生命周期的对象中,
特别是保存到全局变量时,会阻止对短生命周期对象的垃圾回收(从而可能影响程序的性
能)。