Go 学习笔记(13)— 指针定义、指针特点、空指针、指针数组、指向指针的指针、指针作为函数入参
1. 復(fù)合數(shù)據(jù)類型
Go 語(yǔ)言基本的復(fù)合數(shù)據(jù)類型有指針、數(shù)組、切片、字典、通道、結(jié)構(gòu)和接口等。格式如下:
* pointerType // 指針類型,
[n]elementType // 數(shù)組類型,
[]elementType // 切片類型,
map [keyType]valueType // 字典類型
chan valueType // 通道類型// 結(jié)構(gòu)體類型
struct {fieldType fieldTypefieldType fieldType...
}// 接口類型
interface {method1(inputParams) (returnParams)method2(inputParams) (returnParams)...
}
2. 指針定義
每個(gè)變量在運(yùn)行時(shí)都擁有一個(gè)地址,這個(gè)地址代表變量在內(nèi)存中的位置。Go 語(yǔ)言中使用在變量名前面添加&操作符(前綴)來(lái)獲取變量的內(nèi)存地址(取地址操作),格式如下:
ptr := &v // v 的類型為 T
其中 v 代表被取地址的變量,變量 v 的地址使用變量 ptr 進(jìn)行接收, ptr 的類型為*T,稱做 T 的指針類型,*代表指針。
Go 語(yǔ)言的取地址符是 & ,放到一個(gè)變量前使用就會(huì)返回相應(yīng)變量的內(nèi)存地址。
獲取變量在內(nèi)存中地址:
package mainimport "fmt"func main() {var a int = 10fmt.Printf("變量的地址: %x\n", &a) // 變量的地址: c000018068fmt.Printf("%p", &a) // %p 打印變量?jī)?nèi)存地址,指針的值是帶有0x十六進(jìn)制前綴的一組數(shù)據(jù)。
}
一個(gè)指針變量指向了一個(gè)值的內(nèi)存地址。在使用指針前你需要聲明指針。指針聲明格式如下:
var varName *varType
varType 為指針類型, varName 為指針變量名, * 號(hào)用于指定變量是作為一個(gè)指針。以下是有效的指針聲明:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮點(diǎn)型 */
Go 同樣支持多級(jí)指針,類似 **ip 。
一個(gè)指針變量可以指向任何一個(gè)值的內(nèi)存地址,它所指向的值的內(nèi)存地址在 32 和 64 位機(jī)器上分別占用 4 或 8 個(gè)字節(jié),占用字節(jié)的大小與所指向的值的大小無(wú)關(guān)。
當(dāng)一個(gè)指針被定義后沒(méi)有分配到任何變量時(shí),它的默認(rèn)值為 nil 。指針變量通常縮寫(xiě)為 ptr 。
變量、指針和地址三者的關(guān)系是,每個(gè)變量都擁有地址,指針的值就是地址。
3. 指針使用
指針使用流程:
- 定義指針變量
- 為指針變量賦值
- 訪問(wèn)指針變量中指向地址的值
在指針類型前面加上 * 號(hào)(前綴)來(lái)獲取指針?biāo)赶虻膬?nèi)容。
使用示例:
package mainimport "fmt"func main() {var a int= 20 /* 聲明實(shí)際變量 */var ip *int /* 聲明指針變量 */ip = &a /* 指針變量的存儲(chǔ)地址 */fmt.Printf("a 變量的地址是: %x\n", &a )/* 指針變量的存儲(chǔ)地址 */fmt.Printf("ip 變量?jī)?chǔ)存的指針地址: %x\n", ip )/* 使用指針訪問(wèn)值 */fmt.Printf("*ip 變量的值: %d\n", *ip )
}
輸出結(jié)果為:
a 變量的地址是: 20818a220
ip 變量?jī)?chǔ)存的指針地址: 20818a220
*ip 變量的值: 20
以下幾點(diǎn)使用指針的建議:
-
不要對(duì)
map、slice、channel這類引用類型使用指針; -
如果需要修改方法接收者內(nèi)部的數(shù)據(jù)或者狀態(tài)時(shí),需要使用指針;
-
如果需要修改參數(shù)的值或者內(nèi)部數(shù)據(jù)時(shí),也需要使用指針類型的參數(shù);
-
如果是比較大的結(jié)構(gòu)體,每次參數(shù)傳遞或者調(diào)用方法都要內(nèi)存拷貝,內(nèi)存占用多,這時(shí)候可以考慮使用指針;
-
像
int、bool這樣的小數(shù)據(jù)類型沒(méi)必要使用指針; -
如果需要并發(fā)安全,則盡可能地不要使用指針,使用指針一定要保證并發(fā)安全;
-
指針最好不要嵌套,也就是不要使用一個(gè)指向指針的指針,雖然
Go語(yǔ)言允許這么做,但是這會(huì)使你的代碼變得異常復(fù)雜;
4. 指針特點(diǎn)
-
在賦值語(yǔ)句中,
*p出現(xiàn)在=左邊表示是指針聲明,*p出現(xiàn)在=右邊表示取指針指向的值; -
結(jié)構(gòu)體指針訪問(wèn)結(jié)構(gòu)體字段仍然使用
.點(diǎn)操作符,Go不支持->操作符; -
Go不支持指針的運(yùn)算; -
函數(shù)中允許返回變量的地址;
package mainimport "fmt"// Phone is struct type 注釋開(kāi)頭必須是 結(jié)構(gòu)體或者方法的名字,后面再加空格
type Phone struct { //exported type Phone should have comment or be unexportedgo-lintmodel stringcolor stringprice int
}func main() {var a *int = nilvar b *intc := 10b = &ce := *bphone := Phone{"huawei", "blue", 1000}p := &phone// p++ invalid operation: p++ (non-numeric type *Phone)fmt.Printf("a is %v, a type is %T\n", a, a)fmt.Printf("b is %v, b type is %T\n", b, b)fmt.Printf("e is %v, e type is %T\n", e, e)fmt.Printf("p is %v, p type is %T\n", p, p)fmt.Printf("p.model is %v, p.price is %v\n", p.model, p.price)result := sum(5, 3)fmt.Printf("result is %v, result type is %T\n", *result, result)}func sum(a, b int) *int {ret := a + breturn &ret
}
輸出結(jié)果:
a is <nil>, a type is *int
b is 0xc000016068, b type is *int
e is 10, e type is int
p is &{huawei blue 1000}, p type is *main.Phone
p.model is huawei, p.price is 1000
result is 8, result type is *int
-
對(duì)變量進(jìn)行取地址操作使用
&操作符,可以獲得這個(gè)變量的指針變量; -
對(duì)指針變量進(jìn)行取值操作使用
*操作符,可以獲得指針變量指向的原變量的值;
5. Go 空指針
當(dāng)一個(gè)指針被定義后沒(méi)有分配到任何變量時(shí),它的值為 nil 。 nil 指針也稱為空指針。 nil 在概念上和其它語(yǔ)言的 null 、 None 、 nil 、 NULL 一樣,都指代零值或空值。
空指針值為 nil,即沒(méi)有內(nèi)存地址,是不能進(jìn)行賦值操作的,比如下面的示例:
var intP *int
*intP =10
運(yùn)行的時(shí)候會(huì)提示 invalid memory address or nil pointer dereference。這時(shí)候該怎么辦呢?其實(shí)只需要通過(guò) new 函數(shù)給它分配一塊內(nèi)存就可以了,如下所示:
var intP *int = new(int)
//更推薦簡(jiǎn)短聲明法,這里是為了演示
//intP:=new(int)
一個(gè)指針變量通常縮寫(xiě)為 ptr 。空指針判斷方法:
if(ptr != nil) /* ptr 不是空指針 */
if(ptr == nil) /* ptr 是空指針 */
使用示例:
package mainimport "fmt"func main() {var ptr *intfmt.Printf("ptr 的值為 : %x\n", ptr )
}
輸出結(jié)果為:
ptr 的值為 : 0
6. Go 指針數(shù)組
ptr 為整型指針數(shù)組。因此每個(gè)元素都指向了一個(gè)值。以下實(shí)例的三個(gè)整數(shù)將存儲(chǔ)在指針數(shù)組中:
package mainimport "fmt"const MAX int = 3func main() {a := []int{10,100,200}var i intvar ptr [MAX]*int; // 聲明了整型指針數(shù)組for i = 0; i < MAX; i++ {ptr[i] = &a[i] /* 整數(shù)地址賦值給指針數(shù)組 */}for i = 0; i < MAX; i++ {fmt.Printf("a[%d] = %d\n", i,*ptr[i] )}
}
7. Go 指向指針的指針
如果一個(gè)指針變量存放的又是另一個(gè)指針變量的地址,則稱這個(gè)指針變量為指向指針的指針變量。當(dāng)定義一個(gè)指向指針的指針變量時(shí),第一個(gè)指針存放第二個(gè)指針的地址,第二個(gè)指針存放變量的地址:
訪問(wèn)指向指針的指針變量值需要使用兩個(gè) * 號(hào),如下所示:
package mainimport "fmt"func main() {var a intvar ptr *intvar pptr **inta = 3000/* 指針 ptr 地址 */ptr = &a/* 指向指針 ptr 地址 */pptr = &ptr/* 獲取 pptr 的值 */fmt.Printf("變量 a = %d\n", a )fmt.Printf("指針變量 *ptr = %d\n", *ptr )fmt.Printf("指向指針的指針變量 **pptr = %d\n", **pptr)
}
8. Go 指針作為函數(shù)參數(shù)
Go 語(yǔ)言允許向函數(shù)傳遞指針,只需要在函數(shù)定義的參數(shù)上設(shè)置為指針類型即可。
ackage mainimport "fmt"func main() {/* 定義局部變量 */var a int = 100var b int= 200fmt.Printf("交換前 a 的值 : %d\n", a )fmt.Printf("交換前 b 的值 : %d\n", b )/* 調(diào)用函數(shù)用于交換值* &a 指向 a 變量的地址* &b 指向 b 變量的地址*/swap(&a, &b);fmt.Printf("交換后 a 的值 : %d\n", a )fmt.Printf("交換后 b 的值 : %d\n", b )
}func swap(x *int, y *int) {var temp inttemp = *x /* 保存 x 地址的值 */*x = *y /* 將 y 賦值給 x */*y = temp /* 將 temp 賦值給 y */
}
總結(jié)
以上是生活随笔為你收集整理的Go 学习笔记(13)— 指针定义、指针特点、空指针、指针数组、指向指针的指针、指针作为函数入参的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Go 学习笔记(14)— 结构体定义、实
- 下一篇: 桌子多少钱啊?