go中指针相关操作记录

指针寻址总结

当我们使用一些方法时,需要传入变量的指针。go中对于指针的操作有相当明确的规定,不是任何变量和值都可以操作指针的

可寻址情况

  • 一个变量: &x
  • 指针引用(pointer indirection): &*x
  • slice 索引操作(不管 slice 是否可寻址): &s[1]
    因为 slice 底层实现了一个数组,它是可以寻址的
  • 可寻址 struct 的字段: &point.X
  • 可寻址数组的索引操作: &a[0]
  • composite literal 类型: &struct{ X int }{1}

不可寻址的情况

  • 字符串中的字节

  • map 对象中的元素
    如果对象不存在,则返回零值,零值是不可变对象,所以不能寻址,如果对象存在,因为 Go 中 map 实现中元素的地址是变化的,这意味着寻址的结果是无意义的

  • 接口对象的动态值(通过 type assertions 获得)

  • 常数
    如果可以寻址的话,我们可以通过指针修改常数的值,破坏了常数的定义。

  • literal 值(非 composite literal)

  • package 级别的函数

  • 方法 method(用作函数值)

切片寻址使用案例

  • 需求:

有一组不固定长度的数据。数据中的每个值都需要被传入一个方法,方法只接收指针变量。方法会根据传入顺序,修改数据的值,因为传入的是值的指针,原数据中对应的值也会发生变化。

因为原始数据的个数不是固定的,所以数组、结构体类型的变量都不能使用,切片和map可以满足需求。但是因为map中,不能针对每个值进行寻址,所以我们也无法使用,唯一可用的只有切片类型的变量

  • 具体使用场景:

mysql数据库读取操作,从数据库读取值后,需要把读取的值赋值一些变量后,才能使用,
原始用法如下:

func read() map[int64]map[string]string{
	rows,_ := mysql.Query("select id,name,img from user")
	data := make(map[int64]map[string]string)
	for rows.Next(){
		var id string
		var name string
		rows.Scan(&id,&name)
		dataIndex = strconv.ParseInt(id,10,64)
		sonData := map[string]string{"id":id,"name":name}
		data[id] = sonData
	}
}

原始用法,在方法中写死了查询的字段,我们需要能够动态控制返回值,方法才能具有通用行,修改后如下

/**
* table 数据表名
* field 需要返回的字段名,使用逗号拼接
* index 使用哪个字段作返回map的键值
*/
func read(table string,field string,index string) map[int64]map[string]string{
	rows,err := mysql.Query("select "+field+" from "+table)
	checkErr(err)
	data := make(map[int64]map[string]string)
	for rows.Next(){
		//拆分字段为字符串切片
		fieldSlice := strings.Split(field,",")
		//声明一个接口类型的切片,保存一个rows中的各字段值
		fieldData := make([]interface{},len(fieldSlice),len(fieldSlice))
		//设置一个callUserFuncArray用到的参数切片
		//其实该把fieldData 中各个值的指针,插入到该变量中,因mysql的Scan方法需要传入指针类型变量
		paramsSlice := make([]interface{},len(fieldSlice),len(fieldSlice))
		for key := range fieldSlice{
			paramsSlice[key] = &fieldData[key]
		}
		//callUserFuncArray方法为自定义方法,类似php中的call_user_func_array
		res,resErr := callUserFuncArray(rows,"Scan",paramsSlice)
		checkErr(resErr)
		resSlice := res.([]reflect.Value)
		if resSlice[0].Interface() == nil{
			sonData := make(map[string]string)
			for key2,val2 := range fieldSlice{
				//scan方法读取值到接口类型变量中,值实际类型是uint8。先把接口类型转为实际类型,再转为字符串
				sonData[val2] = string(fieldData[key2].([]uint8))
			}
			dataIndex,_ := strconv.ParseInt(sonData[index],10,64)
			data[dataIndex] = sonData
		}else{
			panic(resSlice[0].Interface().(error))
		}
	}
	return data
}

实现了接口的变量,作为参数传递,注意事项

//定义一个接口
type act interface {
    run()
}
//定义一个结构体
type people struct {
    name  string
    age int64
}
//结构体实现了方法run,也就是实现了接口act
func (p *people) run() {
    fmt.Printf("is fast",p.name)
}
func main() {
	//声明一个变量p,类型是people
    p := people{"Bill", 16}
    //调用run方法,可以有多种方式
    //传统方式,因为是people的引用类型实现了run方法,所以需要声明一个引用类型的变量
    p2 := &p
    p2.run()
    //简写,可以直接调用run。
    p.run()
}


以上代码是一个正常的接口定义和实现的过程,可以正常运行
但是当我们加入如下代码时,就报错了

//定义一个方法,参数为实现了act接口的变量
func send(a act) {
     a.run()
}
func main() {
	//声明一个变量p,类型是people
    p := people{"Bill", 16}
    //把变量p传入send方法
    send(p)
}

报错代码类似下图
在这里插入图片描述

意思是传入的变量没有实现接口的方法,可是我们之前的代码是可以正常运行的,为什么到了这里就不行了呢。

这里存在两种情况:

1、变量直接使用方法时

传入值是指针或者值,都可以。调用指针还是值,取决于方法定义时对接受者的声明
如下

func (alias_name reviser) A(){}//只会把接受者当做只读使用,不管传入的是指针还是值,因为指针会首先被解引用
func (alias_name *reviser) A(){}//只会把接受者当做指针,传入的是裸值也会自动转换为指针

2、变量实现了接口,以接口类型调用方法时

接收者是值的方法,可以通过值或者指针调用
接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址。

所以报错的代码修需改为

func main() {
	//声明一个变量p,类型是people
    p := people{"Bill", 16}
    //把变量p传入send方法
    send(&p)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值