加入收藏 | 设为首页 | 会员中心 | 我要投稿 北几岛 (https://www.beijidao.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

go语言学习--指针的理解

发布时间:2021-07-06 06:13:52 所属栏目:大数据 来源: https://www.jb51.cc
导读:Go 的原生数据类型可以分为基本类型和高级类型,基本类型主要包含 string, bool, int 及 float 系列,高级类型包含 struct,array/slice,map,chan,func 。?相比 Java,Python,Javascript 等引用类型的语言,Golang 拥有类似C语言的指针这个相对古老的特
Go 的原生数据类型可以分为基本类型和高级类型,基本类型主要包含 string, bool, int 及 float 系列,高级类型包含 struct,array/slice,map,chan,func 。 ? 相比 Java,Python,Javascript 等引用类型的语言,Golang 拥有类似C语言的指针这个相对古老的特性。但不同于 C 语言,Golang 的指针是单独的类型,而不是 C 语言中的 int 类型,而且也不能对指针做整数运算。从这一点看,Golang 的指针基本就是一种引用。 ? 那么 Golang 为什么需要指针?这种指针又能有什么独特的用途呢? ? 在学习引用类型语言的时候,总是要先搞清楚,当给一个函数/方法传参的时候,传进去的是值还是引用。实际上,在大部分引用型语言里,参数为基本类型时,传进去的大都是值,也就是另外复制了一份参数到当前的函数调用栈。参数为高级类型时,传进去的基本都是引用。这个主要是因为虚拟机的内存管理导致的。 ? 内存管理中的内存区域一般包括 heap 和 stack, stack 主要用来存储当前调用栈用到的简单类型数据:string,boolean,int,float 等。这些类型的内存占用小,容易回收,基本上它们的值和指针占用的空间差不多,因此可以直接复制,GC也比较容易做针对性的优化。 复杂的高级类型占用的内存往往相对较大,存储在 heap 中,GC 回收频率相对较低,代价也较大,因此传引用/指针可以避免进行成本较高的复制操作,并且节省内存,提高程序运行效率。 ? 因此,在下列情况可以考虑使用指针:1,需要改变参数的值;2,避免复制操作;3,节省内存; ? 而在 Golang 中,具体到高级类型 struct,slice,map,也各有不同。实际上,只有 struct 的使用有点复杂,slice,map,chan 都可以直接使用,不用考虑是值还是指针。 ? ? struct: ? 对于函数(function),由函数的参数类型指定,传入的参数的类型不对会报错,例如: ?
func passValue(s struct){}
 
func passPointer(s *struct){}

?

对于方法(method),接收者(receiver)可以是指针,也可以是值,Golang 会在传递参数前自动适配以符合参数的类型。也就是:如果方法的参数是值,那么按照传值的方式 ,方法内部对struct的改动无法作用在外部的变量上,例如: ?

复制代码

package main
 
import "fmt"
 
type MyPoint struct {
    X int
    Y int
}
 
func printFuncValue(p MyPoint){
    p.X = 1
    p.Y = 1
    fmt.Printf(" -> %v",p)
}
 
func printFuncPointer(pp *MyPoint){
    pp.X = 1 // 实际上应该写做 (*pp).X,Golang 给了语法糖,减少了麻烦,但是也导致了 * 的不一致
    pp.Y = 1
    fmt.Printf(" -> %v",pp)
}
 
func (p MyPoint) printMethodValue(){
    p.X += 1
    p.Y += 1
    fmt.Printf(" -> %v",p)
}
 
// 建议使用指针作为方法(method:printMethodPointer)的接收者(receiver:*MyPoint),一是可以修改接收者的值,二是可以避免大对象的复制
func (pp *MyPoint) printMethodPointer(){
    pp.X += 1
    pp.Y += 1
    fmt.Printf(" -> %v",pp)
}
 
func main(){
    p := MyPoint{0,0}
    pp := &MyPoint{0,0}
 
    fmt.Printf("n value to func(value): %v",p)
    printFuncValue(p)
    fmt.Printf(" --> %v",p)
    // Output: value to func(value): {0 0} -> {1 1} --> {0 0}
 
    //printFuncValue(pp) // cannot use pp (type *MyPoint) as type MyPoint in argument to printFuncValue
 
    //printFuncPointer(p) // cannot use p (type MyPoint) as type *MyPoint in argument to printFuncPointer
 
    fmt.Printf("n pointer to func(pointer): %v",pp)
    printFuncPointer(pp)
    fmt.Printf(" --> %v",pp)
    // Output: pointer to func(pointer): &{0 0} -> &{1 1} --> &{1 1}
 
    fmt.Printf("n value to method(value): %v",p)
    p.printMethodValue()
    fmt.Printf(" --> %v",p)
    // Output: value to method(value): {0 0} -> {1 1} --> {0 0}
 
    fmt.Printf("n value to method(pointer): %v",p)
    p.printMethodPointer()
    fmt.Printf(" --> %v",p)
    // Output: value to method(pointer): {0 0} -> &{1 1} --> {1 1}
 
    fmt.Printf("n pointer to method(value): %v",pp)
    pp.printMethodValue()
    fmt.Printf(" --> %v",pp)
    // Output: pointer to method(value): &{1 1} -> {2 2} --> &{1 1}
 
    fmt.Printf("n pointer to method(pointer): %v",pp)
    pp.printMethodPointer()
    fmt.Printf(" --> %v",pp)
    // Output: pointer to method(pointer): &{1 1} -> &{2 2} --> &{2 2}
}

复制代码

?

slice : ? slice 实际上相当于对其依附的 array 的引用,它不存储数据,只是对 array 进行描述。因此,修改 slice 中的元素,改变会体现在 array 上,当然也会体现在该 array 的所有 slice 上。 可以使用 make([]int) 来创建并初始化 map 。 ? ? map : ? 使用 make(map[string]string) 返回的本身是个引用,可以直接用来操作: ?
map["name"]="Jason";

?

而如果使用 map 的指针,反而会产生错误:

*map["name"]="Jason"  //  invalid indirect of m["title"] (type string)
(*map)["name"]="Jason"  // invalid indirect of m (type map[string]string)

?

? chan : ? make(chan int) 返回的是可以直接使用的 channel 。 ? ? func : ? 在 Golang 中,func 可以作为一种值被返回,因此也可以使用类似 Python 的 decorator 的方式来加工函数。 ?

(编辑:北几岛)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读