golang 指针

我们一般使用*T作为一个指针类型,标识一个指向类型为T变量的指针,为了安全考虑,

  • 两个不同的指针类型不能转换,比如*int*int64,
  • 声明什么类型的指针,就赋值指向什么类型的。
    否则,会报诸如此类的错误:
cannot use &b (type *int64) as type *int in assignment
package main


import (
    "fmt"
)

func main() {
    type User struct {
        Id int
        Name string
    }
    var s = User{
        Id: 12,
        Name: "zhao",
    }
    //声明什么类型的指针,赋值什么类型的值的地址,否则无法完成赋值
    //var ain int = 2
    //var prt1 *User = &ain  //cannot use &ain (type *int) as type *User in assignment
    var prt *User = &s //声明一个User类型的指针,并赋值
    prt.Name = "zhang"
    fmt.Println(prt)

    var arr *[]int // 声明一个整形数组切片的指针,
    //arr = &[]string{"string","zhao"} //声明什么类型的数组切片,就赋值什么类型的,否则无法完成赋值
    arr = &[]int{1,2,2,3,4,4,4,5,5,56,6}
    fmt.Println(arr)
    var arrpt []*int //声明一个指针数组切片, 就是说数组的元素是指针,什么类型的指针,就明确类型,否则无法完成赋值
    var a, b int =  2, 3
    arrpt = []*int{&a} //&为取地址符合,只有地址可以赋值给指针变量。
    arrpt = append(arrpt, &b) // append 方法忘数组中追加元素。类型也是要明确的
    fmt.Println(arrpt)
}

注意:

  • 1 .一般情况下,不要通过指针分享内建类型的值.
  • 2.通常,使用指针分享结构体类型的值,除非那个结构体类型实现的是代表私有类型的值。
    1. 引用类型像数组切片,字典,管道,接口,和函数值,我们很少使用指针来分享这些值。
  • 4.通常,不要使用指针分享一个引用类型的值,除非你实现unMarshal类型的功能。

结构体指针

package main

import (
    "fmt"
)

type User struct {
    Id   int
    Name string
}

func (u User) displayId() {
    fmt.Println(u.Id)
}

func (u *User) displayName() {
    fmt.Println(u.Name)
}

func main() {
    us := User{Id: 1, Name: "zhao"}
    us.displayId()
    us.displayName()
    us2 := &User{Id: 2, Name: "qian"}
    us2.displayId()
    us2.displayName()
}

Output:

1
zhao
2
qian

可以看出,无论是结构体变量还是结构体指针变量,都是可以调用接受者不管是结构体还是结构体指针的方法。

传递给接口的时候会有所不同

package main

import (
    "fmt"
)

type DisplayInfo interface {
    displayId()
    displayName()
}

type User struct {
    Id   int
    Name string
}

func (u User) displayId() {
    fmt.Println(u.Id)
}

func (u *User) displayName() {
    fmt.Println(u.Name)
}


func DisplayUserInfo(ds DisplayInfo) {
    ds.displayId()
    ds.displayName()
}

func main() {
    us := User{Id: 1, Name: "zhao"}
    us.displayId()
    us.displayName()
    us2 := &User{Id: 2, Name: "qian"}
    us2.displayId()
    us2.displayName()

    us3 :=User{Id:3,Name:"sun"}
    DisplayUserInfo(us3)
}

Output:

# command-line-arguments
./stpter.go:40:17: cannot use us3 (type User) as type DisplayInfo in argument to DisplayUserInfo:
    User does not implement DisplayInfo (displayName method has pointer receiver)

Compilation finished with exit code 2

us3是有两个方法调用的,那为什么不能赋值给接口呢,因为us3是值类型,传给函数参数时是值拷贝,错误信息中说,User类型没有实现DisplayInfo接口原因是displayName方法接受者是指针。

package main

import (
    "fmt"
)

type DisplayInfo interface {
    displayId()
    changeName(name string)
    displayName()
}

type User struct {
    Id   int
    Name string
}

func (u User) displayId() {
    fmt.Println(u.Id)
}

func (u *User) displayName() {
    fmt.Println(u.Name)
}

func (u *User) changeName(name string) {
    u.Name = name
}

func DisplayUserInfo(ds DisplayInfo) {
    ds.displayId()
    ds.changeName("li")
    ds.displayName()
}

func main() {
    us := User{Id: 1, Name: "zhao"}
    us.displayId()
    us.displayName()
    us2 := &User{Id: 2, Name: "qian"}
    us2.displayId()
    us2.displayName()

    us3 := &User{Id: 3, Name: "sun"}
    us3.displayId()
    us3.displayName()
    DisplayUserInfo(us3)
}

Output:

1
zhao
2
qian
3
sun
3
li

这样就行了。为什么呢?
有人说:

In the case of pointer receiver, the pointer to the struct (*UserMutable in the example above)  implements the interface.
In the case of value receiver, the the struct itself (UserImmutable in the example above)  implements the interface.

意思是,接受者是指针类型的时候,说明指针指向的结构体实现了接口
接受者是值类型的时候,说明的是结构体本身实现了接口.

官方文档中:

The method set of any other named type T consists of all methods with receiver type T.
The method set of the corresponding pointer type *T is the set of 
all methods with receiver *T or T (that is, it also contains the method set of T).
意思是,接受者是T的属于一个方法集,接受者是*T的是另一个方法集,该方法及包含接受者是*T和T的。

上面说两个不同类型的指针之间不能相互转化,但确实需要转化的时候,还是可以做到的,使用unsafe.Pointer

// ArbitraryType is here for the purposes of documentation only and is not actually
// part of the unsafe package. It represents the type of an arbitrary Go expression.
type ArbitraryType int

// Pointer represents a pointer to an arbitrary type. There are four special operations
// available for type Pointer that are not available for other types:
//  - A pointer value of any type can be converted to a Pointer.
//  - A Pointer can be converted to a pointer value of any type.
//  - A uintptr can be converted to a Pointer.
//  - A Pointer can be converted to a uintptr.
// Pointer therefore allows a program to defeat the type system and read and write
// arbitrary memory. It should be used with extreme care.
type Pointer *ArbitraryType

1. Conversion of a *T1 to Pointer to *T2.

package main

import (
    "fmt"
    "unsafe"
    "reflect"
)


func main(){
    //var a *int
    var b int64 = 1
    bpointer := unsafe.Pointer(&b)
    fmt.Println(reflect.TypeOf(bpointer))  // unsafe.Pointer
    fmt.Println(*(*int)(bpointer)) // 1
}

2. Conversion of a Pointer to a uintptr (but not back to Pointer).

buintptr := uintptr(bpointer)
fmt.Println(reflect.TypeOf(buintptr))  // uintptr

3.Conversion of a Pointer to a uintptr and back, with arithmetic.

    us := &User{Name: "zhangsan", Mobile:"18012345678"}
    usPointer:= unsafe.Pointer(us)
    fmt.Println(*(*string)(unsafe.Pointer(uintptr(usPointer) + unsafe.Offsetof(us.Mobile)))) // 18012345678

与C不同,将指针推进到原始分配的末尾是无效的:

    var s string  = "zhang"
    fmt.Println(*(*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s))))

4. Conversion of a Pointer to a uintptr when calling syscall.Syscall.

5.Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr from uintptr to Pointer.

6.Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer.

    原文作者:Jeason_zhao
    原文地址: https://www.jianshu.com/p/fa72db605e55
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞