指针相关的部分实在是没有搞太明白,抽时间来总结下。
1.指针相关基础知识
比如现在有一句话:『谜底666』,这句话在程序中一启动,就要加载到内存中,假如内存地址0x123456,然后我们可以将这句话复制给变量A,这句话的地址复制给变量B,首先变量B就是一个指针变量。
& 运算符:用于取地址
*运算符:用于根据地址取值
PS:地址是干嘛的:每个变量在运行是都有一个地址,这个地址代表该变量在内存中的位置
func main() {
a := 10
b := &a //取地址 等同于 var b *int = &a 此时的*int 代表变量b是一个*int类型
fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078
fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int
fmt.Println(&b) // 0xc00000e018 取b的地址(b本身也是一个指针类型)
}
放一张图:
对普通变量使用&操作符取地址的话,会得到这个变量的指针,然后可以用*操作符进行指针取值。
func main() {
//指针取值
a := 10
b := &a // 取变量a的地址,将指针保存到b中
fmt.Printf("type of b:%T\n", b)
c := *b // 指针取值(根据指针去内存取值)
fmt.Printf("type of c:%T\n", c)
fmt.Printf("value of c:%v\n", c)
}
输出如下:
type of b:*int
type of c:int
value of c:10
再来个例子:
package main
import "fmt"
func main() {
var (
a int = 100
b int = 300
)
fmt.Printf("交换前a的值: %d\n", a)
fmt.Printf("交换前b的值: %d\n\n", b)
swap(&a, &b)
fmt.Printf("交换后a的值: %d\n", a)
fmt.Printf("交换后b的值: %d\n", b)
}
func swap(x, y *int) {
//值传递,两数交换
*x, *y = *y, *x
}
//输出结果如下
交换前a的值: 100
交换前b的值: 300
交换后a的值: 300
交换后b的值: 100
小结:
- 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
- 指针变量的值是指针地址。
- 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
2.如何进行指针传值
func modify1(x int) {
x = 100
}
func modify2(x *int) { //表示x是一个*int类型
*x = 100 //对变量x取值后,赋值100,由于在主函数传的是变量a的地址,所以相当于对a赋值100
}
func main() {
a := 10
modify1(a)
fmt.Println(a) // 10 不改变,
modify2(&a)
fmt.Println(a) // 100
}
上述代码也可以这样操作:
func modify3(x int) int{
x = 100
return x
}
func main() {
a := 10
a = modify3(a) // 这里虽然都叫a,但是内存地址是不一样的
fmt.Println(a) // 100
}
再来一个栗子:
func main() {
a := 10
pointerDemo03(&a)
fmt.Println(a)
}
// 注意,指针作为函数的时候,参数也要加上*
func pointerDemo03(a *int) {
*a = 20
}
//到现在位置应该明白了,不用再赘述这个了
3.new(不太在实际项目中见到)
使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值,每次调用 new 函数都会返回唯一的地址变量(当定义一个空 struct 时,通过 new 创建一个变量时,返回的地址是相同的。):
func main() {
a := new(int)
b := new(bool)
fmt.Printf("%T\n", a) // *int
fmt.Printf("%T\n", b) // *bool
fmt.Println(*a) // 0
fmt.Println(*b) // false
}
关于new函数再举一个栗子:
func main() {
// 创建一个未命名的 int 类型变量,初始值是 0,返回值 p 是指向 int 类型变量的指针。
p := new(int)
fmt.Println(p, *p) // 0xc00001c0b8 0
// 创建一个未命名的 string 类型变量,初始值是 "", 返回值是 q 是指向 string 类型变量的指针。
q := new(string)
fmt.Println(q, *q) // 0xc000010240 ""
*q = "a"
fmt.Println(*q) // a
}
make
make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了
4.数组指针与指针数组
指针数组是指元素为指针类型的数组,数组指针是获取数组变量的地址
数组指针:
func pointerDemo05() {
arr := [10]int{1, 2, 3, 3, 4, 5}
var p *[10]int //定义p是一个数组指针
p = &arr //对这个数组指针赋值,赋值的是变量arr的地址
fmt.Println(*p) // 获取数组中的全部数据
fmt.Println((*p)[0]) // 获取指定数组中索引的数据,因为*p是先运算,所以要先加括号,否则编译报错
fmt.Println(p[0]) // 获取指定数组中索引的数据,这个格式和加括号一样,但是简化的写法
for i := 0; i < len(p); i++ {
fmt.Print(p[i], ",")
}
}
指针数组:元素为指针类型的数组
func pointerDemo07() {
var p [2]*int //注意和上面的区别
a := 10
b := 20
// 变量a的内存地址保存在指针数组p的0索引,b保存在1索引
p[0] = &a
p[1] = &b
fmt.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)
}
}
5.结构体指针
直接使用结构体的方式(用new):
type People struct{
Name string
Age int
}
peo := new(People)
//因为结构体本质是值类型,所以创建结构体指针时已经开辟了内存空间
fmt.Println(peo == nil) //输出:false
//由于结构体中属性并不是指针类型,所以可以直接调用
peo.Name = "jeff"
fmt.Println(peo)//输出:&{jeff 0}
peo1:=peo
peo1.Name="高级语言"
fmt.Println(peo1,peo)//输出:&{高级语言 0} &{高级语言 0}
也可以声明结构体指针来进行赋值:
//声明结构体指针
var peo *People //表示变量peo是一个*People类型(结构体指针类型)
//给结构体指针赋值
peo = &People{"jeff", 18}
/*
上面代码使用短变量方式如下
peo:= &People{"jeff", 18}
*/
fmt.Println(peo)
结构体指针比较的是地址,*结构体指针取出地址中对应的值
p1 := People{"jeff", 18}
p2 := People{"jeff", 18}
fmt.Printf("%p %p\n", &p1, &p2) //输出地址不同
fmt.Println(p1 == p2) //输出:true
p3 := new(People)
p3 = &People{"jeff", 18}
//结构体变量不能和指针比较,使用*指针取出地址中值
fmt.Println(p1 == *p3) //输出:true
p4 := &People{"jeff", 18}
//指针比较的是地址
fmt.Println(p3 == p4) //输出:false
PS:使用new来初始化结构体后,得到的是结构体的地址,栗子如上
再来一个栗子:
type Student struct {
// 成员名称不加var关键字
id int
name string
age int
addr string
}
func main() {
stu := Student{001, "itzhuzhu", 23, "广州"}
var p *Student
p = &stu
pointerDemo10(p)
fmt.Println(stu)
}
func pointerDemo10(p *Student) {
p.addr = "深圳"
}
//输出结果为{1 itzhuzhu 23 深圳}
下面这个同上面:
func main() {
stu := Student{001, "itzhuzhu", 23, "广州"}
//var p *Student
//p = &stu
pointerDemo10(&stu)
fmt.Println(stu)
}
func pointerDemo10(p *Student) {
p.addr = "深圳"
}