mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-25 14:28:58 +00:00
9666211cd7
Fixes #198
75 lines
2.8 KiB
Markdown
75 lines
2.8 KiB
Markdown
### 4.4.1. 結構體面值
|
||
|
||
結構體值也可以用結構體面值表示,結構體面值可以指定每個成員的值。
|
||
|
||
```Go
|
||
type Point struct{ X, Y int }
|
||
|
||
p := Point{1, 2}
|
||
```
|
||
|
||
這里有兩種形式的結構體面值語法,上面的是第一種寫法,要求以結構體成員定義的順序爲每個結構體成員指定一個面值。它要求寫代碼和讀代碼的人要記住結構體的每個成員的類型和順序,不過結構體成員有細微的調整就可能導致上述代碼不能編譯。因此,上述的語法一般隻在定義結構體的包內部使用,或者是在較小的結構體中使用,這些結構體的成員排列比較規則,比如image.Point{x, y}或color.RGBA{red, green, blue, alpha}。
|
||
|
||
其實更常用的是第二種寫法,以成員名字和相應的值來初始化,可以包含部分或全部的成員,如1.4節的Lissajous程序的寫法:
|
||
|
||
```Go
|
||
anim := gif.GIF{LoopCount: nframes}
|
||
```
|
||
|
||
在這種形式的結構體面值寫法中,如果成員被忽略的話將默認用零值。因爲,提供了成員的名字,所有成員出現的順序併不重要。
|
||
|
||
兩種不同形式的寫法不能混合使用。而且,你不能企圖在外部包中用第一種順序賦值的技巧來偷偷地初始化結構體中未導出的成員。
|
||
|
||
```Go
|
||
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類型的值縮放後返迴:
|
||
|
||
```Go
|
||
func Scale(p Point, factor int) Point {
|
||
return Point{p.X * factor, p.Y * factor}
|
||
}
|
||
|
||
fmt.Println(Scale(Point{1, 2}, 5)) // "{5 10}"
|
||
```
|
||
|
||
如果考慮效率的話,較大的結構體通常會用指針的方式傳入和返迴,
|
||
|
||
```Go
|
||
func Bonus(e *Employee, percent int) int {
|
||
return e.Salary * percent / 100
|
||
}
|
||
```
|
||
|
||
如果要在函數內部脩改結構體成員的話,用指針傳入是必須的;因爲在Go語言中,所有的函數參數都是值拷貝傳入的,函數參數將不再是函數調用時的原始變量。
|
||
|
||
```Go
|
||
func AwardAnnualRaise(e *Employee) {
|
||
e.Salary = e.Salary * 105 / 100
|
||
}
|
||
```
|
||
|
||
因爲結構體通常通過指針處理,可以用下面的寫法來創建併初始化一個結構體變量,併返迴結構體的地址:
|
||
|
||
```Go
|
||
pp := &Point{1, 2}
|
||
```
|
||
|
||
它是下面的語句是等價的
|
||
|
||
```Go
|
||
pp := new(Point)
|
||
*pp = Point{1, 2}
|
||
```
|
||
|
||
不過&Point{1, 2}寫法可以直接在表達式中使用,比如一個函數調用。
|