Go 语言中 newmake 是两个内置函数,主要用来创建并分配类型的内存。在我们定义变量的时候,可能会觉得有点迷惑,不知道应该使用哪个函数来声明变量,其实他们的规则很简单:

  • new 只分配内存
  • make 只能用于 slicemapchannel 的初始化

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 不同,它只用于 chanmap 以及 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 函数只用于 mapslicechannel ,并且不返回指针。如果想要获得一个显式的指针,可以使用 new 函数进行分配,或者显式地使用一个变量的地址。

使用 make 的好处是可以使用很短的代码初始化一个长度很大的值,示例如下:

ips := make([]string, 100)

上述代码表示初始化为一个元素类型为 string 且长度为 100 的切片值,用 make 函数初始化的切片值中的每一个元素值都会是其元素类型的零值,这里 ips 的 100 个元素都是空字符串 “”。

使用 make 给切片类型变量分配容量空间:

  1. 给字符串类型切片分配 4 个容量空间。
var arr = make([]string,4)
  1. 给字符串类型切片分配 10 个容量空间,且设置切片长度为 5,也就是设置切片前 5 个元素已经被使用,如果使用 append 向切片中追加元素,新追加的元素将会从第 6 个位置算起(下标为 5)。
var arr = make([]string,5,10)
  1. 使用 make 给通道类型变量分配容量空间,创建了一个不带缓冲的通道。
var ch = make(chan int)
  1. 使用 make 给通道类型变量分配容量空间,创建了一个带 10 个缓冲的通道。
var ch = make(chan int, 10)
  1. 使用 make 给字典类型变量分配容量空间
var mp = make(map[string]int)

给字典类型变量分配空间,不需要指定容量,如果在第二个参数中指定容量,这个容量值也会被忽略。如下边写法:

// 容量10被忽略
var mp = make(map[string]int,10)

虽然在给 map 类型变量分配容量时,指定了长度 10,但由于 map 类型变量能够装载的数据量与系统内存有关,所以给 map 类型变量设置容量的做法将会被忽略。

请牢记一点:make 函数只能初始化字典(map),切片(slice),通道(chan)类型。

Go 语言中的 newmake 主要区别如下:

  • make 只能用来分配及初始化类型为 slicemapchan 的数据。 new 可以分配任意类型的数据;
  • new 分配返回的是指针,即类型 *Typemake 返回引用,即 Type
  • new 分配的空间被清零。 make 分配空间后,会进行初始化;

Go 学习笔记(30)— Go 语言 make 和 new 的区别相关推荐

  1. HaXe学习笔记:HaXe语言和其它编程语言的区别

    以前发在百度空间里的,目前看来可能不够精确,仅供大家参考. ================================================================ 老实说,h ...

  2. 23 DesignPatterns学习笔记:C++语言实现 --- 2.2 Adapter

    23 DesignPatterns学习笔记:C++语言实现 --- 2.2 Adapter 2016-07-22 (www.cnblogs.com/icmzn) 模式理解 1. Adapter 定义 ...

  3. 23 DesignPatterns学习笔记:C++语言实现 --- 1.2 AbstractFactory

    23 DesignPatterns学习笔记:C++语言实现 --- 1.2 AbstractFactory 2016-07-21 (www.cnblogs.com/icmzn) 模式理解   1. F ...

  4. Java快速入门学习笔记9 | Java语言中的方法

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  5. Java快速入门学习笔记8 | Java语言中的数组

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  6. Java快速入门学习笔记7 | Java语言中的类与对象

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  7. Java快速入门学习笔记3 | Java语言中的表达式与操作符

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  8. Java快速入门学习笔记2 | Java语言中的基本类型

    有人相爱,有人夜里开车看海,有人却连LeetCode第一题都解不出来!虽然之前系统地学习过java课程,但是到现在一年多没有碰过Java的代码,遇到LeetCode不知是喜是悲,思来想去,然后清空自己 ...

  9. [编译原理学习笔记2-2] 程序语言的语法描述

    [编译原理学习笔记2-2] 程序语言的语法描述 文章目录 [编译原理学习笔记2-2] 程序语言的语法描述 [2.3.1] 上下文无关文法 [2.3.2] 语法分析树与二义性 [2.3.3] 形式语言鸟 ...

  10. 【学习笔记】C++语言程序设计(郑莉):多态性

    [学习笔记]C++语言程序设计(郑莉):多态性 1. 多态性 2. 运算符重载 2.1 运算符重载的规则 2.2 运算符重载为成员函数 2.3 运算符重载为非成员函数 3. 虚函数 3.1 一般虚函数 ...

最新文章

  1. 去Tech Ed得计划好
  2. 小米快传文件服务器怎么用,手机中的小米快传怎么用?小米快传的详细使用教程...
  3. Word中项目符号和编号用法详解
  4. ABAP—COLLECT的用法
  5. 如何确定JTAG好坏?JTAG到底是什么?
  6. ssl1257-产生数【图论,最短路】
  7. SharePoint2010网站备份还原简单介绍
  8. 设计模式之单例模式的多重实现
  9. 特斯拉2020全年交付49.955万辆车,未能完成50万辆目标
  10. async python_Async Python 竟不比sync Python 快,怎么回事?
  11. shell按照时间排序_【经典排序】希尔排序
  12. linux 下 libpcap 简单使用
  13. Java--随机数和随机数种子(转)
  14. 原则与思维模型--《思维模型》0
  15. 清华EMBA课程系列思考之一 -- Techmark课程带给管理者的思考
  16. 小白一起学Android studio 创建项目
  17. 有趣又漂亮的可视化图表制作
  18. vue 动态渲染图片 不出来
  19. Open edX常见配置(fullstack)
  20. uni-app提交表单成功之后跳转首页

热门文章

  1. 2022-2028年中国康养地产行业市场需求前景及投资战略分析报告
  2. 2022-2028年中国高纯铜市场研究及前瞻分析报告
  3. java调用clang编译的so_写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实
  4. 判断某数组是不是二叉树的后序遍历序列 python递归与非递归解法
  5. Sping中利用HandlerExceptionResolver实现全局异常捕获
  6. RuntimeError: Expected object of backend CUDA but got backend CPU for argument
  7. Lidar激光雷达市场
  8. 适用于CUDA GPU的Numba 随机数生成
  9. 提高汽车系统的安全性
  10. 空间点像素索引(一)