是一種在程式語言中用於儲存記憶體位址的特殊變數或數據型態。指標允許你直接訪問和操作記憶體中的數據,而不是僅存儲數據的值。
在許多程式語言中,包括 Go,在記憶體中的每個變數或數據都有一個唯一的位址,指標就是用來引用這些位址的。
簡單來說,指摽就是用來儲存資料的記憶體位址。
先前提到的變數都是把右邊的值存進左邊的變數中,而這些變數其實就在記憶體中。而記憶體都會有「位址」,這一章節要將這些變數所在的位址提取出來。
再開始介紹指標之前先看看它大概長怎樣:
package main
import "fmt"
func main(){
num := 10
pointer := &num
fmt.Println(pointer)
}
執行結果
0x140000b4008
注意!!
每一台電腦所輸出的結果都不會一樣。
小知識:
地址是一串數字為什麼會出來數字和英文?在程式語言中以0x
開頭的數字代表是 16 進位 由0~f
組成,跟一般我們常用的 10 進位不太一樣。我們的電腦是由 0 和 1 的 2 進位系統組成,因為 2 進位與 16 進位之間比較好轉換 ,所以比起 10 進位,工程師更常使用 16 進位
當我們在談指標時,談的不是一件事,而是一套流程。
這套流程的思考順序如下:
接下來一一來介紹以上的流程:
也就是宣告變數及資料型態,如下:
var x int = 3
其實我們也可以簡化宣告變數的寫法:
x := 3
可以取得這個資料變數的記憶體位址,要怎麼取得,如下:
var x int = 3
// 取得記憶體位址:&變數名稱
fmt.Println(&x)
取得記憶體位址很簡單,只要 &變數名稱
,它就會告訴我它放在記憶體位址的哪個地方。
我們的每一個資料都會被放在記憶體的某個地方。
舉一個生活中的例子:
每棟房子都會有那棟房子專屬的地址,而資料也是。
房子地址就會是資料中的記憶體位址。
房子就會是資料。
有沒有需要把取得的記憶體位址存放起來,如果需要,就要建立一個指標變數,建立指摽變數的方法如下:
package main
import "fmt"
func main() {
var x int = 3
// 指標資料型態: *資料型態
var xPtr *int = &x
fmt.Println(xPtr)
}
執行結果:
0xc000012028
資料變數與指標變數的差異在於它的資料型態,所以 int
就是整數的指標。
所以一開始的資料型態是 int 那指標資料型態就會是 int
,string
就會是 string
。&x
就會被放入 xPtr
這個指標變數中。
印出來會是記憶體位址。
有時候,我們會有指標變數,想要知道對應的指標變數對應的資料是什麼,這時候就需要反解指標變數,去取得原始的資料。
package main
import "fmt"
func main() {
var x int = 3
// 指標資料型態: *資料型態
var xPtr *int = &x
fmt.Println(*xPtr)
}
執行結果:
3
注意!!
這邊要特別注意不要把 *指標資料型態
,搞混成 *指標變數名稱
package main
import "fmt"
func main() {
// 1. 建立存放資料的變數
var x int = 5
fmt.Println("原來的資料:", x)
// 2. 取得記憶體位址:&變數名稱
fmt.Println("資料的記憶體位址:", &x)
// 3. 存放到指標變數,注意指標資料型態:*資料型態
var xPtr *int = &x
fmt.Println("資料的記憶體位址:", xPtr)
// 4. 反解指標變數:*指標變數名稱
fmt.Println("反解指標回原來的資料:", *xPtr)
}
執行結果:
原來的資料: 5
資料的記憶體位址: 0xc000012028
資料的記憶體位址: 0xc000012028 (這邊會印出跟 2 一樣的資料,因為將資料的記憶體位址存放在 xPtr 的指摽變數中)
反解指標回原來的資料: 5
package main
import "fmt"
func main() {
// 1. 建立存放資料的變數
var word string = "Hello"
fmt.Println("原來的資料:", word)
// 2. 取得記憶體位址:&變數名稱
fmt.Println("資料的記憶體位址:", &word)
// 3. 存放到指標變數,注意指標資料型態:*資料型態
var wordPtr *string = &word
fmt.Println("資料的記憶體位址:", wordPtr)
// 4. 反解指標變數:*指標變數名稱
fmt.Println("反解指標回原來的資料:", *wordPtr)
}
執行結果:
原來的資料: Hello
資料的記憶體位址: 0xc000014070
資料的記憶體位址: 0xc000014070
反解指標回原來的資料: Hello
所謂的反解指摽回原來的資料,也就是將字串指標在變回原本的字串,所以這邊也可以這樣寫:
package main
import "fmt"
func main() {
var word string = "Hello"
fmt.Println("原來的資料:", word)
fmt.Println("資料的記憶體位址:", &word)
var wordPtr *string = &word
fmt.Println("資料的記憶體位址:", wordPtr)
word = *wordPtr
fmt.Println("反解指標回原來的資料:", word)
}
也可以用反解指摽變數去存在原本的變數。
或是在宣告一個新的變數去存放反解指標變數,這樣也可以。
package main
import "fmt"
func main() {
var word string = "Hello"
fmt.Println("原來的資料:", word)
fmt.Println("資料的記憶體位址:", &word)
var wordPtr *string = &word
fmt.Println("資料的記憶體位址:", wordPtr)
var another string = *wordPtr
fmt.Println("反解指標回原來的資料:", another)
}
我們可以利用指標特性直接更改其指向變數的值。
package main
import "fmt"
func main(){
num := 10
p := &num
fmt.Println("更改前的 num =",num)
*p = 4450
fmt.Println("更改後的 num =",num)
fmt.Println("p = ", &num)
fmt.Println("*p = ", *p)
}
執行結果:
更改前的 num = 10
更改後的 num = 4450
p = 0xc000012028
*p = 4450
利用指標的特性,透過更改該指標變數的方式更改指向變數的值。
既然可以用指標變數去更改變數的值,那麼是不是可以用非指標變數去更改另一個變數的值?
答案是不行!!!
package main
import "fmt"
func main(){
num := 10
num2 := num
num2 = 20
fmt.Println("num2 =", num2)
fmt.Println("num =", num)
}
執行結果:
num2 = 20
num = 10
第 5 行嘗試複製 num
,但這時已經將 num2
放到新的記憶體空間,並將其值放進去,所以之後去做更改,也不會更改到 num
。
我們印出兩個變數的記憶體位址出來:
package main
import "fmt"
func main(){
num := 10
num2 := num
num2 = 20
fmt.Println("num =", num)
fmt.Println("pointer of num =", &num)
fmt.Println("num2 =", num2)
fmt.Println("pointer of num2 =", &num2)
}
執行結果:
num = 10
pointer of num = 0x1400001c0c8
num2 = 20
pointer of num2 = 0x1400001c0e0
印出來的結果,可以看到兩個變數的位址都不一樣,所以不能透過非指標變數去更改另一個變數的值。
參考資料: