Go語言函數中的引數傳遞效果測試

2020-07-16 10:05:08
Go語言中傳入與返回引數在呼叫和返回時都使用值傳遞,這裡需要注意的是指標、切片和 map 等參照型物件在引數傳遞中不會發生複製,而是將指標進行複製,類似於建立一次參照。

下面通過一個例子來詳細了解Go語言的引數值傳遞,完整的範例程式碼如下所示:
package main

import "fmt"

// 用於測試值傳遞效果的結構體
type Data struct {
    complax []int // 測試切片在引數傳遞中的效果

    instance InnerData // 範例分配的innerData

    ptr *InnerData // 將ptr宣告為InnerData的指標型別
}

// 代表各種結構體欄位
type InnerData struct {
    a int
}

// 值傳遞測試函數
func passByValue(inFunc Data) Data {

    // 輸出引數的成員情況
    fmt.Printf("inFunc value: %+vn", inFunc)

    // 列印inFunc的指標
    fmt.Printf("inFunc ptr: %pn", &inFunc)

    return inFunc
}

func main() {

    // 準備傳入函數的結構
    in := Data{
        complax: []int{1, 2, 3},
        instance: InnerData{
            5,
        },

        ptr: &InnerData{1},
    }

    // 輸入結構的成員情況
    fmt.Printf("in value: %+vn", in)

    // 輸入結構的指標地址
    fmt.Printf("in ptr: %pn", &in)

    // 傳入結構體,返回同型別的結構體
    out := passByValue(in)

    // 輸出結構的成員情況
    fmt.Printf("out value: %+vn", out)

    // 輸出結構的指標地址
    fmt.Printf("out ptr: %pn", &out)
}

1) 測試資料型別

為了測試結構體、切片、指標及結構體中巢狀的結構體在值傳遞中會發生的情況,需要定義一些結構,程式碼如下:
// 用於測試值傳遞效果的結構體
type Data struct {
    complax []int    // 測試切片在引數傳遞中的效果

    instance InnerData    // 範例分配的innerData

    ptr *InnerData    // 將ptr宣告為InnerData的指標型別
}

// 代表各種結構體欄位
type InnerData struct {
    a int
}
程式碼說明如下:
  • 第 2 行,將 Data 宣告為結構體型別,結構體是擁有多個欄位的複雜結構。
  • 第 3 行,complax 為整型切片型別,切片是一種動態型別,內部以指標存在。
  • 第 5 行,instance 成員以 InnerData 型別作為 Data 的成員。
  • 第 7 行,將 ptr 宣告為 InnerData 的指標型別。
  • 第 11 行,宣告一個內嵌的結構 InnerData。

2) 值傳遞的測試函數

範例程式碼中定義的 passByValue() 函數用於值傳遞的測試,該函數的引數和返回值都是 Data 型別,在呼叫過程中,Data 的記憶體會被複製後傳入函數,當函數返回時,又會將返回值複製一次,賦給函數返回值的接收變數,程式碼如下:
// 值傳遞測試函數
func passByValue(inFunc Data) Data {

    // 輸出引數的成員情況
    fmt.Printf("inFunc value: %+vn", inFunc)

    // 列印inFunc的指標
    fmt.Printf("inFunc ptr: %pn", &inFunc)

    return inFunc
}
程式碼說明如下:
  • 第 5 行,使用格式化的%+v動詞輸出 inFunc 變數的詳細結構,以便觀察 Data 結構在傳遞前後內部數值的變化情況。
  • 第 8 行,列印傳入引數 inFunc 的指標地址,在計算機中,擁有相同地址且型別相同的變數,表示的是同一塊記憶體區域。
  • 第 10 行,將傳入的變數作為返回值返回,返回的過程將發生值複製。

3) 測試流程

測試流程會準備一個 Data 格式的資料結構並填充所有成員,這些成員型別包括切片、結構體成員及指標,通過呼叫測試函數,傳入 Data 結構資料,並獲得返回值,對比輸入和輸出後的 Data 結構數值變化,特別是指標變化情況以及輸入和輸出整塊資料是否被複製,程式碼如下:
// 準備傳入函數的結構
in := Data{
    complax: []int{1, 2, 3},
    instance: InnerData{
            5,
    },

    ptr: &InnerData{1},
}

// 輸入結構的成員情況
fmt.Printf("in value: %+vn", in)

// 輸入結構的指標地址
fmt.Printf("in ptr: %pn", &in)

// 傳入結構體, 返回同型別的結構體
out := passByValue(in)

// 輸出結構的成員情況
fmt.Printf("out value: %+vn", out)

// 輸出結構的指標地址
fmt.Printf("out ptr: %pn", &out)
程式碼說明如下:
  • 第 2 行,建立一個 Data 結構的範例 in。
  • 第 3 行,將切片資料賦值到 in 的 complax 成員。
  • 第 4 行,為 in 的 instance 成員賦值 InnerData 結構的資料。
  • 第 8 行,為 in 的 ptr 成員賦值 InnerData 的指標型別資料。
  • 第 12 行,列印輸入結構的成員情況。
  • 第 15 行,列印輸入結構的指標地址。
  • 第 18 行,傳入 in 結構,呼叫 passByvalue() 測試函數獲得 out 返回,此時,passByValue() 函數會列印 in 傳入後的資料成員情況。
  • 第 21 行,列印返回值變數 out 的成員情況。
  • 第 24 行,列印輸出結構的地址。

執行程式碼,輸出結果為:

in value: {complax:[1 2 3] instance:{a:5} ptr:0xc042008100}
in ptr: 0xc042066060
inFunc value: {complax:[1 2 3] instance:{a:5} ptr:0xc042008100}
inFunc ptr: 0xc0420660f0
out value: {complax:[1 2 3] instance:{a:5} ptr:0xc042008100}
out ptr: 0xc0420660c0

從執行結果中發現:
  • 所有的 Data 結構的指標地址都發生了變化,意味著所有的結構都是一塊新的記憶體,無論是將 Data 結構傳入函數內部,還是通過函數返回值傳回 Data 都會發生複製行為。
  • 所有的 Data 結構中的成員值都沒有發生變化,原樣傳遞,意味著所有引數都是值傳遞。
  • Data 結構的 ptr 成員在傳遞過程中保持一致,表示指標在函數引數值傳遞中傳遞的只是指標值,不會複製指標指向的部分。