站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

golang有指針嗎

golang有指針。Go語言對(duì)指針的支持介于Java語言和C/C++語言之間,它既沒有像Java那樣取消了代碼對(duì)指針的直接操作的能力,也避免了C/C++中由于對(duì)指針的濫用而造成的安全和可靠性問題。

golang有指針嗎

本教程操作環(huán)境:windows10系統(tǒng)、GO 1.11.2、thinkpad t480電腦。

指針是一個(gè)代表著某個(gè)內(nèi)存地址的值,這個(gè)內(nèi)存地址往往是在內(nèi)存中存儲(chǔ)的另一個(gè)變量的值的起始位置。

指針地址和變量空間

Go語言保留了指針, 但是與C語言指針有所不同. 主要體現(xiàn)在:

  • 默認(rèn)值:nil

  • 操作符 & 取變量地址, * 通過指針訪問目標(biāo)對(duì)象。

  • 不支持指針運(yùn)算,不支持 -> 運(yùn)算符,直接用 . 訪問目標(biāo)成員。

先來看一段代碼:

package main  import "fmt"  func main(){  var x int = 99 var p *int = &x fmt.Println(p) }

當(dāng)我們運(yùn)行到 var x int = 99 時(shí),在內(nèi)存中就會(huì)生成一個(gè)空間,這個(gè)空間我們給它起了個(gè)名字叫 x,同時(shí), 它也有一個(gè)地址,例如: 0xc00000a0c8,當(dāng)我們想要使用這個(gè)空間時(shí),我們可以用地址去訪問,也可以用我們給它起的名字 x 去訪問.

繼續(xù)運(yùn)行到 var p *int = &x 時(shí),我們定義了一個(gè)指針變量 p,這個(gè) p 就存儲(chǔ)了變量 x 的地址.

所以,指針就是地址,指針變量就是存儲(chǔ)地址的變量。

接著,我們更改 x 的內(nèi)容:

package main  import "fmt"  func main() { 	var x int = 99 	var p *int = &x  	fmt.Println(p)  	x = 100  	fmt.Println("x: ", x) 	fmt.Println("*p: ", *p) 	 	*p = 999  	fmt.Println("x: ", x) 	fmt.Println("*p: ", *p) }

可以發(fā)現(xiàn), x*p 的結(jié)果一樣的。

其中, *p 稱為 解引用 或者 間接引用

*p = 999 是通過借助 x 變量的地址,來操作 x 對(duì)應(yīng)的空間。

不管是 x 還是 *p , 我們操作的都是同一個(gè)空間。

推薦學(xué)習(xí):Golang教程

棧幀的內(nèi)存布局

首先, 先來看一下內(nèi)存布局圖, 以 32位 為例.

golang有指針嗎

其中, 數(shù)據(jù)區(qū)保存的是初始化后的數(shù)據(jù).

上面的代碼都存儲(chǔ)在棧區(qū). 一般 make() 或者 new() 出來的都存儲(chǔ)在堆區(qū)

接下來, 我們來了解一個(gè)新的概念: 棧幀.

棧幀: 用來給函數(shù)運(yùn)行提供內(nèi)存空間, 取內(nèi)存于 stack 上.

當(dāng)函數(shù)調(diào)用時(shí), 產(chǎn)生棧幀; 函數(shù)調(diào)用結(jié)束, 釋放棧幀.

那么棧幀用來存放什么?

  • 局部變量
  • 形參
  • 內(nèi)存字段描述值

其中, 形參與局部變量存儲(chǔ)地位等同

當(dāng)我們的程序運(yùn)行時(shí), 首先運(yùn)行 main(), 這時(shí)就產(chǎn)生了一個(gè)棧幀.

當(dāng)運(yùn)行到 var x int = 99 時(shí), 就會(huì)在棧幀里面產(chǎn)生一個(gè)空間.

同理, 運(yùn)行到 var p *int = &x 時(shí)也會(huì)在棧幀里產(chǎn)生一個(gè)空間.

如下圖所示:

golang有指針嗎

我們?cè)黾右粋€(gè)函數(shù), 再來研究一下.

package mainimport "fmt"func test(m int){ 	var y int = 66 	y += m}func main() { 	var x int = 99 	var p *int = &x  	fmt.Println(p)  	x = 100  	fmt.Println("x: ", x) 	fmt.Println("*p: ", *p)  	test(11)  	*p = 999  	fmt.Println("x: ", x) 	fmt.Println("*p: ", *p)}

如下圖所示, 當(dāng)運(yùn)行到 test(11) 時(shí), 會(huì)繼續(xù)產(chǎn)生一個(gè)棧幀, 這時(shí) main() 產(chǎn)生的棧幀還沒有結(jié)束.

golang有指針嗎

當(dāng) test() 運(yùn)行完畢時(shí), 就會(huì)釋放掉這個(gè)棧幀.

golang有指針嗎

空指針與野指針

空指針: 未被初始化的指針.

var p *int

這時(shí)如果我們想要對(duì)其取值操作 *p, 會(huì)報(bào)錯(cuò).

野指針: 被一片無效的地址空間初始化.

var p *int = 0xc00000a0c8

指針變量的內(nèi)存存儲(chǔ)

表達(dá)式 new(T) 將創(chuàng)建一個(gè) T 類型的匿名變量, 所做的是為 T 類型的新值分配并清零一塊內(nèi)存空間, 然后將這塊內(nèi)存空間的地址作為結(jié)果返回, 而這個(gè)結(jié)果就是指向這個(gè)新的 T 類型值的指針值, 返回的指針類型為 *T.

new() 創(chuàng)建的內(nèi)存空間位于heap上, 空間的默認(rèn)值為數(shù)據(jù)類型的默認(rèn)值. 如: p := new(int)*p0.

package mainimport "fmt"func main(){ 	p := new(int) 	fmt.Println(p) 	fmt.Println(*p)}

這時(shí) p 就不再是空指針或者野指針.

我們只需使用 new() 函數(shù), 無需擔(dān)心其內(nèi)存的生命周期或者怎樣將其刪除, 因?yàn)镚o語言的內(nèi)存管理系統(tǒng)會(huì)幫我們打理一切.

接著我們改一下*p的值:

package mainimport "fmt"func main(){ 	p := new(int) 	 	*p = 1000 	 	fmt.Println(p) 	fmt.Println(*p)}

這個(gè)時(shí)候注意了, *p = 1000 中的 *pfmt.Println(*p) 中的 *p 是一樣的嗎?

大家先思考一下, 然后先來看一個(gè)簡單的例子:

var x int = 10var y int = 20x = y

好, 大家思考一下上面代碼中, var y int = 20 中的 yx = y 中的 y 一樣不一樣?

結(jié)論: 不一樣

var y int = 20 中的 y 代表的是內(nèi)存空間, 我們一般把這樣的稱之為左值; 而 x = y 中的 y 代表的是內(nèi)存空間中的內(nèi)容, 我們一般稱之為右值.

x = y 表示的是把 y 對(duì)應(yīng)的內(nèi)存空間的內(nèi)容寫到x內(nèi)存空間中.

等號(hào)左邊的變量代表變量所指向的內(nèi)存空間, 相當(dāng)于操作.

等號(hào)右邊的變量代表變量內(nèi)存空間存儲(chǔ)的數(shù)據(jù)值, 相當(dāng)于操作.

在了解了這個(gè)之后, 我們?cè)賮砜匆幌轮暗拇a.

p := new(int)*p = 1000fmt.Println(*p)

所以, *p = 1000 的意思是把1000寫到 *p 的內(nèi)存中去;

fmt.Println(*p) 是把 *p的內(nèi)存空間中存儲(chǔ)的數(shù)據(jù)值打印出來.

所以這兩者是不一樣的.

如果我們不在main()創(chuàng)建會(huì)怎樣?

func foo() { 	p := new(int)  	*p = 1000}

我們上面已經(jīng)說過了, 當(dāng)運(yùn)行 foo() 時(shí)會(huì)產(chǎn)生一個(gè)棧幀, 運(yùn)行結(jié)束, 釋放棧幀.

那么這個(gè)時(shí)候, p 還在不在?

p 在哪? 棧幀是在棧上, 而 p 因?yàn)槭?new() 生成的, 所以在 上. 所以, p 沒有消失, p 對(duì)應(yīng)的內(nèi)存值也沒有消失, 所以利用這個(gè)我們可以實(shí)現(xiàn)傳地址.

對(duì)于堆區(qū), 我們通常認(rèn)為它是無限的. 但是無限的前提是必須申請(qǐng)完使用, 使用完后立即釋放.

函數(shù)的傳參

明白了上面的內(nèi)容, 我們?cè)偃チ私?strong>指針作為函數(shù)參數(shù)就會(huì)容易很多.

傳地址(引用): 將地址值作為函數(shù)參數(shù)傳遞.

傳值(數(shù)據(jù)): 將實(shí)參的值拷貝一份給形參.

無論是傳地址還是傳值, 都是實(shí)參將自己的值拷貝一份給形參.只不過這個(gè)值有可能是地址, 有可能是數(shù)據(jù).

所以, 函數(shù)傳參永遠(yuǎn)都是值傳遞.

了解了概念之后, 我們來看一個(gè)經(jīng)典的例子:

package mainimport "fmt"func swap(x, y int){ 	x, y = y, x 	fmt.Println("swap  x: ", x, "y: ", y)}func main(){ 	x, y := 10, 20 	swap(x, y) 	fmt.Println("main  x: ", x, "y: ", y)}

結(jié)果:

swap  x:  20 y:  10main  x:  10 y:  20

我們先來簡單分析一下為什么不一樣.

首先當(dāng)運(yùn)行 main() 時(shí), 系統(tǒng)在棧區(qū)產(chǎn)生一個(gè)棧幀, 該棧幀里有 xy 兩個(gè)變量.

當(dāng)運(yùn)行 swap() 時(shí), 系統(tǒng)在棧區(qū)產(chǎn)生一個(gè)棧幀, 該棧幀里面有 xy 兩個(gè)變量.

運(yùn)行 x, y = y, x 后, 交換 swap() 產(chǎn)生的棧幀里的 xy 值. 這時(shí) main() 里的 xy 沒有變.

swap() 運(yùn)行完畢后, 對(duì)應(yīng)的棧幀釋放, 棧幀里的x y 值也隨之消失.

所以, 當(dāng)運(yùn)行 fmt.Println("main x: ", x, "y: ", y) 這句話時(shí), 其值依然沒有變.

接下來我們看一下參數(shù)為地址值時(shí)的情況.

傳地址的核心思想是: 在自己的棧幀空間中修改其它棧幀空間中的值.

而傳值的思想是: 在自己的棧幀空間中修改自己棧幀空間中的值.

注意理解其中的差別.

繼續(xù)看以下這段代碼:

package mainimport "fmt"func swap2(a, b *int){ 	*a, *b = *b, *a}func main(){ 	x, y := 10, 20 	swap(x, y) 	fmt.Println("main  x: ", x, "y: ", y)}

結(jié)果:

main  x:  20 y:  10

這里并沒有違反 函數(shù)傳參永遠(yuǎn)都是值傳遞 這句話, 只不過這個(gè)時(shí)候這個(gè)值為地址值.

這個(gè)時(shí)候, xy 的值就完成了交換.

我們來分析一下這個(gè)過程.

首先運(yùn)行 main() 后創(chuàng)建一個(gè)棧幀, 里面有 x y 兩個(gè)變量.

運(yùn)行 swap2() 時(shí), 同樣創(chuàng)建一個(gè)棧幀, 里面有 a b 兩個(gè)變量.

注意這個(gè)時(shí)候, a b 中存儲(chǔ)的值是 x y 的地址.

當(dāng)運(yùn)行到 *a, *b = *b, *a 時(shí), 左邊的 *a 代表的是 x 的內(nèi)存地址, 右邊的 *b 代表的是 y 的內(nèi)存地址中的內(nèi)容. 所以這個(gè)時(shí)候, main() 中的 x 就被替換掉了.

所以, 這是在 swap2() 中操作 main() 里的變量值.

現(xiàn)在 swap2() 再釋放也沒有關(guān)系了, 因?yàn)?main() 里的值已經(jīng)被改了.

贊(0)
分享到: 更多 (0)
網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
91精品啪在线观看国产线免费| 中文字幕久热精品视频在线| 亚洲国产精品网站在线播放| 97在线视频精品| 久久丫精品久久丫| 精品国产免费一区二区三区香蕉 | 91精品国产自产在线观看永久| 精品人妻无码专区在中文字幕| 日韩人妻无码中文字幕视频| 国产精品夜夜爽范冰冰| 四色在线精品免费观看| 精品少妇人妻AV一区二区| 亚洲精品无码专区在线播放| 国产成人精品综合网站| 亚洲欧洲国产精品久久| 久久久国产精品亚洲一区| 久久成人国产精品| 久久精品九九亚洲精品| 久久精品无码专区免费东京热| 日韩精品无码一区二区三区不卡 | 国产精品电影网在线好看| 成人精品视频一区二区| 成人久久精品一区二区三区| 麻豆国产精品入口免费观看| 欧美精品久久久久a片一二三区| 亚洲国产精品精华液| 国语自产精品视频在线完整版| 精品久久久久久无码中文字幕一区 | 精品爆乳一区二区三区无码av| 2021国产精品一区二区在线| 亚洲国产精品久久网午夜| 麻豆麻豆必出精品入口| 97久久精品无码一区二区| 亚洲精品线路一在线观看| 中国精品一级毛片免费播放| 国产三级国产精品国产普男人| 国产精品亚洲二区在线观看 | 91精品视频网站| 亚洲精品无码乱码成人| 国产成人精品电影在线观看| 亚洲精品成人av在线|