基础不牢,地动山摇
不像Python这种动态语言,遇见对象就是一言不合的赋给变一个变量,然后查看;而Go语言是静态类型语言,因此变量是有明确类型的,编译器也会检查变量类型的正确性。
1. 变量的初始化
1.1 声明
var name type
1
2
3
4
5
6
7
8
9
var (
a int
b string
c []float32
d func() bool
e struct {
x int
}
)
1.2 初始化
var 变量名 类型 = 表达式var age int = 25var age = 25
1.3 短变量声明并初始化
age := 100
2. Go类型默认的零值
不带初始值的变量声明会被设置为它们的零值:
- 0 for all integer types
- 0.0 for floating point numbers
- false for booleans
- “” for strings
- nil for interfaces, slices, channels, maps, pointers and functions
如果array或struct声明后未指定值,则其中的元素的字段将为零值。该初始化以递归方式完成:
1
2
3
4
5
6
type T struct {
n int
f float64
next *T
}
fmt.Println([2]T{}) // [{0 0 <nil>} {0 0 <nil>}]
3. make和new的区别
Go语言中new和make是内建的两个函数,主要用来创建分配类型内存。 在我们定义生成变量的时候,可能会觉得有点迷惑,其实他们的规则很简单。
3.1 new
new(T)
- 内置函数 new 分配空间
- 传递给new 函数的是一个类型,不是一个值
- 返回值是 指向这个新分配的零值的指针
3.2 make
make(T, args)
make 只能用于 slice,map,channel 三种类型, 并且只能是这三种对象。 和 new 一样,第一个参数是 类型,不是一个值. 但是make 的返回值就是这个类型(即使一个引用类型),而不是指针.具体的返回值,依赖具体传入的类型.
3.3 两者区别
new(T)make(T)
具体可以看mojoyv的文章 以及三月沙的理解 Go make 和 new 的区别,包含很多代码例子
4. 一些初始化相关的陷阱
4.1 在nil map中赋值
1
2
3
4
5
6
7
8
9
# error code:
var m map[string]float64
m["pi"] = 3.1416
# OUTPUT: panic: assignment to entry in nil map
# we should:
m := make(map[string]float64)
m["pi"] = 3.1416
4.2 无效的内存地址或nil指针取消引用
1
2
3
4
5
6
7
8
9
10
11
12
type Point struct {
X, Y float64
}
func (p *Point) Abs() float64 {
return math.Sqrt(p.X*p.X + p.Y*p.Y)
}
func main() {
var p *Point
fmt.Println(p.Abs())
}
上面代码会报错:
1
2
3
4
5
6
7
8
9
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0xd2c5a]
goroutine 1 [running]:
main.(*Point).Abs(...)
../main.go:6
main.main()
../main.go:11 +0x1a
未初始化的指针是nil,我们没法使用它,有两种解决方案:
创建一个指针
1
2
3
4
func main() {
var p *Point = new(Point)
fmt.Println(p.Abs())
}
或者直接跳过指针,用值接收者调用指针方法
1
2
3
4
func main() {
var p Point // has zero value Point{X:0, Y:0}
fmt.Println(p.Abs())
}
4.3 一个copy错误
1
2
3
4
5
6
var src, dst []int
src = []int{1, 2, 3}
copy(dst, src) // Copy elements to dst from src.
fmt.Println("dst:", dst)
# OUTPUT: dst: []
很明显这里的src是一个slice的零值nil,要完整副本的COPY,必须分配容量足够大的目标切片:
1
2
3
4
5
6
7
var src, dst []int
src = []int{1, 2, 3}
dst = make([]int, len(src))
n := copy(dst, src)
fmt.Println("dst:", dst, "(copied", n, "numbers)")
# OUTPUT: dst: [1 2 3] (copied 3 numbers)