一。测试实例:
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()函数那样返回的是指针,因为这三种数据返回实例就好,实例经过各种传递 后,操作的最终也还是源数据。