我们一般使用*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.通常,使用指针分享结构体类型的值,除非那个结构体类型实现的是代表私有类型的值。
- 引用类型像数组切片,字典,管道,接口,和函数值,我们很少使用指针来分享这些值。
- 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))))