【10.0】Go语言基础之指针
【一】什么是指针
-
指针是—种存储变量内存地址(Memory Address)的变量。
-
如上图所示,变量b 的值为156 ,而b 的内存地址为0x1040a124。
- 变量 α存储了b 的地址。
- 我们就称a指向了b 。
【二】指针的定义
【1】指针的语法基础
- 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
- 2 在变量前加 & 取地址符号,表示取该变量的地址
- 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
【2】指针的定义和使用
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// [1]定义一个int类型的变量
var a int = 10
// [2]定义一个变量,来存a的地址 : * 表示指针类型
var p *int
// [3]取地址 :& 表示取该变量的地址
p = &a
// 每一次程序运行申请的内存空间是不一致的,所以每次指针的指向地址也会跟着变化
// 程序重启,内存空间大概率发生更改;程序不终止,当前内存地址不会发生变化
fmt.Println("当前指针 p :>>>> ", p)
// 当前指针 p :>>>> 0xc00001e0a8
// 当前指针 p :>>>> 0xc0000a6058
}
【3】指针的指针
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// [1]定义一个int类型的变量
var a int = 10
// [2]定义一个变量,来存a的地址 : * 表示指针类型
var p *int
// [3]取地址 :& 表示取该变量的地址
p = &a
// 每一次程序运行申请的内存空间是不一致的,所以每次指针的指向地址也会跟着变化
// 程序重启,内存空间大概率发生更改;程序不终止,当前内存地址不会发生变化
fmt.Println("当前指针 p :>>>> ", p)
// 当前指针 p :>>>> 0xc00001e0a8
// 当前指针 p :>>>> 0xc0000a6058
// [4]指针的指针
var p1 **int = &p // 取一次加一个 *
fmt.Println("当前指针 p1 :>>>> ", p1)
var p2 ***int = &p1
fmt.Println("当前指针 p2 :>>>> ", p2)
//当前指针 p :>>>> 0xc00001e0a8
//当前指针 p1 :>>>> 0xc00000a028
//当前指针 p2 :>>>> 0xc00000a038
}
【4】指针反解
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// [1]定义一个int类型的变量
var a int = 10
// [2]定义一个变量,来存a的地址 : * 表示指针类型
var p *int
// [3]取地址 :& 表示取该变量的地址
p = &a
// 每一次程序运行申请的内存空间是不一致的,所以每次指针的指向地址也会跟着变化
// 程序重启,内存空间大概率发生更改;程序不终止,当前内存地址不会发生变化
fmt.Println("当前指针 p :>>>> ", p)
// 当前指针 p :>>>> 0xc00001e0a8
// 当前指针 p :>>>> 0xc0000a6058
// [4]指针的指针
var p1 **int = &p // 取一次加一个 *
fmt.Println("当前指针 p1 :>>>> ", p1)
var p2 ***int = &p1
fmt.Println("当前指针 p2 :>>>> ", p2)
//当前指针 p :>>>> 0xc00001e0a8
//当前指针 p1 :>>>> 0xc00000a028
//当前指针 p2 :>>>> 0xc00000a038
// [5]地址反解成值 : 在(指针)变量前加 * 表示反解,把地址解析成具体的值
fmt.Println("反解*p :>>>> ", *p) // 反解指针变量 p 指向 a 解析出 a 的值
fmt.Println("反解*p1 :>>>> ", *p1) // 反解指针变量 p1 指向 p 解析出 p 的值
fmt.Println("反解**p1 :>>>> ", **p1) // 反解指针变量 p1 指向 p 再指向 a 解析出 a 的值
fmt.Println("反解*p2 :>>>> ", *p2) // 反解指针变量 p2 指向 p1 解析出 p1 的值
fmt.Println("反解***p2 :>>>> ", ***p2) // 反解指针变量 p2 指向 p1 指向 p 再指向 a 解析出 a 的值
//当前指针 p :>>>> 0xc00001e0a8
//当前指针 p1 :>>>> 0xc00000a028
//当前指针 p2 :>>>> 0xc00000a038
//反解*p :>>>> 10
//反解*p1 :>>>> 0xc00001e0a8
//反解**p1 :>>>> 10
//反解*p2 :>>>> 0xc00000a028
//反解***p2 :>>>> 10
}
【5】指针类型的零值
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// [5] 指针的零值
name := "Dream"
var n *string
fmt.Println("当前指针指向变量前 n :>>>> ", n)
// 指针指向 变量 name 取值 &
n = &name
fmt.Println("当前指针指向变量后 n :>>>> ", n)
//当前指针指向变量前 n :>>>> <nil>
//当前指针指向变量后 n :>>>> 0xc000088240
}
【6】指针是引用类型
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// 指针是引用类型 ----> 当参数传递
a := 10 // var a int = 10
p := &a // var p *int = &a
// 调用函数
foo(p)
fmt.Println("函数调用后修改值 :>>>> ", *p)
//反解指针类型 p :>>>> 10
//函数内部修改值 :>>>> 99
//函数调用后修改值 :>>>> 99
}
// 参数类型为 *int : 指针类型(指向 int 类型的指针)
func foo(p *int) {
fmt.Println("反解指针类型 p :>>>> ", *p)
// 修改值 : 并不是修改 p 的值,而是修改 p 指向的 a 的值
*p = 99
fmt.Println("函数内部修改值 :>>>> ", *p)
}
【7】不要向函数传递数组的指针,而应该使用切片
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// 不要向函数传递数组的指针,而应该使用切片
var name = [3]string{"梦梦", "蚩梦", "小梦"}
// 调用函数 : 不要向函数传递数组的指针
foo(&name) // 把 数组 name 的地址取出来 ---> 变成了 指针类型指向 name
fmt.Println("函数外修改后的数组 :>>>> ", name)
//取值 p[1] :>>>> 蚩梦
//函数内修改后的数组 :>>>> [醉梦 蚩梦 小梦]
//函数外修改后的数组 :>>>> [醉梦 蚩梦 小梦]
var a []string = name[:]
// 调用函数 : 传切片进去
bar(a)
fmt.Println("函数外修改后的切片 :>>>> ", a)
fmt.Println("函数外修改后的数组 :>>>> ", name)
//取值 o[1] :>>>> 蚩梦
//函数内修改后的数组 :>>>> [晓梦 蚩梦 小梦]
//函数外修改后的切片 :>>>> [晓梦 蚩梦 小梦]
//函数外修改后的数组 :>>>> [晓梦 蚩梦 小梦]
// 总结 :
// 使用切片的好处是,如果数组长度变了,函数不需要重写
// 如果是数组,需要重写再写一个函数对应
}
// 参数类型为 *int : 指针类型(指向 int 类型的指针)
func foo(p *[3]string) {
// (*p)[1] ----> 先对 p 解引用 变成数组, 再对 p 取值
fmt.Println("取值 p[1] :>>>> ", (*p)[1])
// 修改值
(*p)[0] = "醉梦"
fmt.Println("函数内修改后的数组 :>>>> ", *p)
}
// 参数类型为 o []string : 切片类型
func bar(o []string) {
// (*p)[1] ----> 先对 p 解引用 变成数组, 再对 p 取值
fmt.Println("取值 o[1] :>>>> ", o[1])
// 修改值
o[0] = "晓梦"
fmt.Println("函数内修改后的数组 :>>>> ", o)
}
【8】数组的指针不需要解引用
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// 如果是数组的指针,不需要解引用,直接使用即可,按索引取值即可
var a = [3]int{7, 8, 9}
var p *[3]int = &a
fmt.Println("解引用再取值 :>>>> ", (*p)[0])
fmt.Println("不需要解引用再取值 :>>>> ", p[0]) // 支持直接按索引取值
fmt.Println("取 p :>>>> ", p) // &[7 8 9]
//解引用再取值 :>>>> 7
//不需要解引用再取值 :>>>> 7
//取 p :>>>> &[7 8 9]
}
【9】不支持指针运算
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// go 语言不支持指针运算
var a = [3]int{7, 8, 9}
var p *[3]int = &a
// p++ : 在 c 语言中表示 p[1]
fmt.Println("指针运算 :>>>> ", p++) // ')', ',' or '...' expected, got '++'
}
【10】指针数组和数组指针
package main
import "fmt"
func main() {
// 指针 :存储 变量内存地址 的 变量
// 1 类型前放 * 表示指针类型,这个类型的指针,指向这个类型的指针
// 2 在变量前加 & 取地址符号,表示取该变量的地址
// 3 在(指针)变量前加 * 表示反解,把地址解析成具体的值
// 指针数组(数组里放了一堆指针)和数组指针(指向数组的指针)
// 定义数组
var a = [3]int{7, 8, 9}
// 定义指针 指向数组 --- 数组指针
var p *[3]int = &a
fmt.Println("这是定义的数组指针 p :>>>> ", p)
// 这是定义的数组指针 p :>>>> &[7 8 9]
// 定义数组 , 数组内的类型 为 指针类型 ----> 指针数组
var x, y, z = 4, 5, 6
var b [3]*int = [3]*int{&x, &y, &z}
fmt.Println("这是定义的指针数组 b :>>>> ", b)
// 这是定义的指针数组 b :>>>> [0xc0000a6090 0xc0000a6098 0xc0000a60a0]
}
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17826952.html