• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

深度剖析Go语言数据结构

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

转载地址 : http://www.open-open.com/lib/view/open1390373069882.html

当向一个新程序员解释Go语言时,我发现如果解释Go的数据是如何在内存中表示的,将有助于建立编写高效程序的良好直觉。

基础类型

让我们从一些简单的例子开始:

深度剖析Go语言数据结构

变量i是int类型,在内存中占用一个32位的存储单位。(上图拿32位系统来举例;对以上的例子,只有指针才会在64位的机器上占用更多的空间——int始终是32位——然而我们仍然可以选择64位的系统。)

变量j是int32类型,因为它经过了显式的类型转化。尽管i和j有着同样的内存布局,但它们的类型是不一样的:像这样的赋值i = j会产生类型异常,必须通过显式的类型转换:i = int(j) 。

变量f是个浮点类型,上例中它代表着占用32位的浮点值。它的内存占用跟int32一样,但内部布局不同。

结构与指针

变量bytes是[5]byte类型,一个具有5个字节的数组。它的内存表示就只有5个紧挨着的字节,就像C里的数组一样。相似地,primes变量是一个拥有4个int数值的数组。

Go就像C而不像Java,它让程序员决定什么是或者不是一个指针。拿这个类型定义来举例:

1 type Point struct { X , Y int }
定义一个叫Point的简单的结构类型,意味着内存里是两个相邻的int。

深度剖析Go语言数据结构

Point{ 10, 20 }这句复合语法表示一个被初始化的Point对象。而&Point{ 10, 20 }这句则表示一个指向被初始化的Point对象的指针。前者在内存中有两个数据块,而后者则存放着一个指向两个数据块的指针。

结构中的字段被依次地排列在内存里面。

1	type Rect1 struct { Min, Max Point }
2	type Rect2 struct { Min, Max *Point }

Rect1是一个拥有两个Point类型字段的结构,它的一条记录包含了两条Point记录——共4个int。Rect2是一个拥有两个Point类型指针的结构,在内存里它占两个Point指针的空间。

用过C的程序员也许对Point字段和*Point字段的区别并不陌生,而只用过Java或者Python(或者其他)则可能为需要做出选择而惊讶。通过 为程序员提供控制基础内存布局的可能,Go语言让程序员可以操控所有数据结构总尺寸、所分配变量的总数和内存访问的模式,这些对于建造高性能系统都至关重 要。

字符串

接下来我们继续看一些更有趣的数据类型。


(灰色箭头意味着实现上的真实表示方式,但这在编程过程中是不可见的)

一个字符串在内存中的表示被分成两段,一个指向字符串数据的指针和一个长度值。因为字符串是可枚举的,所以多个字符串共享同一段存储空间也是安全的,因此 如果对s字符串进行一个切片选择,将得到一个可能不一样的指针和长度,但它们也指向同一段字节序列。这意味着,切片并不需要分配空间或者是复制数据,创建 切片很容易,只需要传递明确的下标值就行了。

(顺带一句,在Java和某些与严重有一个著名的缺陷,当你对一个字符串进行切片并保存其中的一小部分,引用将在内存中保存原字符串的完整内容,即使只有 很小的一部分是被用到的。Go也有这个缺陷。要不然(我们尝试但最终舍弃了),我们将对切片采取昂贵的做法——分配内存并拷贝数据——大部分语言都避免这 种做法。

切片

切片是对数组中一段数据的引用。在内存中它有三段数据组成:一个指向数据头的指针、切片的长度、切片的容量。长度是索引操作的上界,如:x[i] 。容量是切片操作的上界,如:x[i:j] 。

跟对字符串做切片一样,对数组进行切片也不会导致复制:它只创建一个存放指针、长度和容量的结构体。在这个例子中,语句[ ] int { 2, 3, 5, 7, 11 } 创建了一个包含5个值的新数组,并为x切片设置了对应的值来描述那个数组。切片表达式x[1:3]并不为数据分配内存:它只填充切片结构的字段,用以复用 数组的存储空间。在这个例子中,长度是2,y[0]和y[1]是仅有的合法数据;但容量是4,y[0:4]是个合法的切片表达式。(查看高效GO获取更多关于长度、容量,以及如何使用切片的信息。)

由于切片不是指针而是多字段的结构,切片操作并不需要分配内存,即使对于切片头也是这样,它可以常驻在栈中。这种表示法让切片的使用的代价很低,就像C中 传递精确的指针和长度一样。Go原生地在切片中使用了指针,这也意味着每个切片操作都分配一个内存对象。即使有了一个更快的内存分配器,这为垃圾回收带来 了不必要的工作,并且我们发现,就像字符串那个例子一样,给于精确的下标,比进行切片操作好。大多数情况下,避免不必要的间接引用和内存分配可以让切片足 够高效了。

new和make

Go有两种创建数据结构的方法:new和make 。它们的区别是常见的早期困惑,但很快就会变得自然。基础的区别在于,new(T)返回一个*T类型,一个可以被隐性反向引用的指针(如图中的黑色指 针),而make(T,args)返回一个原始的T,它并不是一个指针。T中常有写隐性的指针(如图中的灰色指针)。new返回一个指向初始化为全0值的 指针,而make返回一个复杂的结构。

有一种方式让两者统一起来,它对于传统的C和C++是一个重大的改变:定义make(*T)来返回一个指向新分配的T的指针,因此new(Point)和 make(*Point)的效果是一致的。我们用这种方法尝试了一段日子,但最终觉得这对于一些期待一个分配函数的人来说,实在太难以接受了。

中文原文地址:http://www.zingscript.com/post/195

英文原文地址:http://research.swtch.com/godata


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
[CF1495B]Let'sGoHiking-博弈发布时间:2022-07-10
下一篇:
触觉反馈装置近来频出,看来看去还是Go Touch VR的性价比最高发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap