Go語言學習教程之指針的示例詳解
前言
關于指針的主要幾點:
- 指針類型:一個指針類型
*T
表示指向給定類型的變量的所有指針的集合,該給定類型T
稱為基本類型。未初始化的指針的值是nil
。 - 變量:一個變量是保存一個值的存儲位置。允許的值的集合由變量的類型決定。
- 尋址操作:對于類型為
T
的操作數x
,尋址操作&x
會產生一個指向x
的類型為*T
的指針。對于指針類型為*T
的操作數y
,指針間接尋址*y
表示y
指向的類型為T
的變量。
本文使用的Go版本:
$ go version go version go1.18 darwin/amd64
練習1
var a int = 111 var b *int = &a fmt.Println("a的值是:", a) // 111 fmt.Println("a的地址是:", &a) // 0xc000016098 fmt.Println("b的值是:", b) // 0xc000016098 fmt.Println("b的地址是:", &b) // 0xc0000ac018 *b = *b + 1 fmt.Println(a, b) // 112 0xc000016098
代碼中聲明了一個整型的變量a
,以及一個指向整型變量a
的*int
類型的指針變量b
。
內存地址表示數據在內存中存放的位置。如上圖所示,a
相當于是內存地址0xc000016098
的一個名字(用于引用計算機內存地址),當我們獲取a
的值時,就是獲取內存地址0xc000016098
存儲的數據。而指針類型的變量b
(代表內存地址0xc0000ac018
)存儲的是變量a
代表的地址,它存儲的值就是一個地址。
當使用*b
進行指針間接尋址時,可以理解為:找到b
代表的內存地址0xc0000ac018
中存儲的值,存儲的是一個地址0xc000016098
,于是去拿地址0xc000016098
中存儲的值111
。
當對*b
進行賦值時(首先賦值符號=
右側已經計算出結果為112
了),將b
代表的內存地址0xc0000ac018
中,存儲的地址0xc000016098
中,存儲的值改為112
。修改的是內存地址0xc000016098
中存儲的值,所以再次打印a
(代表內存地址0xc000016098
)的值時,已經變?yōu)榱?code>112。
練習2
對于類型為T
的操作數x
,尋址操作&x
會產生一個指向x
的類型為*T
的指針。
操作數必須是可尋址的,即變量、指針間接引用、切片索引操作;或者一個可尋址的結構體操作數的字段選擇;或者一個可尋址的數組的數組索引操作。
有一個特殊的情況是,x
可能是一個復合字面量,復合字面量(結構體字面量、數組字面量、切片字面量、映射字面量)是不可尋址的,但是依然可以使用&x
。對復合字面量進行&x
操作,會生成一個指針,這個指針指向使用字面量的值進行初始化的一個唯一變量。
如果對x
的計算會導致運行時錯誤,那么對&x
的計算也會導致運行時錯誤。
var c float64 = 222.22 fmt.Println(&c) // 1. 對變量c進行尋址操作 0xc0000b2008 var d *float64 = &c fmt.Println(&*d) // 2.對指針間接引用(*d)進行尋址操作 0xc0000b2008 e := make([]string, 2) // 創(chuàng)建一個初始長度為2的切片 e = []string{"e1", "e2"} fmt.Println(&e[1]) // 3. 對切片索引操作進行尋址操作 0xc0000b8030 type F struct { a string b int } fmt.Println(&F{"a", 1}) // 4.對結構體字面量進行尋址操作 &{a 1} var f F = F{"b", 123} fmt.Println(&f.a) // 5. 對結構體的字段選擇進行尋址操作 0xc0000a4048 var g = [3]int{1, 2, 3} // 創(chuàng)建一個數組 fmt.Println(&g[0]) // 6. 對數組的索引操作進行尋址操作 0xc0000ba000 fmt.Println(&[3]int{4, 5, 6}) // 7. 對數組字面量進行尋址操作 &[4 5 6] // var h *int = nil // fmt.Println(*h) // 會導致一個運行時錯誤:panic: runtime error: invalid memory address or nil pointer dereference // fmt.Println(&*h) // 會導致一個運行時錯誤:panic: runtime error: invalid memory address or nil pointer dereference
練習3
var i int = 1 fmt.Println("i的地址", &i) // i的地址 0xc000016098 increase(i) // 函數內部i的地址 0xc0000160b0 fmt.Println("i的值", i) // i的值 1 increaseV1(&i) // 函數內部拿到的i的地址 0xc000016098 fmt.Println("i的值", i) // i的值 2 func increase(i int) { fmt.Println("函數內部i的地址", &i) i++ } func increaseV1(ptrI *int) { fmt.Println("函數內部拿到的i的地址", &*ptrI) *ptrI++ }
將變量作為參數傳遞到函數中的時候,函數會復制變量中的值到局部變量中,所以不會改變外部變量的值。
在調用increase(i)
時,會創(chuàng)建一個新的局部變量i
,這個變量i
的作用域在函數內部,初始化的值是復制的外部變量i
中的值。所以在函數內部執(zhí)行i++
的時候,改變的是局部變量i
的值,不會影響到外部變量。執(zhí)行完之后外部的i
的值還是1
。
當執(zhí)行increaseV1(&i)
時,傳入的是一個指向外部i
的指針,它表示的地址是外部i
的地址0xc000016098
,所以在函數內部執(zhí)行*ptrI++
時,改變的是地址0xc000016098
中存儲的值,執(zhí)行完函數之后,打印外部的i
(代表內存地址0xc000016098
)的值,發(fā)現值已經變?yōu)?code>2了。
到此這篇關于Go語言學習教程之指針的示例詳解的文章就介紹到這了,更多相關Go語言指針內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Golang中 import cycle not allowed 問題
這篇文章主要介紹了Golang中 import cycle not allowed 問題的解決方法,問題從描述到解決都非常詳細,需要的小伙伴可以參考一下2022-03-03