GoLang学习笔记之基础语法(三):指针与Map

本文介绍了Go语言中的指针概念,包括指针变量和指针的使用,如取址和解引用。详细讲解了Go语言中指针作为参数传递给函数的特点。此外,还阐述了Map类型,包括声明、初始化、使用、遍历、删除和排序方法。文中强调了Map的无序性和使用切片进行排序的技巧。
摘要由CSDN通过智能技术生成

目录

前言

一、指针变量与指针

二、指针的使用

1.取址

2.解引获取值

3.指针的几个注意事项

3.1. go语言中的指针具有语法糖,会在相应场合自动解引用去取址 

3.2 指针作为参数传递给函数

三、map类型

1.map的声明和初始化

2.map的使用

3.map元素的遍历

4.map元素的删除

5.map排序

总结


前言

本章将介绍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值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值