指针:
- 指针是一个特殊的变量,因为它存储的数据是另一个变量的内存地址,指针本身也是有内存地址的
- 指针的数据类型有int、float、bool、string、数组、结构体
- 指针的作用就是可以通过变量/对象的内存地址去操作变量/对象
注意:
取址运算符&用于获取对象地址
指针运算符*用于 间接引用目标对象
二级指针(一个指针指向另一个指针)**T,或包含包名*package.T
指针定义:
定义格式:
var 指针名 *数据类型
获取内存地址格式:
&变量
获取指针存的数据
fmt.Println(指针名)
获取指针指向的数据
fmt.Println(*指针名)
通过指针修改变量的值:
// *指针变量是获取到内存地址指向的变量值,拿到后可以再修改,或者其它操作
*指针变量 = 值
演示:
func main() {a := 10// 定义一个指针变量p,并把a的内存地址赋值给pvar p *int = &a// 可以简写为var p = &a// 对比a的内存地址和p变量存的数据是一样的fmt.Println("变量a的内存地址:", &a)fmt.Println("指针p的数据:", p)fmt.Println("获取指针指向的数据:", *p)fmt.Println("指针p的内存地址:", &p)// 通过指针修改数据,a的数据也同步修改*p = 222fmt.Println(a)
}
输出:
变量a的内存地址: 0x1400001a090
指针p的数据: 0x1400001a090
获取指针指向的数据: 10
指针p的内存地址: 0x1400000e028
222
指针类型支持相等运算符,但不能做加减法运算和直接类型转换。如果两个指针指向同一地址,或都为nil,那么它们相等
func main() {x := 10p := &xp++ // 无效运算:p++ (non-numeric type *int)p2 := &xprintln(p == p2) // truevar p3 *intvar p4 *intfmt.Println(p3 == p4) // true
}
操作指针的3个注意事项:
- 空指针:定义了指针,但指针内并没保存其他变量的内存地址
func main() {var p *intfmt.Println(p) // <nil>
}
- 野指针:指针没有合法指向的内存
func main() {var p *int*p = 111fmt.Println(p) // 虽然定义了指针并赋值,但是并没有将指针指向任何有效的变量
}
- new函数使用
func main() {// 创建了一个int类型的内存空间,然后让p指向内存空间,然后把222保存到了内存空间中p := new(int) // int是4个字节,所以new(int)这个空间大小就是4byte*p = 222fmt.Println(*p)
}
指针作为函数参数:
在函数中修改变量值,是不影响原变量值的,可以通过指针完成修改。
不通过指针的时候,修改原来的变量,虽然变量都是a,但是内存地址是不一样的,所以在函数中修改完以后会发现原来的变量并没有被修改,但是通过指针去修改的时候是因为指向的是内存地址,所以对函数中的a进行操作,其实就是对原来的a进行操作,所以可以将原来的变量值修改掉
演示:
func main() {a := 10test(&a)fmt.Println(a)
}// 注意,指针作为函数的时候,参数也要加上*
func test(a *int) {*a = 20
}
数组指针作为函数参数
func main() {arr := [10]int{1, 2, 3, 3, 4, 5}var p *[10]intp = &arrfmt.Println("修改前p[0]=", p[0])test(p)fmt.Println("修改后p[0]=", p[0])
}func test(p *[10]int) {p[0] = 111
}
数组指针:
要分清指针数组和数组指针的区别。指针数组是指元素为指针类型的数组,数组指针是获取数组变量的地址
var 数组指针变量 *[索引] 类型
演示:
func main() {arr := [10]int{1, 2, 3, 3, 4, 5}var p *[10]intp = &arrfmt.Println(*p) // 获取数组中的全部数据fmt.Println((*p)[0]) // 获取指定数组中索引的数据,[]的运算优先级高于*p,所以要把*p加括号fmt.Println(p[0]) // 获取指定数组中索引的数据,这个格式和加括号一样,但是简化的写法for i := 0; i < len(p); i++ {fmt.Print(p[i], ",")}
}
指针数组:
指针数组指的是元素为指针类型的数组(一个数组中存储的都是指针),它就是一个存储了地址的数组。
定义格式:
var 数组名 [索引] *类型
演示:
func main() {var p [2]*inta := 10b := 20// 变量a 的内存地址保存在指针数组p的0索引,b保存在1索引p[0] = &ap[1] = &bfmt.Println(p) // 获取p数组中的内存地址fmt.Println(*p[0], *p[1]) // 获取p数组中的指定索引数据for i := 0; i < len(p); i++ {fmt.Println(*p[i]) // 获取p数组中的所有的数据}for key, value := range p {fmt.Println(key, *value)}
}
切片指针:
其实就是定义指针,指向切片
func main() {s := []int{1, 2, 3, 4, 5}var p *[]intp = &sfmt.Println(*p)fmt.Println("修改前:", (*p)[0]) // 指针切片中没有简化的写法,只能加括号先运算指针,再运算切片(*p)[0] = 111fmt.Println("修改后:", (*p)[0]) // 指针切片中没有简化的写法,只能加括号先运算指针,再运算切片for i := 0; i < len(*p); i++ {fmt.Print((*p)[i], " ")if i == len(*p)-1 {fmt.Println()}}for key, value := range *p {fmt.Println("key:", key, "value:", value)}
}
指针结构体:
其实就是定义指针,指向结构体
type Student struct {id intname stringage intaddr string
}func main() {stu := Student{001, "itzhuzhu", 23, "广州"}var p *Studentp = &stufmt.Println(*p) // 获取全部fmt.Println((*p).name) // 获取指定的数据fmt.Println(p.name) // 结构体指针中也是有简化写法的p.addr = "深圳"fmt.Println(*p)
}
将结构体指针作为函数参数:
type Student struct {id intname stringage intaddr string
}func main() {stu := Student{001, "itzhuzhu", 23, "广州"}var p *Studentp = &stutest(p)fmt.Println(stu)
}func test(p *Student) {p.addr = "深圳"
}
多级指针:
多级指针指的是,存放的都是上一级指针的内存地址,二级指针存的是一级指针的内存地址,以此类推,多级指针的概念和二维数组的概念类似。多级指针可以无限定义级别,几级指针定义的时候就要写几个*
定义格式:
var 指针变量名 *类型
// 几级指针就写几个* 一个* 就是一级指针
演示:
func main() {a := 10var p *intp = &a// 定义二级指针,var p2 **int//var p3 ***int//var p4 ****intp2 = &pfmt.Println(**p2) // 10
}