golang中一個非常鮮明的特點就是引入了指針的使用檩咱,這個在py揭措、php、java中都是不支持的刻蚯,但是很多讀者都反饋說對指針不是很了解绊含,所以今天寫了這篇文章,當(dāng)然我盡量用通俗的語言炊汹,希望對您有用躬充。
什么是指針
想了解什么是指針,你先得了解數(shù)據(jù)是怎么取到的
func main() {
var a int
a = 1
fmt.Println("a的值為", a)
}
上述代碼非常簡單讨便,我相信不會golang的讀者應(yīng)該也能看懂充甚。但是他底層究竟干嘛了?你真的了解過么霸褒?接下來咱們逐一解釋
var a int
首先這一步伴找,是給變量a,在內(nèi)存中開辟了一塊空間傲霸,因為是int類型疆瑰,所以空間大小為4個字節(jié),那么問題來了昙啄,這塊空間開在內(nèi)存哪個位置穆役? 首先這塊位置是系統(tǒng)隨機分配的一塊。另外給這塊內(nèi)存做了一個標記梳凛,方便下次找到耿币,這個標記就在這塊內(nèi)存的起始的位置
a = 1
接下來給變量a賦值為1。那么系統(tǒng)如何賦值的呢韧拒?首先總得找到a這塊內(nèi)存在哪吧淹接?如何找到給a分配的那塊內(nèi)存的呢?當(dāng)然是通過之前給a變量做的那個標記叛溢。通過標記找到這塊內(nèi)存所在的位置塑悼,然后直接在開辟好的內(nèi)存空間存下給a賦值的數(shù)據(jù)就可以了采记。
fmt.Println("a的值為", a)
這段代碼探入,我們暫且先不用去了解fmt.Println是怎么實現(xiàn)的摄乒,只用關(guān)心a是怎么取到的,當(dāng)然是先找到a之前所做的那個標記耽梅,找到對應(yīng)的內(nèi)存所在的位置橡羞,直接往后偏移4個位置把值取出來就行刻帚。
這下是不是感覺清晰明了了砖顷?
我們在上述過程中,所說的標記巷屿,就是指針.所以指針其實就是一個標記數(shù)據(jù)所在位置的數(shù)據(jù)類型而已,只不過他有一些自己特殊的語法而已固以,而且是一種新的數(shù)據(jù)類型。這么來看嘱巾,就很簡單了憨琳。
指針的定義
指針是一種數(shù)據(jù)類型,用于表示數(shù)據(jù)的內(nèi)存地址浓冒。
如何使用指針
我們來看下面幾個例子感受一下
case1
var a string //聲明一個字符串類型的變量栽渴,初始值為""
var b *string //聲明一個字符串指針類型的變量,初始值為nil稳懒,聲明指針類
fmt.Println("a:", a, " b:", b)
//輸出結(jié)果為
//a: b: <nil>
注意:
- 聲明指針的類型,只需要在前面加上一個*就可以了慢味,這是固定的語法
- 不管什么指針類型(*int, *string, *float)场梆,初始值都為nil
case2
var name string = "小飯" //聲明一個name為string類型,并且賦值為"小飯"
var p_name *string = &name //聲明一個p_name為*string(字符串的指針類型)纯路,并且賦值為&a(在a前面加上一個&的意思是取a的首地址)
fmt.Println("name:", name, "name的內(nèi)存地址", &name, " p_name:", p_name, "p_name的具體值:", *p_name)
//輸出結(jié)果為
//name: 小飯 name的內(nèi)存地址 0x14000010240 p_name: 0x14000010240 p_name的具體值: 小飯
注意
- 取變量name的首地址或油,也就是指針的值,需要用&name表示驰唬,而取出來的值也只能用指針這種變量類型來保存顶岸,所以var p_name *string = &name這段代碼是合理的
- p_name的具體值是隨機分配的一個16進制的值,0x14000010240叫编,知道這個代表的是指針的值就行了辖佣,因為是隨機分配的,所以不同設(shè)備是不一樣的搓逾。
- 要取一個指針類型指向的具體值卷谈,用 * (對應(yīng)的指針類型的變量名)就能直接取到,比如上面的例子,對應(yīng)的指針類型的變量名為p_name霞篡,所以用p_name就能直接取到指針p_name所指向的具體值*世蔗。
說明
上面我們通過&name獲取到了name的內(nèi)存空間的地址是0x14000010240,p_name的變量的值實際上是name變量的內(nèi)存空間的值朗兵,p_name也是一個變量
那么p_name變量所存放值的地方污淋,是不是也會有一個內(nèi)存空間呢?是的余掖,p_name這個指針變量也會指向一個內(nèi)存空間
var name string = "小飯"
var p_name *string = &name
fmt.Println("name:", name, "p_name的值", p_name, " p_name指針變量的內(nèi)存地址:", &p_name)
//輸出
//name: 小飯 p_name的值 0x14000010240 p_name指針變量的內(nèi)存地址: 0x1400000e028
指針在數(shù)組中的應(yīng)用
大家首先得區(qū)分一個概念寸爆,數(shù)組指針和指針數(shù)組的區(qū)別。
數(shù)組指針
簡單說數(shù)組指針就是整個數(shù)組都為指針
a, b, c := 1, 2, 3
arr := [3]int{a, b, c}
var ptrArr *[3]int
ptrArr = &arr
arr[1] = 200 //改變數(shù)組的值,并不會影響到對應(yīng)數(shù)組元素的變量本身
fmt.Println(b)
fmt.Println(arr[1])
fmt.Println((*ptrArr)[1]) // 可以簡單寫為:ptrArr[1]
//結(jié)果輸出為
//2
//200
//200
直接改變數(shù)組的某個元素而昨,不會影響到對應(yīng)元素的變量救氯。
指針數(shù)組
簡單說就是數(shù)組每個元素都為指針
a, b, c := 1, 2, 3
arr := [3]int{a, b, c}
arr[1] = 2 // 修改普通數(shù)組中的值
// 定義指針數(shù)組
var ptrArr [3]*int //每個元素為一個指針
ptrArr = [3]*int{&a, &b, &c}
*ptrArr[1] = 200 //修改某個元素的指,不會影響到數(shù)組本身
fmt.Println(b)
fmt.Println(arr[1])
fmt.Println(*ptrArr[1])
//結(jié)果輸出
//200
//2
//200
當(dāng)然指針數(shù)組和數(shù)組指針有很多細節(jié)需要注意歌憨,如果這篇文章閱讀量還可以着憨,咱們后面專門會開一篇講解這個問題。在這里有個簡單的認識即可务嫡。希望大家記得多多轉(zhuǎn)發(fā)和點贊哦甲抖。
指針在函數(shù)中的應(yīng)用
case1
func main() {
var a int = 123
changeData(a)
fmt.Println(a)
}
func changeData(b int) {
b = 456
}
//輸出結(jié)果
//123
大家想象一下最終打印出來的a是123 還是456,當(dāng)然是123.為什么會這樣呢心铃?因為運行到changeData中准谚,把a傳進去之后,相當(dāng)于執(zhí)行了一步
var b int
b = 1
所以自然對b進行任何修改都不會影響到a去扣,輸出的自然是123
case2
func main() {
var a int = 123
changeData(&a)
fmt.Println(a)
}
func changeData(b *int) {
*b = 456
}
在這一次函數(shù)參數(shù)傳遞中柱衔,相當(dāng)于執(zhí)行了
var b *int
b = &a
b就是指向a的指針,所以*b修改了愉棱,a自然也會跟著修改唆铐。
golang指針和c語言指針的區(qū)別
大家知道C語言之所以強大,就是因為c語言支持指針奔滑,而且權(quán)限特別大艾岂,c語言可以對計算機中任何內(nèi)存的指針進行操作,這樣自然而然也會帶來一些不安全的因素朋其,所以在golang中王浴,取消了對指針的一些偏移,翻轉(zhuǎn)等算術(shù)運算(+梅猿、-氓辣、++、--)所以使用起來更安全粒没。
本文由mdnice多平臺發(fā)布