久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(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è)簡(jiǎn)單的例子:

      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)右邊的變量代表變量?jī)?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

      我們先來簡(jiǎn)單分析一下為什么不一樣.

      首先當(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)