Go 学习笔记(30)— Go 语言 make 和 new 的区别
Go
语言中 new
和 make
是两个内置函数,主要用来创建并分配类型的内存。在我们定义变量的时候,可能会觉得有点迷惑,不知道应该使用哪个函数来声明变量,其实他们的规则很简单:
new
只分配内存make
只能用于slice
、map
和channel
的初始化
1. new
在 Go
语言中, new
函数描述如下:
// 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
函数只接受一个参数,这个参数是一个类型,并且返回一个指向该类型内存地址的指针。同时 new
函数会把分配的内存置为零,也就是类型的零值。
new()
函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。
注意, Go
中的 new
关键字并不是声明变量,而是返回该类型的指针
a := new(int) //这时候 a 是一个*int 指针变量
使用 new
函数为变量分配内存空间。
var sum *int
sum = new(int) //分配空间
*sum = 98
fmt.Println(*sum)
当然, new
函数不仅仅能够为系统默认的数据类型,分配空间,自定义类型也可以使用 new
函数来分配空间,如下所示:
type Student struct {name stringage int
}var s *Student
s = new(Student) //分配空间
s.name ="dequan"fmt.Println(s)
这里如果我们不使用 new
函数为自定义类型分配空间(将第 7 行注释),就会报错:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x80bd277]
goroutine 1 [running]:
这就是 new
函数,它返回的永远是类型的指针,指针指向分配类型的内存地址。
使用示例:
package main// 导入系统包
import ("fmt"
)func main() {str := new(string)*str = "hello,world"value := new(int)*value = 10fmt.Println(*str) // hello,worldfmt.Println(*value) // 10}
new
函数用来创建某一个类型的指针型对象。理论上,一个数据类型,只要能够被访问,就可以使用 new
函数来创建这个类型的指针型对象。下边来看一段示例,使用 new
函数创建基本类型的指针型对象。
package main
import ("fmt"
)
type demo struct{}
func main() {var str = new(string)fmt.Println("string pointer", str)var num = new(int)fmt.Println("int pointer", num)var de = new(demo)fmt.Println("struct pointer", de)
}
在调用 new
函数时,将类型名作为参数即可创建出这个类型的指针型对象。
在 Golang
中存在几种特殊的情况。如使用 new
创建 chan
类型指针型对象,但是仍然需要调用 make
来给 chan
类型变量分配容量。
package main
import ("fmt""time"
)
func main() {// 创建通道类型指针型对象var ch = new(chan int)// 给对象分配容量,容量为1*ch = make(chan int)// 开启协程,从通道中读取数据go func() {fmt.Println(<-(*ch))}()// 向通道中写入数据*ch <- 8time.Sleep(time.Second * 1)
}
如果使用 new
创建通道类型变量后,不使用 make
来分配容量,当向通道中写入信息或从通道中读取信息时,将会出现下边错误信息:
... [chan send (nil chan)]:
... [chan receive (nil chan)]
虽然 new
关键字可以创建任何类型的指针型对象,但是由于某些特殊类型的对象被创建后,需要进行更多的初始化工作,所以 Golang
中引入了 make
函数,通过 make
来创建这些特殊类型的对象。这些特殊类型就是:切片类型、通道类型、字典类型。
另一个创建变量的方法是调用内建的 new
函数。表达式 new(T)
将创建一个 T
类型的匿名变量,初始化为 T
类型的零值,然后返回变量地址,返回的指针类型为*T
。
p := new(int) // p, *int 类型, 指向匿名的 int 变量
fmt.Println(*p) // "0"
*p = 2 // 设置 int 匿名变量的值为 2
fmt.Println(*p) // "2"
用 new
创建变量和普通变量声明语句方式创建变量没有什么区别,除了不需要声明一个临时变量的名字外,我们还可以在表达式中使用 new(T)
。换言之, new
函数类似是一种语法糖,而不是一个新的基础概念。
下面的两个 newInt
函数有着相同的行为:
func newInt() *int {return new(int)
}
func newInt() *int {var dummy intreturn &dummy
}
每次调用 new
函数都是返回一个新的变量的地址,因此下面两个地址是不同的:
p := new(int)
q := new(int)
fmt.Println(p == q) // "false"
当然也可能有特殊情况:如果两个类型都是空的,也就是说类型的大小是 0,例如struct{}
和[0]int
,有可能有相同的地址(依赖具体的语言实现)(译注:请谨慎使用大小为 0 的类型,因为如果类型的大小为 0 的话,可能导致 Go
语言的自动垃圾回收器有不同的行为,具体请查看runtime.SetFinalizer
函数相关文档)。
new
函数使用通常相对比较少,因为对于结构体来说,直接用字面量语法创建新变量的方法会更灵活。
由于 new
只是一个预定义的函数,它并不是一个关键字,因此我们可以将 new
名字重新定义为别的类型。例如下面的例子:
func delta(old, new int) int { return new - old }
由于 new
被定义为 int
类型的变量名,因此在 delta 函数内部是无法使用内置的 new
函数的。
2. make
make
也是用于内存分配的,但是和 new
不同,它只用于 chan
、 map
以及 slice
的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。
在 Go
语言中, make
函数的描述如下:
// 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, so make([]int, 0, 10) allocates a slice of length 0 and
// capacity 10.
// 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
通过上面的代码可以看出 make
函数的 t
参数必须是 chan
(通道)、 map
(字典)、 slice
(切片)中的一个,并且返回值也是类型本身。
make
用于给切片、通道、字典类型变量分配容量空间。给不同的类型变量分配空间时, make
参数不一样。下边来介绍一下不同类型使用 make
时的不同用法:
注意: make
函数只用于 map
, slice
和 channel
,并且不返回指针。如果想要获得一个显式的指针,可以使用 new
函数进行分配,或者显式地使用一个变量的地址。
使用 make
的好处是可以使用很短的代码初始化一个长度很大的值,示例如下:
ips := make([]string, 100)
上述代码表示初始化为一个元素类型为 string
且长度为 100 的切片值,用 make
函数初始化的切片值中的每一个元素值都会是其元素类型的零值,这里 ips 的 100 个元素都是空字符串 “”。
使用 make
给切片类型变量分配容量空间:
- 给字符串类型切片分配 4 个容量空间。
var arr = make([]string,4)
- 给字符串类型切片分配 10 个容量空间,且设置切片长度为 5,也就是设置切片前 5 个元素已经被使用,如果使用
append
向切片中追加元素,新追加的元素将会从第 6 个位置算起(下标为 5)。
var arr = make([]string,5,10)
- 使用
make
给通道类型变量分配容量空间,创建了一个不带缓冲的通道。
var ch = make(chan int)
- 使用
make
给通道类型变量分配容量空间,创建了一个带 10 个缓冲的通道。
var ch = make(chan int, 10)
- 使用
make
给字典类型变量分配容量空间
var mp = make(map[string]int)
给字典类型变量分配空间,不需要指定容量,如果在第二个参数中指定容量,这个容量值也会被忽略。如下边写法:
// 容量10被忽略
var mp = make(map[string]int,10)
虽然在给 map
类型变量分配容量时,指定了长度 10,但由于 map
类型变量能够装载的数据量与系统内存有关,所以给 map
类型变量设置容量的做法将会被忽略。
请牢记一点:make 函数只能初始化字典(map),切片(slice),通道(chan)类型。
Go
语言中的 new
和 make
主要区别如下:
make
只能用来分配及初始化类型为slice
、map
、chan
的数据。new
可以分配任意类型的数据;new
分配返回的是指针,即类型*Type
。make
返回引用,即Type
;new
分配的空间被清零。make
分配空间后,会进行初始化;
Go 学习笔记(30)— Go 语言 make 和 new 的区别相关推荐
- HaXe学习笔记:HaXe语言和其它编程语言的区别
以前发在百度空间里的,目前看来可能不够精确,仅供大家参考. ================================================================ 老实说,h ...
- 23 DesignPatterns学习笔记:C++语言实现 --- 2.2 Adapter
23 DesignPatterns学习笔记:C++语言实现 --- 2.2 Adapter 2016-07-22 (www.cnblogs.com/icmzn) 模式理解 1. Adapter 定义 ...
- 23 DesignPatterns学习笔记:C++语言实现 --- 1.2 AbstractFactory
23 DesignPatterns学习笔记:C++语言实现 --- 1.2 AbstractFactory 2016-07-21 (www.cnblogs.com/icmzn) 模式理解 1. F ...
- Java快速入门学习笔记9 | Java语言中的方法
有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...
- Java快速入门学习笔记8 | Java语言中的数组
有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...
- Java快速入门学习笔记7 | Java语言中的类与对象
有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...
- Java快速入门学习笔记3 | Java语言中的表达式与操作符
有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...
- Java快速入门学习笔记2 | Java语言中的基本类型
有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...
- [编译原理学习笔记2-2] 程序语言的语法描述
[编译原理学习笔记2-2] 程序语言的语法描述 文章目录 [编译原理学习笔记2-2] 程序语言的语法描述 [2.3.1] 上下文无关文法 [2.3.2] 语法分析树与二义性 [2.3.3] 形式语言鸟 ...
- 【学习笔记】C++语言程序设计(郑莉):多态性
[学习笔记]C++语言程序设计(郑莉):多态性 1. 多态性 2. 运算符重载 2.1 运算符重载的规则 2.2 运算符重载为成员函数 2.3 运算符重载为非成员函数 3. 虚函数 3.1 一般虚函数 ...
最新文章
- 去Tech Ed得计划好
- 小米快传文件服务器怎么用,手机中的小米快传怎么用?小米快传的详细使用教程...
- Word中项目符号和编号用法详解
- ABAP—COLLECT的用法
- 如何确定JTAG好坏?JTAG到底是什么?
- ssl1257-产生数【图论,最短路】
- SharePoint2010网站备份还原简单介绍
- 设计模式之单例模式的多重实现
- 特斯拉2020全年交付49.955万辆车,未能完成50万辆目标
- async python_Async Python 竟不比sync Python 快,怎么回事?
- shell按照时间排序_【经典排序】希尔排序
- linux 下 libpcap 简单使用
- Java--随机数和随机数种子(转)
- 原则与思维模型--《思维模型》0
- 清华EMBA课程系列思考之一 -- Techmark课程带给管理者的思考
- 小白一起学Android studio 创建项目
- 有趣又漂亮的可视化图表制作
- vue 动态渲染图片 不出来
- Open edX常见配置(fullstack)
- uni-app提交表单成功之后跳转首页
热门文章
- 2022-2028年中国康养地产行业市场需求前景及投资战略分析报告
- 2022-2028年中国高纯铜市场研究及前瞻分析报告
- java调用clang编译的so_写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实
- 判断某数组是不是二叉树的后序遍历序列 python递归与非递归解法
- Sping中利用HandlerExceptionResolver实现全局异常捕获
- RuntimeError: Expected object of backend CUDA but got backend CPU for argument
- Lidar激光雷达市场
- 适用于CUDA GPU的Numba 随机数生成
- 提高汽车系统的安全性
- 空间点像素索引(一)