gopl-zh.github.com/ch4/ch4-04-1.md

2.8 KiB
Raw Blame History

4.4.1. 结构体面值

结构体值也可以用结构体面值表示,结构体面值可以指定每个成员的值。

type Point struct{ X, Y int }

p := Point{1, 2}

这里有两种形式的结构体面值语法上面的是第一种写法要求以结构体成员定义的顺序为每个结构体成员指定一个面值。它要求写代码和读代码的人要记住结构体的每个成员的类型和顺序不过结构体成员有细微的调整就可能导致上述代码不能编译。因此上述的语法一般只在定义结构体的包内部使用或者是在较小的结构体中使用这些结构体的成员排列比较规则比如image.Point{x, y}或color.RGBA{red, green, blue, alpha}。

其实更常用的是第二种写法以成员名字和相应的值来初始化可以包含部分或全部的成员如1.4节的Lissajous程序的写法

anim := gif.GIF{LoopCount: nframes}

在这种形式的结构体面值写法中,如果成员被忽略的话将默认用零值。因为,提供了成员的名字,所有成员出现的顺序并不重要。

两种不同形式的写法不能混合使用。而且,你不能企图在外部包中用第一种顺序赋值的技巧来偷偷地初始化结构体中未导出的成员。

package p
type T struct{ a, b int } // a and b are not exported

package q
import "p"
var _ = p.T{a: 1, b: 2} // compile error: can't reference a, b
var _ = p.T{1, 2}       // compile error: can't reference a, b

虽然上面最后一行代码的编译错误信息中并没有显式提到未导出的成员,但是这样企图隐式使用未导出成员的行为也是不允许的。

结构体可以作为函数的参数和返回值。例如这个Scale函数将Point类型的值缩放后返回

func Scale(p Point, factor int) Point {
	return Point{p.X * factor, p.Y * factor}
}

fmt.Println(Scale(Point{1, 2}, 5)) // "{5 10}"

如果考虑效率的话,较大的结构体通常会用指针的方式传入和返回,

func Bonus(e *Employee, percent int) int {
	return e.Salary * percent / 100
}

如果要在函数内部修改结构体成员的话用指针传入是必须的因为在Go语言中所有的函数参数都是值拷贝传入的函数参数将不再是函数调用时的原始变量。

func AwardAnnualRaise(e *Employee) {
	e.Salary = e.Salary * 105 / 100
}

因为结构体通常通过指针处理,可以用下面的写法来创建并初始化一个结构体变量,并返回结构体的地址:

pp := &Point{1, 2}

它是下面的语句是等价的

pp := new(Point)
*pp = Point{1, 2}

不过&Point{1, 2}写法可以直接在表达式中使用,比如一个函数调用。