在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 10.1 結(jié)構(gòu)體定義
4.7 strings 和 strconv 包
13.6 啟動(dòng)外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫(xiě)
13 錯(cuò)誤處理與測(cè)試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個(gè)例子:使用 Sorter 接口排序
?# 11.5 測(cè)試一個(gè)值是否實(shí)現(xiàn)了某個(gè)接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調(diào)試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語(yǔ)言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運(yùn)行時(shí)異常和 panic
10.2 使用工廠方法創(chuàng)建結(jié)構(gòu)體實(shí)例
12.8 使用接口的實(shí)際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說(shuō)明
7.2 切片
8.0 Map
3.1 Go 開(kāi)發(fā)環(huán)境的基本要求
5.6 標(biāo)簽與 goto
6.10 使用閉包調(diào)試
9.5 自定義包和可見(jiàn)性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內(nèi)置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測(cè)試和基準(zhǔn)測(cè)試
6.8 閉包
4.9 指針
13.1 錯(cuò)誤處理
10.1 結(jié)構(gòu)體定義
5.1 if-else 結(jié)構(gòu)
6.6 遞歸函數(shù)
9.9 通過(guò) Git 打包和安裝
2.7 Go 運(yùn)行時(shí)(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動(dòng)態(tài)類型
13.3 從 panic 中恢復(fù)(Recover)
10.3 使用自定義包中的結(jié)構(gòu)體
11.14 結(jié)構(gòu)體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關(guān)鍵字與標(biāo)識(shí)符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應(yīng)用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結(jié)構(gòu)和要素
8.6 將 map 的鍵值對(duì)調(diào)
6.11 計(jì)算函數(shù)執(zhí)行時(shí)間
5.0 控制結(jié)構(gòu)
10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.6 字符串
3.0 編輯器、集成開(kāi)發(fā)環(huán)境與其它工具
13.8 測(cè)試的具體例子
7.6 字符串、數(shù)組和切片的應(yīng)用
8.4 map 類型的切片
3.9 與其它語(yǔ)言進(jìn)行交互
7.3 For-range 結(jié)構(gòu)
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結(jié)構(gòu)、go install 和 go test
6.3 傳遞變長(zhǎng)參數(shù)
13.9 用(測(cè)試數(shù)據(jù))表驅(qū)動(dòng)測(cè)試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫(kù)
3.3 調(diào)試器
4.5 基本類型和運(yùn)算符
?# 11.8 第二個(gè)例子:讀和寫(xiě)
12.5 用 buffer 讀取文件
總結(jié):Go 中的面向?qū)ο?/span>
11.10 反射包
12.7 用 defer 關(guān)閉文件
9.4 精密計(jì)算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯(cuò)誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項(xiàng)目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標(biāo)簽的結(jié)構(gòu)體
7.5 切片的復(fù)制與追加
?# 11.3 類型斷言:如何檢測(cè)和轉(zhuǎn)換接口變量的類型
5.4 for 結(jié)構(gòu)
4.8 時(shí)間和日期
2.3 在 Linux 上安裝 Go
12 讀寫(xiě)數(shù)據(jù)
6.12 通過(guò)內(nèi)存緩存來(lái)提升性能
9.1 標(biāo)準(zhǔn)庫(kù)概述
12.6 用切片讀寫(xiě)文件
10 結(jié)構(gòu)(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯(cuò)誤的模式
3.2 編輯器和集成開(kāi)發(fā)環(huán)境
12.12 Go 中的密碼學(xué)
5.2 測(cè)試多返回值函數(shù)的錯(cuò)誤
6.7 將函數(shù)作為參數(shù)
8.2 測(cè)試鍵值對(duì)是否存在及刪除元素
3.4 構(gòu)建并運(yùn)行 Go 程序
2.1 平臺(tái)與架構(gòu)
5.3 switch 結(jié)構(gòu)

10.1 結(jié)構(gòu)體定義

結(jié)構(gòu)體定義的一般方式如下:

type identifier struct {
    field1 type1
    field2 type2
    ...
}

type T struct {a, b int} 也是合法的語(yǔ)法,它更適用于簡(jiǎn)單的結(jié)構(gòu)體。

結(jié)構(gòu)體里的字段都有 名字,像 field1、field2 等,如果字段在代碼中從來(lái)也不會(huì)被用到,那么可以命名它為 _。

結(jié)構(gòu)體的字段可以是任何類型,甚至是結(jié)構(gòu)體本身(參考第 10.5 節(jié)),也可以是函數(shù)或者接口(參考第 11 章)??梢月暶鹘Y(jié)構(gòu)體類型的一個(gè)變量,然后像下面這樣給它的字段賦值:

var s T
s.a = 5
s.b = 8

數(shù)組可以看作是一種結(jié)構(gòu)體類型,不過(guò)它使用下標(biāo)而不是具名的字段。

使用 new

使用 new 函數(shù)給一個(gè)新的結(jié)構(gòu)體變量分配內(nèi)存,它返回指向已分配內(nèi)存的指針:var t *T = new(T),如果需要可以把這條語(yǔ)句放在不同的行(比如定義是包范圍的,但是分配卻沒(méi)有必要在開(kāi)始就做)。

var t *T
t = new(T)

寫(xiě)這條語(yǔ)句的慣用方法是:t := new(T),變量 t 是一個(gè)指向 T的指針,此時(shí)結(jié)構(gòu)體字段的值是它們所屬類型的零值。

聲明 var t T 也會(huì)給 t 分配內(nèi)存,并零值化內(nèi)存,但是這個(gè)時(shí)候 t 是類型T。在這兩種方式中,t 通常被稱做類型 T 的一個(gè)實(shí)例(instance)或?qū)ο螅╫bject)。

示例 10.1 structs_fields.go 給出了一個(gè)非常簡(jiǎn)單的例子:

package main
import "fmt"

type struct1 struct {
    i1  int
    f1  float32
    str string
}

func main() {
    ms := new(struct1)
    ms.i1 = 10
    ms.f1 = 15.5
    ms.str= "Chris"

    fmt.Printf("The int is: %d\n", ms.i1)
    fmt.Printf("The float is: %f\n", ms.f1)
    fmt.Printf("The string is: %s\n", ms.str)
    fmt.Println(ms)
}

輸出:

The int is: 10
The float is: 15.500000
The string is: Chris
&{10 15.5 Chris}

使用 fmt.Println 打印一個(gè)結(jié)構(gòu)體的默認(rèn)輸出可以很好的顯示它的內(nèi)容,類似使用 %v 選項(xiàng)。

就像在面向?qū)ο笳Z(yǔ)言所作的那樣,可以使用點(diǎn)號(hào)符給字段賦值:structname.fieldname = value

同樣的,使用點(diǎn)號(hào)符可以獲取結(jié)構(gòu)體字段的值:structname.fieldname

在 Go 語(yǔ)言中這叫 選擇器(selector)。無(wú)論變量是一個(gè)結(jié)構(gòu)體類型還是一個(gè)結(jié)構(gòu)體類型指針,都使用同樣的 選擇器符(selector-notation) 來(lái)引用結(jié)構(gòu)體的字段:

type myStruct struct { i int }
var v myStruct    // v是結(jié)構(gòu)體類型變量
var p *myStruct   // p是指向一個(gè)結(jié)構(gòu)體類型變量的指針
v.i
p.i

初始化一個(gè)結(jié)構(gòu)體實(shí)例(一個(gè)結(jié)構(gòu)體字面量:struct-literal)的更簡(jiǎn)短和慣用的方式如下:

    ms := &struct1{10, 15.5, "Chris"}
    // 此時(shí)ms的類型是 *struct1

或者:

    var ms struct1
    ms = struct1{10, 15.5, "Chris"}

混合字面量語(yǔ)法(composite literal syntax)&struct1{a, b, c} 是一種簡(jiǎn)寫(xiě),底層仍然會(huì)調(diào)用 new (),這里值的順序必須按照字段順序來(lái)寫(xiě)。在下面的例子中能看到可以通過(guò)在值的前面放上字段名來(lái)初始化字段的方式。表達(dá)式 new(Type)&Type{} 是等價(jià)的。

時(shí)間間隔(開(kāi)始和結(jié)束時(shí)間以秒為單位)是使用結(jié)構(gòu)體的一個(gè)典型例子:

type Interval struct {
    start int
    end   int
}

初始化方式:

intr := Interval{0, 3}            (A)
intr := Interval{end:5, start:1}  (B)
intr := Interval{end:5}           (C)

在(A)中,值必須以字段在結(jié)構(gòu)體定義時(shí)的順序給出,& 不是必須的。(B)顯示了另一種方式,字段名加一個(gè)冒號(hào)放在值的前面,這種情況下值的順序不必一致,并且某些字段還可以被忽略掉,就像(C)中那樣。

結(jié)構(gòu)體類型和字段的命名遵循可見(jiàn)性規(guī)則(第 4.2 節(jié)),一個(gè)導(dǎo)出的結(jié)構(gòu)體類型中有些字段是導(dǎo)出的,另一些不是,這是可能的。

下圖說(shuō)明了結(jié)構(gòu)體類型實(shí)例和一個(gè)指向它的指針的內(nèi)存布局:

type Point struct { x, y int }

使用 new 初始化:

作為結(jié)構(gòu)體字面量初始化:

類型 strcut1 在定義它的包 pack1 中必須是唯一的,它的完全類型名是:pack1.struct1。

下面的例子 Listing 10.2—person.go 顯示了一個(gè)結(jié)構(gòu)體 Person,一個(gè)方法,方法有一個(gè)類型為 *Person 的參數(shù)(因此對(duì)象本身是可以被改變的),以及三種調(diào)用這個(gè)方法的不同方式:

package main
import (
    "fmt"
    "strings"
)

type Person struct {
    firstName   string
    lastName    string
}

func upPerson(p *Person) {
    p.firstName = strings.ToUpper(p.firstName)
    p.lastName = strings.ToUpper(p.lastName)
}

func main() {
    // 1-struct as a value type:
    var pers1 Person
    pers1.firstName = "Chris"
    pers1.lastName = "Woodward"
    upPerson(&pers1)
    fmt.Printf("The name of the person is %s %s\n", pers1.firstName, pers1.lastName)

    // 2—struct as a pointer:
    pers2 := new(Person)
    pers2.firstName = "Chris"
    pers2.lastName = "Woodward"
    (*pers2).lastName = "Woodward"  // 這是合法的
    upPerson(pers2)
    fmt.Printf("The name of the person is %s %s\n", pers2.firstName, pers2.lastName)

    // 3—struct as a literal:
    pers3 := &Person{"Chris","Woodward"}
    upPerson(pers3)
    fmt.Printf("The name of the person is %s %s\n", pers3.firstName, pers3.lastName)
}

輸出:

The name of the person is CHRIS WOODWARD
The name of the person is CHRIS WOODWARD
The name of the person is CHRIS WOODWARD

在上面例子的第二種情況中,可以直接通過(guò)指針,像 pers2.lastName="Woodward" 這樣給結(jié)構(gòu)體字段賦值,沒(méi)有像 C++ 中那樣需要使用 -> 操作符,Go 會(huì)自動(dòng)做這樣的轉(zhuǎn)換。

注意也可以通過(guò)解指針的方式來(lái)設(shè)置值:(*pers2).lastName = "Woodward"

結(jié)構(gòu)體的內(nèi)存布局

Go 語(yǔ)言中,結(jié)構(gòu)體和它所包含的數(shù)據(jù)在內(nèi)存中是以連續(xù)塊的形式存在的,即使結(jié)構(gòu)體中嵌套有其他的結(jié)構(gòu)體,這在性能上帶來(lái)了很大的優(yōu)勢(shì)。不像 Java 中的引用類型,一個(gè)對(duì)象和它里面包含的對(duì)象可能會(huì)在不同的內(nèi)存空間中,這點(diǎn)和 Go 語(yǔ)言中的指針很像。下面的例子清晰地說(shuō)明了這些情況:

type Rect1 struct {Min, Max Point }
type Rect2 struct {Min, Max *Point }

遞歸結(jié)構(gòu)體

結(jié)構(gòu)體類型可以通過(guò)引用自身來(lái)定義。這在定義鏈表或二叉樹(shù)的元素(通常叫節(jié)點(diǎn))時(shí)特別有用,此時(shí)節(jié)點(diǎn)包含指向臨近節(jié)點(diǎn)的鏈接(地址)。如下所示,鏈表中的 su,樹(shù)中的 rile 分別是指向別的節(jié)點(diǎn)的指針。

鏈表:

這塊的 data 字段用于存放有效數(shù)據(jù)(比如 float64),su 指針指向后繼節(jié)點(diǎn)。

Go 代碼:

type Node struct {
    data    float64
    su      *Node
}

鏈表中的第一個(gè)元素叫 head,它指向第二個(gè)元素;最后一個(gè)元素叫 tail,它沒(méi)有后繼元素,所以它的 su 為 nil 值。當(dāng)然真實(shí)的鏈接會(huì)有很多數(shù)據(jù)節(jié)點(diǎn),并且鏈表可以動(dòng)態(tài)增長(zhǎng)或收縮。

同樣地可以定義一個(gè)雙向鏈表,它有一個(gè)前趨節(jié)點(diǎn) pr 和一個(gè)后繼節(jié)點(diǎn) su

type Node struct {
    pr      *Node
    data    float64
    su      *Node
}

二叉樹(shù):

二叉樹(shù)中每個(gè)節(jié)點(diǎn)最多能鏈接至兩個(gè)節(jié)點(diǎn):左節(jié)點(diǎn)(le)和右節(jié)點(diǎn)(ri),這兩個(gè)節(jié)點(diǎn)本身又可以有左右節(jié)點(diǎn),依次類推。樹(shù)的頂層節(jié)點(diǎn)叫根節(jié)點(diǎn)(root),底層沒(méi)有子節(jié)點(diǎn)的節(jié)點(diǎn)叫葉子節(jié)點(diǎn)(leaves),葉子節(jié)點(diǎn)的 leri 指針為 nil 值。在 Go 中可以如下定義二叉樹(shù):

type Tree strcut {
    le      *Tree
    data    float64
    ri      *Tree
}

結(jié)構(gòu)體轉(zhuǎn)換

Go 中的類型轉(zhuǎn)換遵循嚴(yán)格的規(guī)則。當(dāng)為結(jié)構(gòu)體定義了一個(gè) alias 類型時(shí),此結(jié)構(gòu)體類型和它的 alias 類型都有相同的底層類型,它們可以如示例 10.3 那樣互相轉(zhuǎn)換,同時(shí)需要注意其中非法賦值或轉(zhuǎn)換引起的編譯錯(cuò)誤。

示例 10.3:

package main
import "fmt"

type number struct {
    f float32
}

type nr number   // alias type

func main() {
    a := number{5.0}
    b := nr{5.0}
    // var i float32 = b   // compile-error: cannot use b (type nr) as type float32 in assignment
    // var i = float32(b)  // compile-error: cannot convert b (type nr) to type float32
    // var c number = b    // compile-error: cannot use b (type nr) as type number in assignment
    // needs a conversion:
    var c = number(b)
    fmt.Println(a, b, c)
}

輸出:

{5} {5} {5}

練習(xí) 10.1 vcard.go:

定義結(jié)構(gòu)體 Address 和 VCard,后者包含一個(gè)人的名字、地址編號(hào)、出生日期和圖像,試著選擇正確的數(shù)據(jù)類型。構(gòu)建一個(gè)自己的 vcard 并打印它的內(nèi)容。

提示:
VCard 必須包含住址,它應(yīng)該以值類型還是以指針類型放在 VCard 中呢?
第二種會(huì)好點(diǎn),因?yàn)樗加脙?nèi)存少。包含一個(gè)名字和兩個(gè)指向地址的指針的 Address 結(jié)構(gòu)體可以使用 %v 打?。?{Kersschot 0x126d2b80 0x126d2be0}

練習(xí) 10.2 persionext1.go:

修改 persionext1.go,使它的參數(shù) upPerson 不是一個(gè)指針,解釋下二者的區(qū)別。

練習(xí) 10.3 point.go:

使用坐標(biāo) X、Y 定義一個(gè)二維 Point 結(jié)構(gòu)體。同樣地,對(duì)一個(gè)三維點(diǎn)使用它的極坐標(biāo)定義一個(gè) Polar 結(jié)構(gòu)體。實(shí)現(xiàn)一個(gè) Abs() 方法來(lái)計(jì)算一個(gè) Point 表示的向量的長(zhǎng)度,實(shí)現(xiàn)一個(gè) Scale 方法,它將點(diǎn)的坐標(biāo)乘以一個(gè)尺度因子(提示:使用 math 包里的 Sqrt 函數(shù))(function Scale that multiplies the coordinates of a point with a scale factor)。

練習(xí) 10.3 rectangle.go:

定義一個(gè) Rectangle 結(jié)構(gòu)體,它的長(zhǎng)和寬是 int 類型,并定義方法 Area()Perimeter(),然后進(jìn)行測(cè)試。

鏈接