教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang 切片 接口_Golang语言常用关键字之 make 和 new

golang 切片 接口_Golang语言常用关键字之 make 和 new

发布时间:2022-02-07   编辑:jiaochengji.com
教程集为您提供golang 切片 接口,Golang语言常用关键字之 make 和 new等资源,欢迎您收藏本站,我们将为您提供最新的golang 切片 接口,Golang语言常用关键字之 make 和 new资源

上一章中对于golang的语言基础说明如下:

<ul><li>1 函数调用</li><li>2 接口</li><li>3 反射</li></ul>

接下来我们来对golang的常用关键字进行说明,主要内容有:

<ul><li>1. for 和 range</li><li>2. select</li><li>3. defer</li><li>4. panic 和 recover</li><li>5. make 和 new</li></ul>

— — — — — — — — — — — — — — — — — — — — — — — — — — — —

当我们想要在 Go 语言中初始化一个结构时,可能会用到两个不同的关键字 — <code>make</code> 和 <code>new</code>。因为它们的功能相似,所以初学者可能会对这两个关键字的作用感到困惑1,但是它们两者能够初始化的却有较大的不同。

<ul><li><code>make</code> 的作用是初始化内置的数据结构,也就是我们在前面提到的切片、哈希表和 Channel2;</li><li><code>new</code> 的作用是根据传入的类型分配一片内存空间并返回指向这片内存空间的指针3;</li></ul>

我们在代码中往往都会使用如下所示的语句初始化这三类基本类型,这三个语句分别返回了不同类型的数据结构:

<pre class="has"><code>slice := make([]int, 0, 100) hash := make(map[int]bool, 10) ch := make(chan int, 5)</code></pre>
<ol><li><code>slice</code> 是一个包含 <code>data</code>、<code>cap</code> 和 <code>len</code> 的私有结构体 <code>internal/reflectlite.sliceHeader</code>;</li><li><code>hash</code> 是一个指向 <code>runtime.hmap</code> 结构体的指针;</li><li><code>ch</code> 是一个指向 <code>runtime.hchan</code> 结构体的指针;</li></ol>

相比与复杂的 <code>make</code> 关键字,<code>new</code> 的功能就很简单了,它只能接收一个类型作为参数然后返回一个指向该类型的指针:

<pre class="has"><code>i := new(int) var v int i := &v</code></pre>

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

<figcaption> 图 - make 和 new 初始化的类型 </figcaption>

接下来我们将分别介绍 <code>make</code> 和 <code>new</code> 在初始化不同数据结构时的过程,我们会从编译期间和运行时两个不同阶段理解这两个关键字的原理,不过由于前面的章节已经详细地分析过 <code>make</code> 的原理,所以这里会将重点放在另一个关键字 <code>new</code> 上。

<h2>1. make</h2>

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

<figcaption> 图 - make 关键字的类型检查 </figcaption>

在编译期间的类型检查阶段,Go 语言就将代表 <code>make</code> 关键字的 <code>OMAKE</code> 节点根据参数类型的不同转换成了 <code>OMAKESLICE</code>、<code>OMAKEMAP</code> 和 <code>OMAKECHAN</code> 三种不同类型的节点,这些节点会调用不同的运行时函数来初始化相应的数据结构。

<h2>2. new</h2>

编译器会在中间代码生成阶段通过以下两个函数处理该关键字:

<ol><li><code>cmd/compile/internal/gc.callnew</code> 函数会将关键字转换成 <code>ONEWOBJ</code> 类型的节点2;</li><li><code>cmd/compile/internal/gc.state.expr</code> 函数会根据申请空间的大小分两种情况处理: <ol><li>如果申请的空间为 0,就会返回一个表示空指针的 <code>zerobase</code> 变量;</li><li>在遇到其他情况时会将关键字转换成 <code>runtime.newobject</code> 函数:</li></ol></li></ol>
<pre class="has"><code>func callnew(t *types.Type) *Node { ... n := nod(ONEWOBJ, typename(t), nil) ... return n } func (s *state) expr(n *Node) *ssa.Value { switch n.Op { case ONEWOBJ: if n.Type.Elem().Size() == 0 { return s.newValue1A(ssa.OpAddr, n.Type, zerobaseSym, s.sb) } typ := s.expr(n.Left) vv := s.rtcall(newobject, true, []*types.Type{n.Type}, typ) return vv[0] } }</code></pre>

需要注意的是,无论是直接使用 <code>new</code>,还是使用 <code>var</code> 初始化变量,它们在编译器看来就是 <code>ONEWOBJ</code> 和 <code>ODCL</code> 节点。这些节点在这一阶段都会被 <code>cmd/compile/internal/gc.walkstmt</code> 转换成通过 <code>runtime.newobject</code> 函数在堆上申请内存:

<pre class="has"><code>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()) } } }</code></pre>

不过这也不是绝对的,如果通过 <code>var</code> 或者 <code>new</code> 创建的变量不需要在当前作用域外生存,例如不用作为返回值返回给调用方,那么就不需要初始化在堆上。

<code>runtime.newobject</code> 函数会是获取传入类型占用空间的大小,调用 <code>runtime.mallocgc</code> 在堆上申请一片内存空间并返回指向这片内存空间的指针:

<pre class="has"><code>func newobject(typ *_type) unsafe.Pointer { return mallocgc(typ.size, typ, true) }</code></pre>

<code>runtime.mallocgc</code> 函数的实现大概有 200 多行代码,我们会在后面的章节中详细分析 Go 语言的内存管理机制。

<h2>3. 小结</h2>

到了最后,简单总结一下 Go 语言中 <code>make</code> 和 <code>new</code> 关键字的实现原理,<code>make</code> 关键字的作用是创建切片、哈希表和 Channel 等内置的数据结构,而 <code>new</code> 的作用是为类型申请一片内存空间,并返回指向这片内存的指针。

全套教程点击下方链接直达:

IT实战:Go语言设计与实现自学教程​zhuanlan.zhihu.com
到此这篇关于“golang 切片 接口_Golang语言常用关键字之 make 和 new”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang 切片 接口_Golang语言常用关键字之 make 和 new
golang key map 所有_golang系列——高级语法之map
想系统学习GO语言(Golang
2020-10-18Go语言接口
golang key map 所有_golang之map
golang 接口_Golang介绍
切片 里面包含interface_Golang数据结构详解之切片
276-go语言golang面试题知识点
golang 接口_Golang之接口
golang 切片 接口_Golang简单入门教程——函数进阶篇

[关闭]
~ ~