# **Go 指針**
Go支持指針!**指針**是內存地址,它能夠提升代碼運行效率但是增加了代碼的復雜度,C程序員深受指針的折磨。在`第2章`中當我們討論不安全的代碼時,就使用過指針,這一節哦我們將深入介紹Go指針的一些難點。另外,當你足夠了解原生Go指針時,其安全性大可放心。
使用指針時,`*`可以獲取指針的值,此操作成為**指針的解引用**,`*`也叫取值操作符;`&`可以獲取非指針變量的地址,叫做取地址操作符。
>Tip: 通常來說,經驗較少的開發者應該盡量少使用指針,因為指針很容易產生難以察覺的bug。
你可以創建一個參數為指針的函數:
```go
func getPointer(n *int) {
}
```
同樣,一個函數的返回值也可以為指針:
```go
func returnPointer(n int) *int {
}
```
`pointers.go`展示了如何安全地使用Go指針,該文件分為4部分,其中第一部分是:
```go
package main
import "fmt"
func getPointer(n *int) {
*n = *n * *n
}
func returnPointer(n int) *int {
v := n * n
return &v
}
```
`getPointer()`的作用是修改傳遞來的參數,而無需返回值。這是因為傳遞的參數是指針,其指向了變量的地址,所以能夠將變量值的改變反映到原值上。
`returnPointer()`的參數是一個整數,返回值是指向整數的指針,盡管這樣看起來并沒有什么用處,但是在第四章中,當我們討論指向結構體的指針以及其他復雜數據結構時,你就會發現這種操作的優勢。
`getPointer()`和`returnPointer()`函數的作用都是求一個整數的平方,區別在于`getPointer()`使用傳遞來的參數存儲計算結果,而`returnPointer()`函數重新聲明了一個變量來存儲運算結果。
第二部分:
```go
func main() {
i := -10
j := 25
pI := &i
pJ := &j
fmt.Println("pI memory:", pI)
fmt.Println("pJ memory:", pJ)
fmt.Println("pI value:", *pI)
fmt.Println("pJ memory:", *pJ)
```
`i`和 `j`是整數,`pI`和`pJ`分別是指向`i`和`j`的指針,`pI`是變量的內存地址,`*pI`是變量的值。
第三部分:
```go
*pI = 123456
*pI--
fmt.Println("i:", i)
```
這里我們使用指針`pI`改變了變量`i`的值。
最后一部分代碼:
```go
getPointer(pJ)
fmt.Println("j:", j)
k := returnPointer(12)
fmt.Println(*k)
fmt.Println(k)
}
```
根據前面的討論,我們通過修改`pJ`的值就可以將改變反映到`j`上,因為`pJ`指向了`j`變量。我們將`returnPointer()`的返回值賦值給指針變量`k`。
運行`pointers.go`的輸出是:
$ go run pointers.go
pI memory: 0xc420014088
pJ memory: 0xc420014090
pI value: -10
pJ memory: 25
i: 123455
j: 625
144
0xc4200140c8
你可能對`pointers.go`中的某些代碼感到困惑,因為我們在第六章才開始討論函數及函數定義,可以去了解關于函數的更多信息。
>Tip: 在Go中字符串是數值類型而不是指針這和C語言不一樣。
- 介紹
- 1 Go與操作系統
- 01.1 Go的歷史
- 01.2 Go的未來
- 01.3 Go的優點
- 01.3.1 Go是完美的么
- 01.3.2 什么是預處理器
- 01.3.3 godoc
- 01.4 編譯Go代碼
- 2 理解 Go 的內部構造
- Go 編譯器
- Go 的垃圾回收
- 三色算法
- 有關 Go 垃圾收集器操作的更多信息
- Maps, silces 與 Go 垃圾回收器
- Unsafe code
- 有關 unsafe 包
- 另一個 usafe 包的例子
- 從 Go 調用 C 代碼
- 在同一文件用 Go 調用 C 代碼
- 在單獨的文件用 Go 調用 C 代碼
- 從 C 調用 Go 代碼
- Go 包
- C 代碼
- defer 關鍵字
- 用 defer 打印日志
- Panic 和 Recover
- 單獨使用 Panic 函數
- 兩個好用的 UNIX 工具
- strace
- dtrace
- 配置 Go 開發環境
- go env 命令
- Go 匯編器
- 節點樹
- 進一步了解 Go 構建
- 創建 WebAssembly 代碼
- 對 Webassembly 的簡單介紹
- 為什么 WebAssembly 很重要
- Go 與 WebAssembly
- 示例
- 使用創建好的 WebAssembly 代碼
- Go 編碼風格建議
- 練習和相關鏈接
- 本章小結
- 3 Go基本數據類型
- 03.1 Go循環
- 03.1.1 for循環
- 03.1.2 while循環
- 03.1.3 range關鍵字
- 03.1.4 for循環代碼示例
- 03.3 Go切片
- 03.3.1 切片基本操作
- 03.3.2 切片的擴容
- 03.3.3 字節切片
- 03.3.4 copy()函數
- 03.3.5 多維切片
- 03.3.6 使用切片的代碼示例
- 03.3.7 使用sort.Slice()排序
- 03.4 Go 映射(map)
- 03.4.1 Map值為nil的坑
- 03.4.2 何時該使用Map?
- 03.5 Go 常量
- 03.5.1 常量生成器:iota
- 03.6 Go 指針
- 03.7 時間與日期的處理技巧
- 03.7.1 解析時間
- 03.7.2 解析時間的代碼示例
- 03.7.3 解析日期
- 03.7.4 解析日期的代碼示例
- 03.7.5 格式化時間與日期
- 03.8 延伸閱讀
- 03.9 練習
- 03.10 本章小結
- 9 并發-Goroutines,Channel和Pipeline
- 09.1 關于進程,線程和Go協程
- 09.1.1 Go調度器
- 09.1.2 并發與并行
- 09.2 Goroutines
- 09.2.1 創建一個Goroutine
- 09.2.2 創建多個Goroutine
- 09.3 優雅地結束goroutines
- 09.3.1 當Add()和Done()的數量不匹配時會發生什么?
- 09.4 Channel(通道)
- 09.4.1 通道的寫入
- 09.4.2 從通道接收數據
- 09.4.3 通道作為函數參數傳遞
- 09.5 管道
- 09.6 延展閱讀
- 09.7 練習
- 09.8 本章小結