go语言的指针类型
一、指針與引用的相關(guān)概念
什么是指針?
指針,全稱為指針變量,是用來存儲內(nèi)存地址的一種變量。程序中,一般通過指針來訪問其指向的內(nèi)存地址中的內(nèi)容(數(shù)據(jù))。
什么是引用?
引用,是C++中提出來的一種新的使用變量的方式,即,給實際變量起個別名,通過這個別名來引用實際的變量。標(biāo)準(zhǔn)C中不支持變量的引用。
指針與引用的區(qū)別?
1.指針是實實在在的變量,有自己的內(nèi)存存儲空間,它可以指向任何有效的變量。
2.引用是一種形式、方法,定義的引用變量,實際上是原實際變量的另一個名稱(別名),引用變量本身沒有自己的實際存儲空間,操作引用變量,就是在操作實際變量。
go語言的指針與c語言指針,以及java,python等引用類型的語言的區(qū)別與聯(lián)系:
1.Java,Python,Javascript 等引用類型的語言
2.Golang 的指針是單獨(dú)的類型,而不是 C 語言中的 int 類型,而且也不能對指針做整數(shù)運(yùn)算。
3.從這一點(diǎn)看,Golang 的指針基本就是一種引用。
對引用類型語言來說,參數(shù)傳遞時,什么時候傳值,什么時候傳引用?
1.在大部分引用型語言里,參數(shù)為基本類型時,傳進(jìn)去的大都是值,也就是另外復(fù)制了一份參數(shù)到當(dāng)前的函數(shù)調(diào)用棧。
2.參數(shù)為高級類型時,傳進(jìn)去的基本都是引用。這個主要是因為虛擬機(jī)的內(nèi)存管理導(dǎo)致的。
傳值和傳引用的區(qū)別?
1.內(nèi)存管理中的內(nèi)存區(qū)域一般包括 heap 和 stack, stack 主要用來存儲當(dāng)前調(diào)用棧用到的簡單類型數(shù)據(jù):string,boolean,int,float 等。
2.這些類型的內(nèi)存占用小,容易回收,基本上它們的值和指針占用的空間差不多,因此可以直接復(fù)制,GC也比較容易做針對性的優(yōu)化。
3.復(fù)雜的高級類型占用的內(nèi)存往往相對較大,存儲在 heap 中,GC 回收頻率相對較低,代價也較大,因此傳引用/指針可以避免進(jìn)行成本較高的復(fù)制操作,并且節(jié)省內(nèi)存,提高程序運(yùn)行效率。
而在 Golang 中,具體到高級類型 struct,slice,map,也各有不同。實際上,只有 struct 的使用有點(diǎn)復(fù)雜,slice,map,chan 都可以直接使用,不用考慮是值還是指針。
二、go語言指針介紹
go的原生數(shù)據(jù)類型分類:
1.基本類型:string, bool, int, float等 使用值傳遞
2.高級類型:sturuct, array/slice, map, chan, func。 使用引用傳遞
go的指針定義:
a.一個指針的值是另一個變量的地址。
b.一個指針對應(yīng)變量在內(nèi)存中的存儲位置。
c.并不是每一個值都會有一個內(nèi)存地址。
d.滅一個變量必然有對應(yīng)的內(nèi)存地址?! ?/p>
e.用過指針我們可以直接讀或者更新對應(yīng)變量的值,而不需要知道該變量的名字。
go語言中,什么時候使用指針?(指針使用的場景):
1.需要改變參數(shù)的值
2.避免復(fù)制操作
3.節(jié)省內(nèi)存
go語言的值傳遞和“引用傳遞”
1.go語言中的傳遞方式只有值傳遞
2.所謂的“引用傳遞”是指將要傳遞的較大的值,復(fù)制他的內(nèi)存地址然后將內(nèi)存地址通過值傳遞傳給對象。對象拿到內(nèi)存地址的值就可以直接訪問了。這個過程看起來很像“引用傳遞”
指針(pointer)在go 語言中拆分為兩個核心概念:
1.類型指針,允許對這個指針類型的數(shù)據(jù)進(jìn)行修改。傳遞數(shù)據(jù)使用指針,而無需拷貝數(shù)據(jù)。類型指針不能進(jìn)行偏移量和運(yùn)算。
2.切片,由指向起始元素的原始指針、元素數(shù)量和容量組成。
go語言指針的優(yōu)勢:
1.Go 語言的指針類型變量擁有指針的高效訪問
2.不會發(fā)生指針偏移,從而避免非法修改關(guān)鍵性數(shù)據(jù)問題。
3.垃圾回收也比較容易對不會發(fā)生偏移的指針進(jìn)行檢索和回收。
4.切片比原始指針具備更強(qiáng)大的特性,更為安全。
5.切片發(fā)生越界時,運(yùn)行時會報出宕機(jī),并打出堆棧,而原始指針只會崩潰。
指針類型和指針地址:
1.每個變量在運(yùn)行時都擁有一個地址,這個地址代表變量在內(nèi)存中的位置
2.Go 語言中使用&作符放在變量前面對變量進(jìn)行“取地址”操作。
ptr := &v // v的類型為T
v 代表被取地址的變量
變量prt 接收 被取地址的 v
ptr 的類型就為*T,稱做 T 的指針類型。*代表指針。
package main
import (
"fmt"
)
func main() {
//聲明整型 cat 變量。
var cat int = 1
//聲明字符串 str 變量。
var str string = "banana"
//使用 fmt.Printf 的動詞%p輸出 cat 和 str 變量取地址后的指針值,指針值帶有0x的十六進(jìn)制前綴。
fmt.Printf("%p %p", &cat, &str)
}
//運(yùn)行結(jié)果
//0xc042052088 0xc0420461b0
指針的實際用法
變量、指針、地址,三者的關(guān)系?
1.每個變量都擁有地址
2.指針的值就是地址
如何使用指針獲取指針指向的值?
在對普通變量使用&操作符取地址獲得這個變量的指針后,可以對指針使用*操作,也就是指針取值
package main
import (
"fmt"
)
func main() {
// 準(zhǔn)備一個字符串類型
var house = "Malibu Point 10880, 90265"
// 對字符串取地址, 將指針保存到 ptr 中,ptr類型為*string
ptr := &house
// 打印ptr的類型 類型為 *string。
fmt.Printf("ptr type: %T
", ptr)
// 打印ptr的指針地址 每次運(yùn)行都會發(fā)生變化。
fmt.Printf("address: %p
", ptr)
// 對 ptr 指針變量進(jìn)行取值操作 value 變量類型為 string。
value := *ptr
// 取值后的類型
fmt.Printf("value type: %T
", value)
// 指針取值后就是指向變量的值
fmt.Printf("value: %s
", value)
}
//運(yùn)行結(jié)果
//ptr type: *string
//address: 0xc0420401b0
//value type: string
//value: Malibu Point 10880, 90265
指針獲取指針
取地址操作符&和取值操作符*是一對互補(bǔ)操作符
&取出地址,*根據(jù)地址取出地址指向的值。
變量、指針地址、指針變量、取地址、取值的相互關(guān)系和特性:
1.對變量進(jìn)行取地址(&)操作,可以獲得這個變量的指針變量。
2.指針變量的值是指針地址。
3.對指針變量進(jìn)行取值(*)操作,可以獲得指針變量指向的原變量的值。
指針的零值和相等測試
1.任何類型的指針的零值都是nil。
2.如果 p != nil 為真,那么p就指向某個有效變量 (p為指針)
3.指針只有指向同一個變量或者全部為nil時才相等。
var x,y int var z *int var w *string fmt.Println(&x ==&x, &x ==&y,&x ==nil , z == nil , w ==nil) //true false false true true
指針的相等測試
如何使用指針修改值?
package main
import "fmt"
// 定義一個交換函數(shù),參數(shù)為 a、b,類型都為 *int,都是指針類型。
func swap(a, b *int) {
// 將 a 指針取值,把值(int類型)賦給 t 變量,t 此時也是 int 類型。
t := *a
// 取 b 指針值,賦給 a 變量指向的變量。注意,此時*a的意思不是取 a 指針的值,而是“a指向的變量”。
*a = *b
// 將 t 的值賦給 b 指向的變量。
*b = t
}
func main() {
// 準(zhǔn)備 x、y 兩個變量,賦值 1 和 2,類型為 int。
x, y := 1, 2
// 取出 x 和 y 的地址作為參數(shù)傳給 swap() 函數(shù)進(jìn)行調(diào)用。
swap(&x, &y)
// 交換完畢時,輸出 x 和 y 的值。
fmt.Println(x, y)
}
//運(yùn)行結(jié)果
//2 1
使用指針修改值
1.*操作符作為右值時,意義是取指針的值
2.作為左值時,也就是放在賦值操作符的左邊時,表示 a 指向的變量。
歸納:
1.*操作符的根本意義就是操作指針指向的變量。
2.當(dāng)操作在右值時,就是取指向變量的值;
3.當(dāng)操作在左值時,就是將值設(shè)置給指向的變量。
每次我們對一個變量取地址,或者是復(fù)制指針,我們都是為原變量創(chuàng)建新的別名。例如a就是變量p的別名。
指針特別有價值的地方在于我們可以不用名字而訪問一個變量的?! ?/p>
package main
import "fmt"
func swap(a, b *int) {
b, a = a, b
}
func main() {
x, y := 1, 2
swap(&x, &y)
fmt.Println(x, y)
}
//運(yùn)行結(jié)果
//1 2
swap() 函數(shù)中交換指針值
1.上面代碼中的 swap() 函數(shù)交換的是 a 和 b 的地址
2.在交換完畢后,a 和 b 的變量值確實被交換。
3.但和 a、b 關(guān)聯(lián)的兩個變量并沒有實際關(guān)聯(lián)。
4.這就像寫有兩座房子的卡片放在桌上一字?jǐn)傞_,交換兩座房子的卡片后并不會對兩座房子有任何影響。
創(chuàng)建指針的另一種方法--new()函數(shù)
package main
import "fmt"
func main() {
//申請一個指針str,類型為*string
str := new(string)
//指針str指向字符串“ninja”
*str = "ninja"
//打印str指向的地址空間
fmt.Println(*str)
//打印指針str的內(nèi)存地址
fmt.Printf("%p
",str)
fmt.Printf("%d
",str)
//打印指針str的類型
fmt.Printf("%T
",str)
}
//運(yùn)行結(jié)果
//ninja
//0xc00000e1e0
//824633778656
//*string
new()方法創(chuàng)建指針
總結(jié)
- 上一篇: linux基础简介,Linux简介基础
- 下一篇: 神俑降临苏巧葵怎么样?苏巧葵角色介绍