1、名称
- 指针
Go提供了指针,它的值是变量的地址。使用& 操作符可以获取一个变量的地址,使用* 操作符可以获取指针引用的变量的值,但是指针不支持算数运算。
指针类型的零值是 nil。测试 p != nil,结果是 true 说明 p 指向一个变量。指针是可比较的,两个指针当且仅当指针指向同一个变量或者两者都是 nil 的情况下才是相等的。
var x, y int
fmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"
- 方法和接口
一个关联了命名类型的函数称为方法。Go里面的方法可以关联到几乎所有的命名类型。 - 指针对于 flag 包是很关键的,它使用程序的命令行参数来设置整个程序内某些变量的值。
package main
import (
"flag"
"fmt"
"strings"
)
var n = flag.Bool("n", false, "omit trailing newline")
var sep = flag.String("s", " ","separator")
func main() {
flag.Parse()
fmt.Println(strings.Join(flag.Args(), *sep))
if !*n {
fmt.Println()
}
}
- 变量的声明周期
编译器可以选择使用堆或者栈上的空间来进行分配,令人惊奇的是,这个选择不是基于使用 var 或 new 关键字来声明变量。
var global *int
func f() {
var x int
x = 1
global = &x
}
func g() {
y := new(int)
*y = 1
}
在这里, x 一定使用堆空间,因为它在f 函数返回以后还可以从 global 变量访问,尽管它被声明为一个局部变量。这种情况我们说 x 从 f 中逃逸。相反,当 g 函数返回时,变量 *y 变得不可访问,可回收。因为 *y 没有从g 中逃逸,索引编译器可以安全地在粘上分配 *y,即使使用 new 函数创建它。
- 类型声明
通过 == 和 < 之类的比较操作符,命名类型的值可以与其相同类型的值或者底层类型相同的未命名类型的值比较。但是不同命名类型的值不能直接比较:
type Celsius float64
type Fahrenheit float64
func main() {
var c Celsius
var f Fahrenheit
fmt.Println(c == 0) // "true"
fmt.Println(f >= 0) // "true"
fmt.Println(c == f) // "编译错误: 类型不匹配"
fmt.Println(c == Celsius(f)) // "true"
}
2、new 函数
另外一种创建变量的方式是使用内置的 new 函数。表达式 new(T) 创建一个未命名的T类型变量。初始化为T类型的零值,并返回其地址(地址类型为 *T)
p := new(int) // *int 类型的p,指向未命名的 int 变量
fmt.Println(*p) // 输出 "0"
*p = 2 // 把未命名的 int 设置为2
fmt.Println(*p) // 输出 "2"
下面两个 newInt 函数有相同的行为,每一次调用 new 返回一个具有唯一地址的不同变量。
func newInt() *int {
return new(int)
}
func newInt() *int {
var dumpy int
return &dumpy
}
3、作用域
如果一个实体在函数中声明,它只在函数局部有效。如果声明在函数外,它将对包里面的所有源文件可见。实体第一个字母的大小写决定其可见性是否跨包。如果名称以大写字母开头,它是导出的,意味着它对包外是可见和可访问的,可以被自己包之外的其他程序所引用。包名本身总是由小写字母组成。
当编译器遇到一个名字的引用时,将从内部的封闭词法块到全局块寻找其声明。如果没有找到,它会报 “undeclared name” 错误;如果在内层和外层块存在这个声明,内层的将先被找到。这种情况下,内层声明将覆盖外部声明,使它不可访问:
func main() {
x := "hello!"
for i := 0; i < len(x); i++ {
x := x[i]
if x != '!' {
x = x + 'A' - 'a'
fmt.Printf("%c", x) // "HELLO" (每次迭代一个字母)
}
}
}
func main() {
x := "hello!"
for _, x := range x {
x = x + 'A' - 'a'
fmt.Printf("%c", x) // "HELLO" (每次迭代一个字母)
}
}