在Go语言中,有两个比较雷同的内置函数,分别是new和make方法,二者都可以用来分配内存,那他们有什么区别呢?下面我们就从底层来分析一下二者的不同。感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助
前言

在Go语言中,有两个比较雷同的内置函数,分别是new和make方法,二者都可以用来分配内存,那他们有什么区别呢?对于初学者可能会觉得有点迷惑,尤其是在掌握不牢固的时候经常遇到panic,下面我们就从底层来分析一下二者的不同。感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

new的使用

new可以对类型进行内存创建和初始化,其返回值是所创建类型的指针引用,这是与make函数的区别之一。我们通过一个示例代码看下:

func main() {
var a *int
fmt.Println(a) // nil
*a = 123 //panic
fmt.Println(a)
}

通过上面代码可以看出,当我们通过var声明一个变量后打印后输出nil,当我们给这个变量赋值的时候会报错:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10a9043]

综上可以总结出初始化一个指针变量,其值为nil,nil的值是不能直接赋值的。

既然我们知道了没有为其分配内存,那么我们使用new分配一个吧。代码修改后:

func main() {
var a *int
a = new(int)
fmt.Printf("a type is :%T,a point value is :%v,a value is:%v,a size is: %v\n", a, a, *a, unsafe.Sizeof(a))
//a type is :*int,a point value is :0xc00001a0a0,a value is:0,a size is: 8
*a = 123
fmt.Printf("a type is :%T,a point value is :%v,a value is:%v,a size is: %v\n", a, a, *a, unsafe.Sizeof(a))
//a type is :*int,a point value is :0xc00001a0a0,a value is:123,a size is: 8
}

通过以上示例我们可以看到new其返回一个指向新分配的类型为int的指针,指针值为0xc00001a0a0,这个指针指向的内容的值为零(zero value)。通过new进行内存分配就可以对其进行赋值。

底层实现

new函数的签名如下:

func new(Type) *Type

Type是指变量的类型,可以看到new会根据变量类型返回一个指向该类型的指针。

底层调用的是runtime.newobject申请内存空间:

func newobject(typ *_type) unsafe.Pointer {
return mallocgc(typ.size, typ, true)
}

通过调用mallocgc在堆上按照typ.size的大小申请内存,因此new只会为结构体申请一块内存空间,不会为结构体中的指针类型申请内存空间。

make的使用

make 函数也是用于内存分配的,但是和new不同,仅支持 slice、map、channel 三种数据类型的内存创建,其返回值是所创建类型的本身,而不是新的指针引用。

注意:这三种类型都是引用类型,所以没必要返回他们的指针了,必须得初始化,但是不是设置为零值。

我们通过一个示例看一下:

func test()  {
var s *[]int
fmt.Printf("s: %p %#v \n", &s, s) //s: 0xc00000e028 (*[]int)(nil)
s = new([]int)
fmt.Printf("s: %p %#v \n", &s, s) //s: 0xc00000e028 &[]int(nil)
(*s)[0] = 8
fmt.Printf("s: %p %#v \n", &s, s) //panic: runtime error: index out of range [0] with length 0
}

我们先用new进行初始化,会给引用类型初始化为nil,nil是不能直接赋值的。下面改为make。

func test()  {
var s = make([]int, 5)
fmt.Printf("s: %p %#v \n", &s, s) //s: 0xc00000c060 []int{0, 0, 0, 0, 0}
s[0] = 8
fmt.Printf("s: %p %#v \n", &s, s) //s: 0xc00000c060 []int{8, 0, 0, 0, 0}
}

通过以上示例输出我们可以看到,make不仅可以开辟一个内存,还能给这个内存的类型初始化其零值。同理,对于map、channel也是同样的效果。

底层实现

make函数的签名如下:

func make(t Type, size ...IntegerType) Type

可以看到make返回的是复合类型本身。

make在申请slice内存时,底层调用的是runtime.makeslice,

func makeslice(et *_type, len, cap int) unsafe.Pointer {
mem, overflow := math.MulUintptr(et.size, uintptr(cap))
if overflow || mem > maxAlloc || len < 0 || len > cap {
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicmakeslicelen()
}
panicmakeslicecap()
} return mallocgc(mem, et, true)
}

可以看到makeslice申请内存底层调用的也是mallocgc,首先通过MulUintptr根据容量cap乘以type.siz计算出所需要内存大小,然后再分配所需内存,make为map和channel申请内存底层分别是runtime.makemap_small,runtime.makechan,也是同样调用mallocgc。

总结
  • make和new都是golang用来分配内存的函数,且在堆上分配内存,make 即分配内存,也初始化内存。new只是将内存清零,并没有初始化内存。
  • make返回的还是引用类型本身;而new返回的是指向类型的指针。
  • make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。

到此这篇关于深入理解Golang make和new的区别及实现原理的文章就介绍到这了

原文来自:https://www.jb51.net/article/266132.htm

本文地址:https://www.linuxprobe.com/make-new-linux.html

Linux命令大全:https://www.linuxcool.com/

Linux系统大全:https://www.linuxdown.com/

红帽认证RHCE考试心得:https://www.rhce.net/

Golang make和new的区别及实现原理详解的更多相关文章

  1. 图论中DFS与BFS的区别、用法、详解…

    DFS与BFS的区别.用法.详解? 写在最前的三点: 1.所谓图的遍历就是按照某种次序访问图的每一顶点一次仅且一次. 2.实现bfs和dfs都需要解决的一个问题就是如何存储图.一般有两种方法:邻接矩阵 ...

  2. 图论中DFS与BFS的区别、用法、详解?

    DFS与BFS的区别.用法.详解? 写在最前的三点: 1.所谓图的遍历就是按照某种次序访问图的每一顶点一次仅且一次. 2.实现bfs和dfs都需要解决的一个问题就是如何存储图.一般有两种方法:邻接矩阵 ...

  3. 红黑树原理详解及golang实现

    目录 红黑树原理详解及golang实现 二叉查找树 性质 红黑树 性质 operation 红黑树的插入 golang实现 类型定义 leftRotate RightRotate Item Inter ...

  4. Golang入门教程(十三)延迟函数defer详解

    前言 大家都知道go语言的defer功能很强大,对于资源管理非常方便,但是如果没用好,也会有陷阱哦.Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在 ...

  5. gcc与g++区别以及相关参数详解

    ---恢复内容开始--- 原文链接:g++和gcc的区别 一 .二者区别 gcc和g++都是GNU(一个组织)的编译器. 1.对于.c后缀的文件,gcc把它当做是C程序:g++当做是C++程序: 2. ...

  6. Java中泛型区别以及泛型擦除详解

    一.引言 复习javac的编译过程中的解语法糖的时候看见了泛型擦除中的举例,网上的资料大多比较散各针对性不一,在此做出自己的一些详细且易懂的总结. 二.泛型简介 泛型是JDK 1.5的一项新特性,一种 ...

  7. Linux下的/etc/crontab文件和crontab -e命令区别及Crontab命令详解(转)

    /etc/crontab文件和crontab -e命令区别 1.格式不同 前者 # For details see crontabs # Example of job definition: # .- ...

  8. Golang 读写锁RWMutex 互斥锁Mutex 源码详解

    前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...

  9. python入门-PyCharm中目录directory与包package的区别及相关import详解

    一.概念介绍 在介绍目录directory与包package的区别之前,先理解一个概念---模块 模块的定义:本质就是以.py结尾的python文件,模块的目的是为了其他程序进行引用. 目录(Dict ...

  10. PyCharm中目录directory与包package的区别及相关import详解

    一.概念介绍 在介绍目录directory与包package的区别之前,先理解一个概念---模块 模块的定义:本质就是以.py结尾的python文件,模块的目的是为了其他程序进行引用. 目录(Dire ...

随机推荐

  1. 【转载】EXCEL VBA 同时选择多个工作表的方法

    如果要用VBA同时选择多个工作表,可以利用数组或设置"Select"方法的参数为"False"来扩展所选择的内容,如下面的一些代码:   1.用工作表名称:   ...

  2. 什么是Rabbitmq消息队列? (安装Rabbitmq,通过Rabbitmq实现RPC全面了解,从入门到精通)

    目录 Rabbitmq 一: 消息队列介绍 1.介绍 2.MQ解决了什么问题 1.应用的解耦 2.流量削峰 3.消息分发(发布订阅: 观察者模式) 4.异步消息(celery就是对消息队列的封装) 3 ...

  3. java注解基础知识整理

    目录 1.注解的定义 1.1.定义一个注解 1.2.注解的使用 2.JDK内置注解 2.1.java.lang包下的注释类型 2.2.元注解 2.3.Deprecated注解 3.在注解中定义属性 3 ...

  4. MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(2)-Swagger框架集成

    Swagger是什么? Swagger是一个规范且完整API文档管理框架,可以用于生成.描述和调用可视化的RESTful风格的 Web 服务.Swagger 的目标是对 REST API 定义一个标准 ...

  5. org.apache.catalina.startup.HostConfig.deployWAR Error deploying web application archive

    昨天23点给一个老项目改完打包发布到云服务器后报错,Tomcat9 启动不了,白折腾了我几个小时. 这错误以前遇到过,由于太过久远已经忘记,特此记录 错误日志 22-Dec-2021 23:52:18 ...

  6. DVWA靶场实战(五)——File Upload

    DVWA靶场实战(五) 五.File Upload: 1.漏洞原理: File Upload中文名叫做文件上传,文件上传漏洞是指用户上传了一个可执行脚本文件(php.jsp.xml.cer等文件),而 ...

  7. [数据结构]普里姆(Prim)算法生成最小生成树

    前提介绍:最小生成树概念 一个连通图的生成树是图的极小连通子图,它包含图中的所有定点,并且只含尽可能少的边,这意味着对于生成树来说,就砍去使生成树变成非连通图:若给它怎家一条边就会形成图中的一条回路. ...

  8. Java学习笔记:2022年1月6日

    Java学习笔记:2022年1月6日 摘要:不可变字符串为什么不可变?StringBuffer类与StringBuilder类,字符串操作拾遗,记事本原理,进制转化问题. 目录 Java学习笔记:20 ...

  9. 使用 NineData 高效编写 SQL

    SQL 是 Structured Query Language 的缩写,中文翻译为"结构化查询语言".它是关系型数据库的标准语言,所有的关系型数据库管理系统(RDBMS),比如 M ...

  10. PHP判断访问来源是PC端还是移动端

    一个方法轻松搞定,各种判断后当返回true为移动端,反之为PC端. function isMobile(){ // 如果有HTTP_X_WAP_PROFILE则一定是移动设备 if (isset ($ ...