目录
3.1. go语言中的指针具有语法糖,会在相应场合自动解引用去取址
前言
本章将介绍go语言中指针的理解与使用,同时介绍go语言中的又一个引用数据类型map。
一、指针变量与指针
- 指针:指针是一个地址,可以通过指针来获取该地址空间中对应的值
- 指针变量:指针变量是一个变量,该变量中存放的值为指针(既指针变量中存放的是一个地址)
- 下图展示了指针与指针变量的关系。
二、指针的使用
在go语言中,指针允许的操作十分少,只有简单的取址和解引用操作会涉及到指针
go语言中的指针无法进行运算,既p++(p是一个指针)这种操作是非法的。
1.取址
var a = 5
p := &a //p是一个指针变量,其存放着变量a的内存地址
fmt.Printf("%T\n",p) //输出:*int
2.解引获取值
*P = 8
fmt.Println(a) // 输出:8
3.指针的几个注意事项
3.1. go语言中的指针具有语法糖,会在相应场合自动解引用去取址
type person struct{
name string
age int
}
func newPerson(name string,age int)*person{
return &persion{
name:name,
age:age,
}
}
func (p *person)setAge(age int){
p.age = age
}
func (p person)getName()string{
return p.name
}
观察上述代码,可以看到,在使用setAge()方法时,其调用对象(既方法接收者)是一个person类型的指针变量,根据别的语言的习惯,使用指针修改对象属性值时,应该是(*p)->age = age ,但是在go语言中不需要我们手动解引用,编译时会自动解引用。
3.2 指针作为参数传递给函数
- 要记住一句话:go语言中函数的参数传递永远是值拷贝,无论所传的参数是值类型还是引用类型
- 当指针变量作为参数传递给函数时,实际上形参对实参进行了一份值拷贝,既会在内存中开辟一块新的空间,这块空间存放的是传入的指针变量的值(一个地址),在函数中通过该地址修改对应空间的变量值
- 我们可以通过以下代码来理解指针变量作为函数参数的过程
func f1(a *int){ fmt.Printf("在函数f1中形参的地址为:%p\n",&a) //输出:在函数f1中形参的地址为:0xc00012c020 fmt.Printf("在函数f1中行参的值为:%v\n",a) //输出:在函数f1中行参的值为:0xc000128010 b := a fmt.Printf("在函数f1中临时变量b的地址为:%p\n",&b) //输出:在函数f1中临时变量b的地址为:0xc00012c028 fmt.Printf("在函数f1中临时变量b的值为:%v\n",b) //输出:在函数f1中临时变量b的值为:0xc000128010 } func f2(a int){ fmt.Printf("在函数f2中形参的地址为:%p\n",&a) //输出:在函数f2中形参的地址为:0xc000128018 fmt.Printf("在函数f2中行参的值为:%v\n",a) //输出:在函数f2中行参的值为:5 b := &a fmt.Printf("在函数f2中临时变量b的地址为:%p\n",&b) //输出:在函数f2中临时变量b的地址为:0xc00012c030 fmt.Printf("在函数f2中临时变量b的值为:%v\n",b) //输出:在函数f2中临时变量b的值为:0xc000128018 } func main(){ a := 5 fmt.Printf("实参的地址为:%p\n",&a) // 输出:实参的地址为:0xc000128010 f1(&a) f2(a) }
- 上述代码图示如下
- f1()指针传参过程
- f2()变量传参过程
三、map类型
1.map的声明和初始化
var m1 map[string]int // 这样声明必须初始化,不能直接使用,否则会报错:panic: assignment to entry in nil map
fmt.Println(m1 == nil) // 未初始化默认是不分配空间的
m2 := make(map[string]int,1)// 使用make初始化map,可以指定容量,map会自动扩容,但消耗时间成本,所以最好提前确定需要的容量
注意:
-
map作为参数传入函数时代价极小,32位机器中只占4个字节,64位机器占用8个字节,无论该map中存储了什么类型什么数量的数据。
- map中的key值并不是任意的,key值可以是int,string等常用类型,也可以是接口,但不能是切片,数组和结构体。
-
map是以key-value对的形式存储数据,查找起来十分方便,但仍然比按索引查找的切片慢100倍(出自《The Way To Go》),因此如果对性能要求很大,应尽量使用切片来作为数据缓存区。
2.map的使用
m2["id"] = 1
m2["age"] = 18
3.map元素的遍历
// map的遍历
for key,value := range m2{ // 注意,for-range结构遍历map是乱序的,并非有顺序的遍历
fmt.Println(key,":",value)
}
// 检测map中是否存在某个key值
if value,ok := m2["haha"];!ok{ //如果存在key,ok为true,value为key对应的value,否则ok为false,value为对应类型0值
fmt.Println("no key haha",value)
}
4.map元素的删除
// 删除map的key-value对
delete(m2,"age") // 使用内置函数delete可以删除map中对应的key-value对
for key,value := range m2{
fmt.Println(key,":",value)
}
5.map排序
- 前面提到,数据存储在map中是无序的,因此使用for-range结构对map进行遍历,遍历的结果也是无序的,要想将map中的数据有序输出,我们就必须借助切片。
/ map的排序 // 标准库中没有实现map排序的方法,想要实现排序,可以将key(或者value)复制到切片中使用sort包进行排序 // 下面举例将map按key值(字符串类型)排序 m3 := make(map[string]int,5) for i:=0;i<5;i++{ name := "stu" + strconv.Itoa(i) m3[name] = 5-i-1 } s1 := make([]string,len(m3)) for key,_ := range m3{ s1 = append(s1,key) } sort.Strings(s1) // 有关sort包的使用会在后面接口章节详细介绍 for _,v := range s1{ if value,ok := m3[v];ok{ fmt.Printf("key:%v,value:%v\n",v,value) } }
总结
以上就是有关go语言中指针与map结构的基本内容,go语言中的指针只有取址和解引两种基本操作,不支持算数运算(既++,--),map结构存放的是key-value队,且存放顺序是无序的,常用if-ok的形式来判断map中是否含有某个key值。