登录
首页 >  Golang >  Go教程

Go语言中make和new有什么区别

来源:亿速云

时间:2023-03-02 10:00:55 399浏览 收藏

编程并不是一个机械性的工作,而是需要有思考,有创新的工作,语法是固定的,但解决问题的思路则是依靠人的思维,这就需要我们坚持学习和更新自己的知识。今天golang学习网就整理分享《Go语言中make和new有什么区别》,文章讲解的知识点主要包括make、new、go语言,如果你对Golang方面的知识点感兴趣,就不要错过golang学习网,在这可以对大家的知识积累有所帮助,助力开发能力的提升。

这篇文章主要介绍“Go语言中make和new有什么区别”,在日常操作中,相信很多人在Go语言中make和new有什么区别问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Go语言中make和new有什么区别”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    写在前面

    虽然 make 和 new  都是能够用于初始化数据结构,但是它们两者能够初始化的结构类型却有着较大的不同;make 在 Go 语言中只能用于初始化语言中的3种类型:slice、map、chan

    slice := make([]int, 0, 100)
    hash := make(map[int]bool, 10)
    ch := make(chan int, 5)

    这些基本类型都是语言为我们提供的,我们在前面的章节中其实已经介绍过了它们初始化的过程以及原理,但是在这里还是需要提醒各位读者注意的是,这三者返回了不同类型的数据结构:

    • slice 是一个包含 datacap 和 len 的结构体

    • hash 是一个指向 hmap 结构体的指针

    • ch 是一个指向 hchan 结构体的指针

    而另一个用于初始化数据结构的关键字 new 的作用其实就非常简单了,它只是接收一个类型作为参数然后返回一个指向这个类型的指针:

    i := new(int)
    
    var v int
    i := &v

    上述代码片段中的两种不同初始化方法其实是等价的,它们都会创建一个指向 int 零值的指针。

    到了这里我们对 Go 语言中这两种不同关键字的使用也有了一定的了解:make 用于创建切片、哈希表和管道等内置数据结构,new 用于分配并创建一个指向对应类型的指针。

    实现原理

    接下来我们将分别介绍 make 和 new 在初始化不同数据结构时的具体过程,我们会从编译期间和运行时两个不同的阶段理解这两个关键字的原理,不过由于前面已经详细地介绍过 make 的实现原理,所以我们会将重点放在 new 上从 Go 语言的源代码层面分析它的实现。

    make

    在前面的章节中我们其实已经谈到过 make 在创建 数组和切片、哈希表 和 Channel 的具体过程,所以在这一小节中,我们也只是会简单提及 make 相关的数据结构初始化原理。

    在编译期间的 类型检查 阶段,Go 语言其实就将代表 make 关键字的 OMAKE 节点根据参数类型的不同转换成了 OMAKESLICEOMAKEMAP 和 OMAKECHAN 三种不同类型的节点,这些节点最终也会调用不同的运行时函数来初始化数据结构。

    new

    内置函数 new 会在编译期间的 SSA 代码生成 阶段经过 callnew 函数的处理,如果请求创建的类型大小时 0,那么就会返回一个表示空指针的 zerobase 变量,在遇到其他情况时会将关键字转换成 newobject

    func callnew(t *types.Type) *Node {
        if t.NotInHeap() {
            yyerror("%v is go:notinheap; heap allocation disallowed", t)
        }
        dowidth(t)
    
        if t.Size() == 0 {
            z := newname(Runtimepkg.Lookup("zerobase"))
            z.SetClass(PEXTERN)
            z.Type = t
            return typecheck(nod(OADDR, z, nil), ctxExpr)
        }
    
        fn := syslook("newobject")
        fn = substArgTypes(fn, t)
        v := mkcall1(fn, types.NewPtr(t), nil, typename(t))
        v.SetNonNil(true)
        return v
    }

    需要提到的是,哪怕当前变量是使用 var 进行初始化,在这一阶段可能会被转换成 newobject 的函数调用并在堆上申请内存:

    func walkstmt(n *Node) *Node {
        switch n.Op {
        case ODCL:
            v := n.Left
            if v.Class() == PAUTOHEAP {
                if prealloc[v] == nil {
                    prealloc[v] = callnew(v.Type)
                }
                nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v])
                nn.SetColas(true)
                nn = typecheck(nn, ctxStmt)
                return walkstmt(nn)
            }
        case ONEW:
            if n.Esc == EscNone {
                r := temp(n.Type.Elem())
                r = nod(OAS, r, nil)
                r = typecheck(r, ctxStmt)
                init.Append(r)
                r = nod(OADDR, r.Left, nil)
                r = typecheck(r, ctxExpr)
                n = r
            } else {
                n = callnew(n.Type.Elem())
            }
        }
    }

    当然这也不是绝对的,如果当前声明的变量或者参数不需要在当前作用域外『生存』,那么其实就不会被初始化在堆上,而是会初始化在当前函数的栈中并随着 函数调用 的结束而被销毁。

    newobject 函数的工作就是获取传入类型的大小并调用 mallocgc 在堆上申请一片大小合适的内存空间并返回指向这片内存空间的指针:

    func newobject(typ *_type) unsafe.Pointer {
        return mallocgc(typ.size, typ, true)
    }

    mallocgc 函数的实现大概有 200 多行代码,在这一节中就不展开详细分析了,我们会在后面的章节中详细介绍 Go 语言的内存管理机制。

    到此,关于“Go语言中make和new有什么区别”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注golang学习网,小编会继续努力为大家带来更多实用的文章!

    今天关于《Go语言中make和new有什么区别》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!

    声明:本文转载于:亿速云 如有侵犯,请联系study_golang@163.com删除
    相关阅读
    更多>
    最新阅读
    更多>
    课程推荐
    更多>
    评论列表