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