Go语言学习11-数据初始化

简介: 【5月更文挑战第3天】本篇带大家通过内建函数 new 和 make 了解Go语言的数据初始化过程

image.png

引言

书接上篇,我们了解了 Go 语言的指针类型。那到目前为止,Go 的数据类型就差不多介绍完了,下面就是讲解如何地对 Go 数据的初始化了。

主要内容

这里的数据初始化是指对某个数据类型的值或变量的初始化。我们知道,在 Go 语言中,几乎所有的数据类型的值都可以使用字面量来进行表示和初始化。在大多数情况下,使用字面量就能满足初始化值或变量的要求。

除了上面说到的字面量的初始化方式,Go 语言还提供了两个专门用于数据初始化的内建函数 newmake

1. 内建函数 new

new 函数用于为值分配内存。与其他编程语言(如 Java)中的 new 不同的是,它并不会去初始化分配到的内存,而只会清零它。因此调用表达式 new(T) 被求值时,所做的是为 T 类型的新值分配并清零一块内存空间,然后将这块内存空间的地址作为结果返回。而这个结果就是指向这个新的 T 类型值的指针值。它的类型为 *T。实际上这个 new 函数返回的 *T 类型值总会指向一个 T 类型的零值。

例如,调用表达式 new(string) 的求值结果指向的是一个 string 类型的零值 "",而调用表达式 new([3]int) 的求值结果指向的则是一个 [3]int 类型的零值 [3]int{0, 0, 0}

正因为有这种干净的内存分配策略,使得我们可以在用内建函数 new 创建某个数据类型的新值之后立刻就可以拿来使用,而不用担心在这个值中会遗留某些初始化的痕迹。

下面我们以标准库代码包 bytes 中的结构体类型 Buffer 为例介绍以下:

bytes.Buffer 是一个尺寸可变的字节缓冲区。它的零值就是一个立即可用的空缓冲区。因此,调用表达式 new(bytes.Buffer) 的求值结果就是一个指向一个空缓冲区的指针值。然后,我们就可以立即在这个缓冲区上进行读写操作了。

标准库代码包 sync 中的结构体类型 Mutex 也是一个可以 new 后即用的数据类型。它的零值就是一个处于未锁定状态的互斥量。

内建函数 new 的这种特性为我们提供了一个关于自定义数据类型的可参考的设计规则:

例如,在我们自定义一个结构体类型的时候就要考虑到,在其中的每个字段的值都分别为对应类型的零值的时候,这个结构体值就应该已经处于可用的状态。这样,我们在 new 它的时候就能够得到一个立即可用的值的指针值,而不需要再做额外的初始化。

当然,在感觉一个类型的零值还无法让它变得可用的时候,我们可以使用相应的字面量来达到分配内存空间并初始化值的目的。

前面我们已经讲过,我们可以在字面量中灵活的指定新值中的每一个元素的值。但是要注意,字面量所代表的是该类型的值,而不是指向该类型值的指针值。 因此,我们在将它们与调用 new 函数的调用表达式做等价替换的时候,还需要在字面量的前面加入取址操作符 & 以表示指向该类型值的指针值。

2. 内建函数 make

new 不同的是,make 函数只能被用于创建 切片类型字典类型通道类型 的值,并返回一个已被初始化的(即非零值的)的对应类型的值。

那 make 函数为啥要这样做呢?

在之前的博文中我们介绍过,这 3 个复合类型都是引用类型。在它们的每一个值的内部都会保持着一个对某个底层数据结构值的引用。如果不对它们的值进行初始化,那么其中的这种引用关系是不会被建立起来的,同时相关的内部值也会不正确。在这种情况下,该类型的值也就不能够被使用,因为它们是不完整的,还处于未就绪的状态。这就意味着,在创建这 3 个引用类型的值的时候,必须将内存空间分配和数据初始化这两个步骤绑定在一起。也正是为了保证这些值的可用性,切片类型字典类型通道类型的零值都是 nil,不是那个未被初始化的值。因此,当我们 new3 个引用类型并想创建它们的值的时候,得到的却是一个指向空值 nil 的指针值。

除此之外,内建函数 make 所接受的参数也与 new 函数有所不同。make 函数除了会接受一个表示目标类型的类型字面量之外,还会接受一个或两个额外的参数。

对于 切片类型 来说,我们可以在把新值的长度和容量也传递给 make 函数。例如:

// 调用表达式创建了一个新的 []int 类型的值,这个值的长度为10、容量为100
make([]int, 10, 100)

当然,我们也可以省略掉最后一个参数,即不指定新值的容量。这种情况下,该值的容量会与其长度一致。示例如下:

// 变量s的类型是[]int的,而长度和容量都是10。
s := make([]int,10)

在使用 make 函数初始化一个切片值的过程中,该值会引用一个长度与其容量相同且元素类型与其元素类型一致的数组值。这个数组值就是该切片值的底层数组。该数组值中的每个元素都是当前元素类型的零值。但是,切片值只会展现出数量与其长度相同的元素。因此,调用表达式 make([]int,10,100) 所创建并初始化的值就是 []int{0 0 0 0 0 0 0 0 0 0}

我们在使用 make 函数创建字典类型的值的时候,也可以指定其底层数据结构的长度。但是,该字典值只会展示出我们明确放入的键值对。例如:

// 调用如下表达式,所创建和初始化的值会是 map[string]int
make(map[string]int,100)

// 也可以忽略掉那个用于表示底层数据结构长度的参数
make(map[ string]int)

虽然我们也可以忽略掉那个用于表示底层数据结构长度的参数 ,但是还是建议:

应该在性能敏感的应用场景下,根据这个字典值可能包含的键值对的数量以及放入它们的时间,仔细地设置该长度参数。

最后我们简单介绍以下对于 通道类型(Channel) 的值的数据初始化。到此目前为止,我们还没有对通道类型进行过任何说明。不过,在后续的博文中,我们会详细讲解这个特殊的引用类型。

// 使用make函数创建一个通道类型的值
make(chan int, 10)

其中的第一个参数表示的是 通道的类型,而第二个参数则表示该 通道的长度。与字典类型相同,第二个参数也可以被忽略掉。不过忽略它的含义却与针对字典类型的情况有着很大的不同,这些我们会在后面的博文中专门讲解。

注意: make 函数只能被应用在引用类型的值的创建上。并且,它的结果是第一个参数所代表的类型的值,而不是指向这个值的指针值。

如果我们想要获得该指针值的话,只能在调用 make 函数的表达式的求值结果之上应用取址操作符 & ,像这样:

m := make(map[string]int, 100)
mp := &m

到目前为止,我们已经介绍了 3 种创建值的的方法:

  • 使用 字面量
  • 调用内建函数 new
  • 调用内建函数 make

它们适用于不同的应用场景。当然,在某些应用场景中、我们可以有多种选择:

  • 在创建一个切片类型的值的时候,我们既可以使用字面量也可以使用 make 函数。这种选择的结果往往取决于我们是否需要定制切片值中的某个或某些元素值。

  • 如果我们能够保证一个结构体类型的值在其中字段的值均为零值的情况下就能够处于可用状态的话,那么仅使用 new 函数来初始化它与使用字面量进行初始化是基本等价的。不过要注意,这两种方法产生的结果的类型是不同的。

总结

  • 字面量可以被用于初始化几乎所有的Go语言数据类型的值,除了接口类型和通道类型。接口类型没有值,而通道类型的值只能使用 make 函数来创建。如果需要指向值的指针值,那么可以在表示该值的字面量之上进行取址操作。
  • 内建函数 new 主要被用于创建值类型的值。调用 new 函数的表达式的结果值将会是指向被创建值的指针值,并且被创建值会是其所属数据类型的零值。因此,new 函数不适合被用来创建引用类型的值。最直接的原因是引用类型的值的零值都是 nil,是不可用的。
  • 内建函数 make 仅能被用于某些引用类型(切片类型字典类型通道类型)的值的创建。它在创建值之后还会对其进行必要的初始化。与 new 函数不同.调用 make 函数的表达式的结果值将会是被创建的值本身,而不是指向它的指针值。
目录
相关文章
|
4月前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
4月前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
28天前
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
43 10
|
4月前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
75 3
|
4月前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
4月前
|
SQL 安全 Java
阿里双十一背后的Go语言实践:百万QPS网关的设计与实现
解析阿里核心网关如何利用Go协程池、RingBuffer、零拷贝技术支撑亿级流量。 重点分享: ① 如何用gRPC拦截器实现熔断限流; ② Sync.Map在高并发读写中的取舍。
161 1
|
4月前
|
存储 算法 安全
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
68 0
|
6月前
|
开发框架 Go 计算机视觉
纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架
开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C++ 库,如 OpenCV 或 dlib,但通过 cgo 调用 C 程序会引入巨大的延迟,并在性能方面产生显著的权衡。此外,在许多情况下,在各种平台上安装 OpenCV 是很麻烦的。使用纯Go开发的插件不仅在开发时方便,在项目部署和项目维护也能省很多时间精力。
165 5
|
7月前
|
Go 数据安全/隐私保护 开发者
Go语言开发
【10月更文挑战第26天】Go语言开发
96 3
|
7月前
|
Java 程序员 Go
Go语言的开发
【10月更文挑战第25天】Go语言的开发
88 3