GO 指针

一。测试实例:


package main

import (
       "fmt"
       "testing"
)

func Test_pointer_0(t *testing.T) {
       fmt.Println("Test_pointer>>>>>")

       var i int // i 的类型是int型
       i=1 // i 的值为 1;
       var p *int // p 的类型是指针且是 “int型的指针”
       p=&i         // p 的值为 “i的地址”

       /**
         i=1;p=826814784776;*p=1
        */
       fmt.Printf("i=%d;p=%d;*p=%d\n",i,p,*p)

       /**
         '*p' 表示解析指针p,即获取指针p指向的数据(即i的值),这行代码也就等价于 i = 2
          不能写成 p=2, 否则会编译失败,因为p的类型是 *int,是指针类型
        */
       *p=2
       /**
         i=2;p=826814784776;*p=2
        */
       fmt.Printf("i=%d;p=%d;*p=%d\n",i,p,*p)

       i=3
       /**
         i=3;p=826814784776;*p=3
        */
       fmt.Printf("i=%d;p=%d;*p=%d\n",i,p,*p)
}

type Integer int

//给Integer 类型新增方法ShowV_Integer,传入的参数是值而不是引用:即是函数调用者的拷贝,不会影响函数调用者
func (this Integer)ShowV_Integer() {
       this = 100
       fmt.Println("[ShowV] this=",this)
}

//给Integer 类型新增方法ModifyV_Integer,传入的参数是引用而不是值:即是函数调用者的引用,会影响函数调用者
func (this *Integer)ModifyV_Integer() {
       // 不能写成:this += 1,否则会编译失败
       *this += 1
       fmt.Println("[ModifyV] this=",this)
       fmt.Println("[ModifyV] *this=",*this)
}

func Test_pointer_1(t *testing.T) {
       fmt.Println("Test_pointer_1>>>>>")

       var a Integer = 1

       //输出结果:[ShowV] this=100;
       a.ShowV_Integer()

       /**
              输出结果:
              [ModifyV] this= 0xc082006508;
              [ModifyV] *this=2;
        */
       a.ModifyV_Integer()

       //输出结果:this=2;
       fmt.Println("this=",a)

}


type TestObj struct {
       v int
}

/**
TestObj 类型新增方法ShowV_obj,传入的参数是值而不是引用:即是函数调用者的拷贝,不会影响函数调用者
调用者必须是TestObj的实例,而不是实例指针
 */
func (this TestObj)ShowV_obj() {

       fmt.Println("[ShowV] this=",this)
       this.v = 100
       fmt.Println("[ShowV] this.v=",this.v)
}

/**
       给TestObj 类型新增方法ModifyV_obj,传入的参数是引用而不是值:即是函数调用者的引用,会影响函数调用者
       调用者必须是TestObj的实例,而不是实例指针!虽然参数是指针形式,可还是要用实例调用,只是调用时会将调用者(实例)的指针传递进去
 */
func (this *TestObj)ModifyV_obj() {

       //对这种对象类型的调用者引用,可以采用this.v 和 (*this).v 两种方式获取值。
       this.v += 1
       (*this).v += 1
       fmt.Println("[ModifyV] this=",this)
       fmt.Println("[ModifyV] this.v=",this.v)
       fmt.Println("[ModifyV] (*this).v=",(*this).v)
}

func Test_pointer_2(t *testing.T) {
       fmt.Println("Test_pointer_2>>>>>")

       //不能写成:var a TestObj = &TestObj{},否则会编译错误,
       var a TestObj = TestObj{}

       /**
         输出结果:
         [ShowV] this= {0}
         [ShowV] this.v=100;
        */
       a.ShowV_obj()
       /**
       输出结果:
       [ModifyV] this= &{2}
       [ModifyV] this.v= 2
       [ModifyV] (*this).v= 2

       注意:对象类型的引用(指针) 是 这种形式:&{2}
        */
       a.ModifyV_obj()
       //this=2;
       fmt.Println("this=",a)

}


func Test_pointer_3(t *testing.T) {
       var a int = 1
       var b  = &a    //b 是*int 类型,b的值是a的内存地址
       /**
          b是指针(引用),*b 的意思是获取b所引用的内存数据(b的值这个内存地址所在的数据,也就是a),即获取相当于 a = 2
          不能采用 b = 2,因为b 的指针类型是*int,而不是对象类型指针。对象类型的指针可以采用 p.key 或者(*p).key的形式获取值。
        */
       *b = 2
       /**
         输出结果:
         a: 2 ,b: 0xc082006508 ,*b: 2
        */
       fmt.Println("a:",a,",b:",b,",*b:",*b)
       var c [3]int = [3]int{1,2,3}
       /**
          d 是指针,类型是 *[3]int
        */
       var d = &c

       /**
              d[0] = 2 效果与 (*d)[0] = 2 一样。
              错误写法:*d[0] = 2
        */
       d[0] = 2
       (*d)[1] = 3
       /**
       输出结果:
       c: [2 3 3] ,d: &[2 3 3] ,*d: [2 3 3]

        */
       fmt.Println("c:",c,",d:",d,",*d:",*d)

}

//o1只能接收对象实例
func Add_1(o1 TestObj,v int) {
       fmt.Println("o1:",o1)
       o1.v += v;
}

//o1只能接收指针
func Add_2(o1 *TestObj,v int) {
       fmt.Println("o1:",o1)
       o1.v += v;
}

func Test_pointer_4(t *testing.T) {
       var o1 = TestObj{}
       //输出结果:o1: {0}
       Add_1(o1,1)
       //输出结果:end Add_1,o1.v= 0
       fmt.Println("end Add_1,o1.v=",o1.v)
       /**
       Add_2(o1,1): 编译错误,因为Add_2第一个参数必须是指针
       输出结果o1: &{0}
        */
       Add_2(&o1,1)
       //输出结果:end Add_2,o1.v= 1
       fmt.Println("end Add_2,o1.v=",o1.v)

}




总结:

(1)给类型新增方法(函数)时,不管方法前的参数类型是指针还是类型实例,调用这都必须是类型实例,而不能是指针!若是方法前参数是指针形式,会将调用者(实例)的指针传递进去。如:

func (this *TestObj)ModifyV_obj() {。。。} ,调用者必须是TestObj的实例,而不是指针。

(2)定义函数时,若是函数的参数形式是指针,则调用时也必须传递指针,不能传递类型实例。如:

func Add_1(o1 TestObj,v int) {。。。},调用时,o1必须是TestObj的实例,而不能是实例指针。
func Add_2(o1 *TestObj,v int) {。。。},调用时,o1必须是TestObj的实例的指针,而不能是实例。



注意:

GO有四个比较特殊的数据类型:数组切片,map,channel,接口(interface)。这四个数据类型看起来像引用类型,因为要修改这四种类型的实例数据的话,不用传递实例指针,而是直接传递实例。先看一组例子:在GO内置的url.go中有以下这么一段:


// Values maps a string key to a list of values.
// It is typically used for query parameters and form values.
// Unlike in the http.Header map, the keys in a Values map
// are case-sensitive.
type Values map[string][]string

// Get gets the first value associated with the given key.
// If there are no values associated with the key, Get returns
// the empty string. To access multiple values, use the map
// directly.
func (v Values) Get(key string) string {
       if v == nil {
              return ""
       }
       vs, ok := v[key]
       if !ok || len(vs) == 0 {
              return ""
       }
       return vs[0]
}

// Set sets the key to value. It replaces any existing
// values.
func (v Values) Set(key, value string) {
       v[key] = []string{value}
}

// Add adds the value to key. It appends to any existing
// values associated with key.
func (v Values) Add(key, value string) {
       v[key] = append(v[key], value)
}

// Del deletes the values associated with key.
func (v Values) Del(key string) {
       delete(v, key)
}

// ParseQuery parses the URL-encoded query string and returns
// a map listing the values specified for each key.
// ParseQuery always returns a non-nil map containing all the
// valid query parameters found; err describes the first decoding error
// encountered, if any.
func ParseQuery(query string) (m Values, err error) {
       m = make(Values)
       err = parseQuery(m, query)
       return
}

func parseQuery(m Values, query string) (err error) {
       for query != "" {
              key := query
              if i := strings.IndexAny(key, "&;"); i >= 0 {
                     key, query = key[:i], key[i+1:]
              } else {
                     query = ""
              }
              if key == "" {
                     continue
              }
              value := ""
              if i := strings.Index(key, "="); i >= 0 {
                     key, value = key[:i], key[i+1:]
              }
              key, err1 := QueryUnescape(key)
              if err1 != nil {
                     if err == nil {
                            err = err1
                     }
                     continue
              }
              value, err1 = QueryUnescape(value)
              if err1 != nil {
                     if err == nil {
                            err = err1
                     }
                     continue
              }
              m[key] = append(m[key], value)
       }
       return err
}


我们发现,对map类型的 修改 如

func (v Values) Set(key, value string) {
       v[key] = []string{value}
}

传递的是 Values 类型,而不是 *Values。 那么,为什么这种方式会修改源数据呢? 因为 这四种数据其实内部都有一个指针指向源数据,操作的时候其实操作的是指针指向的源数据。 这样在传递参数的时候,虽然传递的是值,相当于复制数据,可是复制后的对象中也有指向源数据的指针。


而且 我们发现,GO内置的make() 函数,只接受创建 数组切片、map、channel 这三种类型实例,而且返回的是实例,而不像New()函数那样返回的是指针,因为这三种数据返回实例就好,实例经过各种传递 后,操作的最终也还是源数据。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值