Go 语言中 make 和new 的区别

Go 语言中 make 和new 的区别

声明一个变量

csharp 复制代码
var a int
var b string

这里我们使用 var 关键字,声明两个变量,然后就可以在程序中使用。当我们不指定变量的默认值的时候呢,这些变量的默认值是它所属类型的零值。比如上面的 int 型它的零值为 0,string 的零值为 "",引用类型的零值为 nil。

接下来尝试一下定义引用类型。

go 复制代码
package main

import "fmt"

func main() {
    var a *int
    *a = 10
    fmt.Println(*a)
}

执行一下这个程序,它会发生 panic,原因是:

go 复制代码
panic: runtime error: invalid memory address or nil pointer dereference

从这我们可以看出,如果是一个引用类型,我们不仅要声明它,还要为它分配内存空间,否则我们赋值的 10 就无处安放。值类型的声明不需要我们分配内存空间,因为已经默认给我们分配好啦。

new 关键字

我们使用 new 关键字来改写一下上面的 demo

go 复制代码
package main

import "fmt"

func main() {
    var a *int
    a = new(int)
    *a = 10
    fmt.Println(*a)
}

我们可以运行成功,并且成功打印出 10。下面贴出 new 内置函数的注释。

dart 复制代码
// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly\
// allocated zero value of that type.
func new(Type) *Type

简单翻译:此内置函数用于分配内存,第一个参数接收一个类型而不是一个值,函数返回一个指向该类型内存地址的指针,同时把分配的内存置为该类型的零值。

内置函数 new 会在编译期的,Go 语言编译器的中间代码具有静态单赋值(SSA)的特性,代码生成阶段经过 callnew 函数的处理,如果请求创建的类型大小是 0,那么就会返回一个表示空指针的 zerobase 变量,在遇到其他情况时会将关键字转换成 newobject

scss 复制代码
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 的函数调用并在堆上申请内存:

go 复制代码
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 在堆上申请一片大小合适的内存空间并返回指向这片内存空间的指针:

arduino 复制代码
func newobject(typ *_type) unsafe.Pointer {
    return mallocgc(typ.size, typ, true)
}

make 关键字

make 也是用于分配内存,与 new 不同的是,它一般只用于chan,map,slice 的初始化,并且直接返回这三种类型本身。以下是 make 内置函数的注释。

scss 复制代码
// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
//  Slice: The size specifies the length. The capacity of the slice is
//  equal to its length. A second integer argument may be provided to
//  specify a different capacity; it must be no smaller than the
//  length. For example, make([]int, 0, 10) allocates an underlying array
//  of size 10 and returns a slice of length 0 and capacity 10 that is
//  backed by this underlying array.
//  Map: An empty map is allocated with enough space to hold the
//  specified number of elements. The size may be omitted, in which case
//  a small starting size is allocated.
//  Channel: The channel's buffer is initialized with the specified
//  buffer capacity. If zero, or the size is omitted, the channel is
//  unbuffered.
func make(t Type, size ...IntegerType) Type

从声明也能看出,返回的还是该类型。

go 复制代码
new的语法是:func new(Type) *Type;
make的语法为:func make(t Type, size ...IntegerType) Type

两者异同

  • new 和 make 都是用于内存的分配。
  • make 只用于 chan,map,slice 的初始化。
  • new 用于给类型分配内存空间,并且置零。
  • make 返回类型本身,new 返回指向类型的指针。

参考资料

相关推荐
paopaokaka_luck3 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
码农小旋风5 小时前
详解K8S--声明式API
后端
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml45 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~5 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616885 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
睡觉谁叫~~~6 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
2401_865854889 小时前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
AskHarries9 小时前
Spring Boot集成Access DB实现数据导入和解析
java·spring boot·后端
2401_857622669 小时前
SpringBoot健身房管理:敏捷与自动化
spring boot·后端·自动化