Golang的高級數據類型-指針(Pointer)實戰篇
作者:尹正傑
版權聲明:原創作品,謝絕轉載!否則將追究法律責任。
前面分享過存儲數據的方式,可以通過變量,或者復合類型中的數組,切片,Map,結構體等進行存儲。今天我們來分享一下Go語言中的指針操作。
一.變量地址和指針
1>.定義指針變量
package main import ( "fmt" ) func main() { /** 在Go語言中,一旦定義了一個變量就會在內存中開辟空間。因此,在Go語言中為了避免空間的浪費,定義變量若未使用就會編譯報錯。 */ var age uint8 = 18 /** 我們可以使用取地址運算符("&")來獲取數據的內存地址。 %p: 是一個占位符,表示輸出一個十六進制地址格式。 \n: 表示換行符。 */ fmt.Printf("age的值是:[%d],age的內存地址是:[%p]\n", age, &age) /* 接下來我們定義一個uint8類型的指針變量p1,其實指針變量也是變量,只不過指針變量指向了變量的內存地址。 */ var p1 *uint8 /* 對age變量取地址並將結果賦值給指針變量。 */ p1 = &age fmt.Println(p1) }
2>.數組和切片指針的區別
package main import ( "fmt" ) func main() { //定義一個字符串數組 var nameList [56]string fmt.Printf("nameLists數組的內存地址為%p\n", &nameList) fmt.Printf("nameList數組中第一個元素的地址為%p\n", &nameList[0]) fmt.Printf("nameList數組中第二個元素的地址為%p\n", &nameList[1]) fmt.Printf("nameList數組中第三個元素的地址為%p\n\n", &nameList[2]) //定義一個整型數組 var age_list [56]int fmt.Printf("age_list數組的內存地址為%p\n", &age_list) fmt.Printf("age_list數組中第一個元素的地址為%p\n", &age_list[0]) fmt.Printf("age_list數組中第二個元素的地址為%p\n", &age_list[1]) fmt.Printf("age_list數組中第三個元素的地址為%p\n\n", &age_list[2]) //定義一個整型切片 age_slice := make([]int, 56) /** age_slice切片變量保存的是存儲切片容器的地址。他並不像數組那樣直接指向數組的首元素地址,它類似於一個二級指針。 當我們使用取地址運算符對age_slice變量取地址時並不是取的切片真正存儲切片容器的地址,而是存儲切片容器地址變量本身的地址喲~ */ fmt.Printf("age_slice切片變量本身的內存地址為%p\n", &age_slice) /** 通過下標訪問第一個元素其實訪問的是age_slice變量中保存的內存地址對應的下標。 */ fmt.Printf("age_slice切片中第一個元素的地址為%p\n", &age_slice[0]) fmt.Printf("age_slice切片中第二個元素的地址為%p\n", &age_slice[1]) fmt.Printf("age_slice切片中第三個元素的地址為%p\n\n", &age_slice[2]) }
二.指針的使用
1>.通過指針間接修改變量的值
package main import ( "fmt" ) func main() { //使用自動推導類型創建一個age變量 age := 18 //我們將age變量的內存地址賦值給指針變量p1 p1 := &age /** 我們可以使用取值運算符("*")來獲取指針變量的值。 %p: 是一個占位符,表示輸出一個十六進制地址格式。 %d: 是一個占位符,表示輸出一個整型。 \n: 表示換行符。 *: 取值運算符,可以將指針變量中的保存的數據取出來。換句話說,可以將一段內存地址保存的值取出來。 */ fmt.Printf("age的內存地址是:%p,age的值是:%d\n", p1, *p1) //我們可以通過指針間接修改變量的值 *p1 = 27 fmt.Printf("age的內存地址是:%p,age的值是:%d\n", p1, age) }
2>.使用指針注意事項
package main import ( "fmt" ) func main() { /** 溫馨提示: 在使用指針變量時,要注意定義指針默認值為nil,如果直接擦歐總指向nil的內存地址會報錯; 在其它語言中,比如c++,指針是允許運算的,但是在Go語言中,指針只有取地址運算符和取值運算符,不允許指針參與運算喲~ */ var p *int /** 錯誤代碼: 由於p指針變量默認值是nil,因此會報錯"invalid memory address or nil pointer dereference" */ //*p = 123 age := 18 p = &age //所以,在使用指針變量時,一定要讓指針變量有正確的指向。 fmt.Printf("age的內存地址是:%p,age的值是:%d\n", p, *p) /** 錯誤代碼: 在Go語言中,指針只有取地址運算符和取值運算符。 */ //p2 := p + 100 }
三.指針變量賦值
package main import ( "fmt" ) func main() { /** 為指針賦值有兩種方法: 1>.把同類型的變量地址賦值給指針; 2>.使用new函數,就是申請一片內存空間,返回地址; */ var ( p1 *int p2 *int ) fmt.Println("p1的默認值是:", p1) fmt.Println("p2的默認值是:", p2) /** 方案一: 定義一個同類型的變量,然后取地址賦值給指針變量。 */ age := 18 p1 = &age fmt.Printf("p1的內存地址是:%p,p1的值是:%d\n", p1, *p1) /** 方案二: 使用new函數,比如"new(int)"表示創建一個int大小的內存空間,返回為*int */ p2 = new(int) fmt.Printf("p2的內存地址是:%p,p2的值是:%d\n", p2, *p2) *p2 = 2020 //為指針變量賦值 fmt.Printf("p2的內存地址是:%p,p2的值是:%d\n", p2, *p2) }
四.指針的應用場景(指針變量作為函數參數傳遞的時候是引用傳遞)
1>.交換兩個變量的值案例
package main import "fmt" /* 定義一個函數交換2個變量的值,接收的是2個int變量 */ func swap(x int, y int) { x, y = y, x } /* 定義一個函數交換2個變量的值,接收的是2個int指針變量 */ func swapPointer(x *int, y *int) { *x, *y = *y, *x //需要改變的是地址上存儲的內容 } func main() { /* 定義2個變量,一會用於在函數中交換兩個變量的值 */ var ( a = 100 b = 200 ) /* 值傳遞,不能通過函數內部修改影響外部變量。 */ swap(a, b) fmt.Printf("a = [%d], b = [%d]\n", a, b) /* 指針變量作為函數參數傳遞的時候是引用傳遞 */ swapPointer(&a, &b) fmt.Printf("a = [%d], b = [%d]\n", a, b) }
2>.結構體傳參案例
package main import ( "fmt" ) type Student struct { Name string Age int } func addAge(s Student) { s.Age += 1 } /** 在實際開發中,一般傳遞的結構體對象都是指針傳遞 */ func addAgePointer(s *Student) { s.Age += 2 } func main() { s1 := Student{"Jason Yin", 18} addAge(s1) fmt.Printf("姓名:[%s],年齡[%d]\n", s1.Name, s1.Age) /** 結構體對象是值傳遞,但一般函數傳遞的都是結構體對象指針 */ addAgePointer(&s1) fmt.Printf("姓名:[%s],年齡[%d]\n", s1.Name, s1.Age) }