Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。声明变量的一般形式是使用 var 关键字:
var varName dataType [= value]Go 语言和许多编程语言不同,它在声明变量时将变量的类型放在变量的名称之后。
这样做的好处就是可以避免像 C 语言中那样含糊不清的声明形式,例如:
// C 语言写法 int* a, b, c; // 创建一个 int 型指针 a 和两个 int 型变量 b,c而在 Go 中,则可以轻松地将它们都声明为指针类型。
当一个变量被声明之后,系统自动赋予它该类型的零值: int 为 0, float 为 0.0, bool 为 false, string 为空字符串,指针为 nil 等。所有的内存在 Go 中都是经过初始化的。
在C语言中,变量在声明时,并不会对变量对应内存区域进行清理操作。此时,变量值可能是完全不可预期的结果。开发者需要习惯在使用C语言进行声明时要初始化操作,稍有不慎,就会造成不可预知的后果。
在网络上只有程序员才能看懂的“烫烫烫”和“屯屯屯”的梗,就来源于 C/C++ 中变量默认不初始化。
微软的 VC 编译器会将未初始化的栈空间以 16 进制的 0xCC 填充,而未初始化的堆空间使用 0xCD 填充,而 0xCCCC 和 0xCDCD 在中文的 GB2312 编码中刚好对应“烫”和“屯”字。
因此,如果一个字符串没有结束符\0,直接输出的内存数据转换为字符串就刚好对应“烫烫烫”和“屯屯屯”。
变量的命名规则遵循骆驼命名法,即首个单词小写,每个新单词的首字母大写,例如: numShips 和 startDate 。
例如:
var a int = 10 var b = 10 c := 10使用示例:
package main var a = "卧虎藏龙" var b string = "www.wohu.com" var c bool func main() { d := 100 println(a, b, c, d) }注意: 一般建议一行只写一个变量,这样方便代码阅读。
在 Go 语言中,定义变量要么通过声明、要么通过 make 和 new 函数,不一样的是 make 和 new 函数属于显式声明并初始化。
如果我们声明的变量没有显式声明初始化,那么该变量的默认值就是对应类型的零值。从下面的表格可以看到,可以称为引用类型的零值都是 nil。
类型零值int、float0boolfalsestring“” 空字符串struct内部字段的零值slicenilmapnil指针nil函数nilchannilinterfacenil运行结果:
0 0 0 false 1 2 123 hello 123 hello在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,如下代码
package main import "fmt" func main() { var a int = 2 fmt.Println("a is ", a) a := 5 fmt.Println("a is ", a) }编译器会报错误
# command-line-arguments src/main.go:32:4: no new variables on left side of :=但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a 。
如果声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误 a declared and not used
空白标识符也被用于抛弃值,如值 5 在 _, b = 5, 7 中被抛弃。
_ 也叫作匿名变量,匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
注意:在多个短变量声明和赋值中,至少有一个新声明的变量出现在左值中,即便其他变量名可能是重复声明的,编译器也不会报错,代码如下,编译器不会报 err 重复定义。
conn, err := net.Dial("tcp", "127.0.0.1:8080") conn2, err := net.Dial("tcp", "127.0.0.1:8080")Go 语言中变量可以在三个地方声明:
函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数
全局作用域在任何地方都可以访问的变量,称其具有全局作用域。在 Go 语言中,全局作用域有两类:
Go 语言内置的预声明标识符(包括预声明的类型名、关键字、内置函数等)它们具有全局作用域,在任意命名空间内都可见。
Go 语言包内以大写字母开头的标识符( 包括变量、常量、函数和方法名、自定义类型、结构字段等),它们具有全局作用域,在任意命名空间内都可见。
包内作用域在 Go 语言包内定义的以小写字母开头的标识符(变量、常量、函数和方法名、自定义类型、结构字段等〉,它们在本包可见,在其他包都是不可见的,这些标识符具有包内作用域。
隐式作用域每个代码块内定义的变量称为“局部变量”这些局部变量只在当前代码块内可见,其作用域属于当前代码块的隐式作用域。
不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。
句法块是由花括弧所包含的一系列语句,就像函数体或循环体花括弧包裹的内容一样。句法块内部声明的名字是无法被外部块访问的。这个块决定了内部声明的名字的作用域范围。
我们可以把块(block)的概念推广到包括其他声明的群组,这些声明在代码中并未显式地使用花括号包裹起来,我们称之为词法块。对全局的源代码来说,存在一个整体的词法块,称为全局词法块;对于每个包、每个 for 、 if 和 switch 语句,也都有对应词法块;
每个 switch 或 select 的分支也有独立的词法块;当然也包括显式书写的词法块(花括弧包含的语句)。
和 for 循环类似, if 和 switch 语句也会在条件部分创建隐式词法域,还有它们对应的执行体词法域。下面的 if-else 测试链演示了 x 和 y 的有效作用域范围:
if x := f(); x == 0 { fmt.Println(x) } else if y := g(x); x == y { fmt.Println(x, y) } else { fmt.Println(x, y) } fmt.Println(x, y) // compile error: x and y are not visible here第二个 if 语句嵌套在第一个内部,因此第一个 if 语句条件初始化词法域声明的变量在第二个 if 中也可以访问。 switch 语句的每个分支也有类似的词法域规则:条件部分为一个隐式词法域,然后是每个分支的词法域。
if f, err := os.Open(fname); err != nil { // compile error: unused: f return err } f.ReadByte() // compile error: undefined f f.Close() // compile error: undefined f变量 f 的作用域只在 if 语句内,因此后面的语句将无法引入它,这将导致编译错误。你可能会收到一个局部变量 f 没有声明的错误提示,具体错误信息依赖编译器的实现。
通常需要在 if 之前声明变量,这样可以确保后面的语句依然可以访问变量:
f, err := os.Open(fname) if err != nil { return err } f.ReadByte() f.Close()在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
package main import "fmt" func main() { /* 声明局部变量 */ var a, b, c int /* 初始化参数 */ a = 10 b = 20 c = a + b fmt.Printf ("结果: a = %d, b = %d and c = %d\n", a, b, c) }在函数体外声明的变量称之为全局变量,全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。全局变量可以在整个包甚至外部包(被导出后)使用。
package main import "fmt" /* 声明全局变量 */ var g int func main() { /* 声明局部变量 */ var a, b int /* 初始化参数 */ a = 10 b = 20 g = a + b fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g) }Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑。
package main import "fmt" /* 声明全局变量 */ var g int = 20 func main() { /* 声明局部变量 */ var g int = 10 fmt.Printf ("结果: g = %d\n", g) }以上实例执行输出结果为:
main()函数中 a = 10 sum() 函数中 a = 10 sum() 函数中 b = 20 main()函数中 c = 30