指针相关的部分实在是没有搞太明白,抽时间来总结下。
1.指针相关基础知识
比如现在有一句话:『谜底666』,这句话在程序中一启动,就要加载到内存中,假如内存地址0x123456,然后我们可以将这句话复制给变量A,这句话的地址复制给变量B,首先变量B就是一个指针变量。
& 运算符:用于取地址
*运算符:用于根据地址取值
PS:地址是干嘛的:每个变量在运行是都有一个地址,这个地址代表该变量在内存中的位置
func main() {a := 10b := &a //取地址 等同于 var b *int = &a 此时的*int 代表变量b是一个*int类型fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*intfmt.Println(&b) // 0xc00000e018 取b的地址(b本身也是一个指针类型)
}
放一张图:
对普通变量使用&操作符取地址的话,会得到这个变量的指针,然后可以用*操作符进行指针取值。
func main() {//指针取值a := 10b := &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 mainimport "fmt"func main() {var (a int = 100b 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 := 10modify1(a)fmt.Println(a) // 10 不改变,modify2(&a)fmt.Println(a) // 100
}
上述代码也可以这样操作:
func modify3(x int) int{x = 100return x
}func main() {a := 10a = modify3(a) // 这里虽然都叫a,但是内存地址是不一样的fmt.Println(a) // 100
}
再来一个栗子:
func main() {a := 10pointerDemo03(&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) // *intfmt.Printf("%T\n", b) // *boolfmt.Println(*a) // 0fmt.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 := 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)}
}
5.结构体指针
直接使用结构体的方式(用new):
type People struct{Name stringAge 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) //输出:truep3 := new(People)
p3 = &People{"jeff", 18}
//结构体变量不能和指针比较,使用*指针取出地址中值
fmt.Println(p1 == *p3) //输出:truep4 := &People{"jeff", 18}
//指针比较的是地址
fmt.Println(p3 == p4) //输出:false
PS:使用new来初始化结构体后,得到的是结构体的地址,栗子如上
再来一个栗子:
type Student struct {// 成员名称不加var关键字id intname stringage intaddr string
}
func main() {stu := Student{001, "itzhuzhu", 23, "广州"}var p *Studentp = &stupointerDemo10(p)fmt.Println(stu)
}func pointerDemo10(p *Student) {p.addr = "深圳"
}
//输出结果为{1 itzhuzhu 23 深圳}
下面这个同上面:
func main() {stu := Student{001, "itzhuzhu", 23, "广州"}//var p *Student//p = &stupointerDemo10(&stu)fmt.Println(stu)
}func pointerDemo10(p *Student) {p.addr = "深圳"
}