最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文搞懂Golang?值傳遞還是引用傳遞

 更新時間:2023年01月11日 14:26:33   作者:Ch3n  
最多人犯迷糊的就是?slice、map、chan?等類型,都會認為是?“引用傳遞”,從而認為?Go?語言的?xxx?就是引用傳遞。正因為它們還引用類型(指針、map、slice、chan等這些),這樣就可以修改原內(nèi)容數(shù)據(jù),這篇文章主要介紹了Golang?值傳遞還是引用傳遞,需要的朋友可以參考下

Go 官方的定義

本部分引用 Go 官方 FAQ 的 “When are function parameters passed by value?”,內(nèi)容如下。

如同 C 系列的所有語言一樣,Go 語言中的所有東西都是以值傳遞的。也就是說,一個函數(shù)總是得到一個被傳遞的東西的副本,就像有一個賦值語句將值賦給參數(shù)一樣。

傳值和傳引用

什么是傳值(值傳遞)

傳值的意思是:函數(shù)傳遞的總是原來這個東西的一個副本,一副拷貝。其指的是在調(diào)用函數(shù)時將實際參數(shù)復制一份傳遞到函數(shù)中,這樣在函數(shù)中如果對參數(shù)進行修改,將不會影響到實際參數(shù)。 比如我們傳遞一個int類型的參數(shù),傳遞的其實是這個參數(shù)的一個副本;傳遞一個指針類型的參數(shù),其實傳遞的是這個該指針的一份拷貝,而不是這個指針指向的值。

對于int這類基礎類型我們可以很好的理解,它們就是一個拷貝,但是指針呢?我們覺得可以通過它修改原來的值,怎么會是一個拷貝呢?下面我們看個例子。

test_demo.go

package main

import (
	"fmt"
	"testing"
)

func modify(ip *int) {
	fmt.Printf("函數(shù)里接收到的指針的內(nèi)存地址是:%p\n", &ip)
	*ip = 1
}

func TestDemo(t *testing.T) {
	i := 10
	ip := &i
	fmt.Printf("原始指針的內(nèi)存地址是:%p\n", &ip)
	modify(ip)
	fmt.Println("int值被修改了,新值為:", i)

}

輸出結果:

原始指針的內(nèi)存地址是:0xc00000e038
函數(shù)里接收到的指針的內(nèi)存地址是:0xc00000e040
int值被修改了,新值為: 1

什么是傳引用(引用傳遞)

傳引用,也叫做引用傳遞, 指在調(diào)用函數(shù)時將實際參數(shù)的地址直接傳遞到函數(shù)中,那么在函數(shù)中對參數(shù)所進行的修改,將影響到實際參數(shù)。

在 Go 語言中,官方已經(jīng)明確了沒有傳引用,也就是沒有引用傳遞這一情況。

爭議最大的 map 和 slice
這時候又有小伙伴疑惑了,你看 Go 語言中的 map 和 slice 類型,能直接修改,難道不是同個內(nèi)存地址,不是引用了?

其實在 FAQ 中有一句提醒很重要:“map 和 slice 的行為類似于指針,它們是包含指向底層 map 或 slice 數(shù)據(jù)的指針的描述符”。

迷惑map

package main

import (
	"fmt"
	"testing"
)

func modify(p map[string]int) {
	fmt.Printf("函數(shù)里接收到map的內(nèi)存地址是:%p\n", &p)
	p["張三"] = 20
}

func TestDemo(t *testing.T) {
	persons := make(map[string]int)
	persons["張三"] = 19

	mp := &persons

	fmt.Printf("原始map的內(nèi)存地址是:%p\n", mp)
	modify(persons)
	fmt.Println("map值被修改了,新值為:", persons)
}

輸出結果:

原始map的內(nèi)存地址是:0xc000114028
函數(shù)里接收到map的內(nèi)存地址是:0xc000114030

確實是值傳遞,那修改后的 map 的結果應該是什么。既然是值傳遞,那肯定就是 “這次一定!",對嗎?

輸出結果:

map值被修改了,新值為: map[張三:20]

原因:

指針類型可以修改,非指針類型不行,可以大膽的猜測,使用make函數(shù)創(chuàng)建的map是不是一個指針類型呢?看一下源代碼:

// makemap implements a Go map creation make(map[k]v, hint)
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If bucket != nil, bucket can be used as the first bucket.
func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
    //省略無關代碼
}

通過查看src/runtime/hashmap.go源代碼發(fā)現(xiàn),注意其返回的是 *hmap類型,是一個指針。也就是 Go 語言通過對 map 類型的相關方法進行封裝,達到了用戶需要關注指針傳遞的作用。

現(xiàn)在看func modify(p map)這樣的函數(shù),其實就等于func modify(p *hmap),和前面什么是值傳遞里舉的func modify(ip *int)的例子一樣,可以參考分析。

這類情況我們稱其為 “引用類型” ,但 “引用類型” 不等同于就是傳引用,又或是引用傳遞了,還是有比較明確的區(qū)別的。

chan類型

chan類型本質(zhì)上和map類型是一樣的,這里不做過多的介紹,參考下源代碼:

func makechan(t *chantype, size int64) *hchan {
    //省略無關代碼
}

chan也是一個引用類型,和map相差無幾,make返回的是一個*hchan。

和map、chan都不一樣的slice

slicemap、chan都不太一樣的,一樣的是,它也是引用類型,它也可以在函數(shù)中修改對應的內(nèi)容。

package main

import (
	"fmt"
	"testing"
)

func modify(ages []int) {
	fmt.Printf("函數(shù)里接收到slice的內(nèi)存地址是%p\n", ages)
	ages[0] = 1
}

func TestDemo(t *testing.T) {
	ages := []int{6, 6, 6}
	fmt.Printf("原始slice的內(nèi)存地址是%p\n", ages)
	modify(ages)
	fmt.Println(ages)
}

從結果來看,兩者的內(nèi)存地址一樣,也成功的變更到了變量 ages 的值。這難道不是引用傳遞嗎?
關注兩個細節(jié):

  • 沒有用 & 來取地址。
  • 可以直接用 %p 來打印。

之所以可以同時做到上面這兩件事,是因為標準庫 fmt 針對在這一塊做了優(yōu)化:

func (p *pp) fmtPointer(value reflect.Value, verb rune) {
    var u uintptr
    switch value.Kind() {
    case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
        u = value.Pointer()
    default:
        p.badVerb(verb)
        return
    }
    //省略部分代碼
}

通過源代碼發(fā)現(xiàn),對于chan、mapslice等被當成指針處理,通過value.Pointer()獲取對應的值的指針。

// If v's Kind is Slice, the returned pointer is to the first
// element of the slice. If the slice is nil the returned value
// is 0.  If the slice is empty but non-nil the return value is non-zero.
func (v Value) Pointer() uintptr {
    // TODO: deprecate
    k := v.kind()
    switch k {
    //省略無關代碼
    case Slice:
        return (*SliceHeader)(v.ptr).Data
    }
}

很明顯了,當是slice類型的時候,返回是slice這個結構體里,字段Data第一個元素的地址。

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

在 Go 語言運行時,傳遞的也是相應 slice 類型的底層數(shù)組的指針,但需要注意,其使用的是指針的副本。嚴格意義是引用類型,依舊是值傳遞。slice是一種結構體+元素指針的混合類型,通過元素array(Data)的指針,可以達到修改slice里存儲元素的目的。

總結

最終可以確認的是Go語言中所有的傳參都是值傳遞(傳值),都是一個副本,一個拷貝。

讓最多人犯迷糊的就是 slice、mapchan 等類型,都會認為是 “引用傳遞”,從而認為 Go 語言的 xxx 就是引用傳遞。正因為它們還引用類型(指針、map、slice、chan等這些),這樣就可以修改原內(nèi)容數(shù)據(jù)。

再記住,Go里只有傳值(值傳遞)。

參考資料

群里又吵起來了,Go 是傳值還是傳引用?
Go語言參數(shù)傳遞是傳值還是傳引用

到此這篇關于Golang 值傳遞還是引用傳遞的文章就介紹到這了,更多相關Golang: 值傳遞還是引用傳遞內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • go實現(xiàn)圖片拼接與文字書寫的方法實例

    go實現(xiàn)圖片拼接與文字書寫的方法實例

    這篇文章主要給大家介紹了關于go實現(xiàn)圖片拼接與文字書寫的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-01-01
  • go-cqhttp環(huán)境配置及安裝過程

    go-cqhttp環(huán)境配置及安裝過程

    這篇文章主要介紹了go-cqhttp環(huán)境配置,包括go-cqhttp安裝及簡單介紹,本文結合示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-09-09
  • 詳解Go語言中Goroutine退出機制的原理及使用

    詳解Go語言中Goroutine退出機制的原理及使用

    goroutine是Go語言提供的語言級別的輕量級線程,在我們需要使用并發(fā)時,我們只需要通過?go?關鍵字來開啟?goroutine?即可。本文就來詳細講講Goroutine退出機制的原理及使用,感興趣的可以了解一下
    2022-07-07
  • 詳解Golang如何監(jiān)聽某個函數(shù)的開始執(zhí)行和執(zhí)行結束

    詳解Golang如何監(jiān)聽某個函數(shù)的開始執(zhí)行和執(zhí)行結束

    這篇文章主要為大家詳細介紹了Golang如何監(jiān)聽某個函數(shù)的開始執(zhí)行和執(zhí)行結束,文中的示例代碼講解詳細,有需要的小伙伴可以參考一下
    2024-02-02
  • Golang中使用Mqtt的方法示例

    Golang中使用Mqtt的方法示例

    本文介紹了Golang中使用paho.mqtt.golang庫實現(xiàn)MQTT客戶端與服務器的連接、訂閱和消息收發(fā),具有一定的參考價值,感興趣的可以了解一下
    2025-02-02
  • 一文詳解Golang的模塊版本管理與語義版本控制

    一文詳解Golang的模塊版本管理與語義版本控制

    在Golang中,模塊(module)是Go 1.11版本引入的依賴管理系統(tǒng),幫助開發(fā)者管理項目的依賴,在Go模塊推出之前,開發(fā)者通常使用GOPATH和vendor目錄來管理項目的依賴,本文將給大家詳細介紹Golang的模塊版本管理與語義版本控制,需要的朋友可以參考下
    2023-12-12
  • vscode中安裝Go插件和配置Go環(huán)境詳細步驟

    vscode中安裝Go插件和配置Go環(huán)境詳細步驟

    要在VSCode中配置Go語言插件,首先需要確保你的電腦已經(jīng)安裝了Go環(huán)境和最新版本的VSCode,這篇文章主要給大家介紹了關于vscode中安裝Go插件和配置Go環(huán)境的相關資料,需要的朋友可以參考下
    2024-01-01
  • Golang設計模式之生成器模式講解和代碼示例

    Golang設計模式之生成器模式講解和代碼示例

    生成器是一種創(chuàng)建型設計模式,使你能夠分步驟創(chuàng)建復雜對象,與其他創(chuàng)建型模式不同,生成器不要求產(chǎn)品擁有通用接口,這使得用相同的創(chuàng)建過程生成不同的產(chǎn)品成為可能,本文就通過代碼示例為大家詳細介紹Golang生成器模式,感興趣的同學可以參考下
    2023-06-06
  • Go語言擴展原語之ErrGroup的用法詳解

    Go語言擴展原語之ErrGroup的用法詳解

    除標準庫中提供的同步原語外,Go語言還在子倉庫sync中提供了4種擴展原語,本文主要為大家介紹的是其中的golang/sync/errgroup.Group,感興趣的小伙伴可以了解一下
    2023-07-07
  • Go語言中for循環(huán)的經(jīng)典案例分析

    Go語言中for循環(huán)的經(jīng)典案例分析

    for循環(huán)問題,在面試中經(jīng)常都會被問到,并且在實際業(yè)務項目中也經(jīng)常用到for循環(huán),要是沒用好,一不下心就掉坑。本文為大家挑選了幾個經(jīng)典的案例,一塊來探討下,看看如何避免掉坑,多積累積累采坑經(jīng)驗
    2023-02-02

最新評論

科尔| 板桥市| 山阳县| 嘉善县| 涟水县| 泰宁县| 阿城市| 江阴市| 长治县| 丰宁| 黄骅市| 收藏| 文山县| 深州市| 渭南市| 勐海县| 昌吉市| 墨脱县| 泸西县| 杭锦后旗| 交口县| 那坡县| 西吉县| 普格县| 开化县| 尼勒克县| 伊宁市| 厦门市| 乌鲁木齐市| 博客| 乐至县| 河间市| 蓬莱市| 周至县| 白玉县| 泾源县| 衡水市| 凌源市| 龙南县| 云安县| 吴堡县|