百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

仙人指路,引而不发,Golang入门教程New和Make函数区别EP16

yuyutoo 2024-10-11 21:41 11 浏览 0 评论

Golang只有二十五个系统保留关键字,二十几个系统内置函数,加起来只有五十个左右需要记住的关键字,纵观编程宇宙,无人能出其右。其中还有一些保留关键字属于“锦上添花”,什么叫锦上添花?就是从表面上看,就算没有,也无伤大雅,不影响业务或者逻辑的实现,比如lambda表达式之类,没有也无所谓,但在初始化数据结构的时候,我们无法避免地,会谈及两个内置函数:New和Make。

New函数

假设声明一个变量:

package main

import "fmt"

func main() {

	var a string

	fmt.Println(a)
	fmt.Println(&a)

}

系统返回:


0x14000090210

这里我们使用var关键字声明了一个数据类型是字符串的变量a,然后没有做任何赋值操作,于是a的默认值变为系统的零值,也就是空,a的内存地址已经做好了指向,以便存储a将来的值。

下面开始赋值:

package main

import "fmt"

func main() {

	var a string
	a = "ok"
	fmt.Println(a)
	fmt.Println(&a)

}

系统返回:

ok
0x14000104210

可以看到a的值和内存地址都发生了改变,整个初始化过程,我们并没有使用new函数

下面我们把数据类型换成指针:

package main

import "fmt"

func main() {

	var a *string

	fmt.Println(a)
	fmt.Println(&a)

}

系统返回:

<nil>
0x140000a4018

可以看到由于数据类型换成了指针,零值变成了nil

接着像字符串数据类型一样进行赋值操作:

package main

import "fmt"

func main() {

	var a *string

	*a = "ok"

	fmt.Println(*a)
	fmt.Println(&a)

}

系统返回:

panic: runtime error: invalid memory address or nil pointer dereference

是的,空指针异常,为什么?因为指针是一个引用类型,对于引用类型来说,系统不仅需要我们要声明它,还要为它分配内存空间,否则我们赋值的变量就没地方放,这里系统没法为nil分配内存空间,所以没有内存空间就没法赋值。

而像字符串这种值类型就不会有这种烦恼,因为值类型的声明不需要我们分配内存空间,系统会默认为其分配,为什么?因为值类型的零值是一个具体的值,而不是nil,比如整形的零值是0,字符串的零值是空,空不是nil,所以就算是空,也可以赋值。

那引用类型就没法赋值了?

package main

import "fmt"

func main() {

	var a *string
	a = new(string)
	*a = "ok"

	fmt.Println(*a)
	fmt.Println(&a)

}

系统返回:

ok
0x14000126018

这里我们使用了new函数,它正是用于分配内存,第一个参数接收一个类型而不是一个值,函数返回一个指向该类型内存地址的指针,同时把分配的内存置为该类型的零值。

换句话说,new函数可以帮我们做之前系统自动为值类型数据类型做的事。

当然,new函数不仅仅能够为系统的基本类型的引用分配内存,也可以为自定义数据类型的引用分配内存:

package main
package main

import "fmt"

func main() {

	type Human struct {
		name string
		age  int
	}
	var human *Human
	human = new(Human)
	human.name = "张三"
	fmt.Println(*human)
	fmt.Println(&human)

}

系统返回:

{张三 0}
0x1400011c018

这里我们自定义了一种人类的结构体类型,然后声明该类型的指针,由于指针是引用类型,所以必须使用new函数为其分配内存,然后,才能对该引用的结构体属性进行赋值。

说白了,new函数就是为了解决引用类型的零值问题,nil算不上是真正意义上的零值,所以需要new函数为其“仙人指路”。

Make函数

make函数从功能层面上讲,和new函数是一致的,也是用于内存的分配,但它只能为切片slice,字典map以及通道channel分配内存,并返回一个初始化的值。

这显然有些矛盾了,既然已经有了new函数,并且new函数可以为引用数据类型分配内存,而切片、字典和通道不也是引用类型吗?

大家既然都是引用类型,为什么不直接使用new函数呢?

package main

import "fmt"

func main() {
	a := *new([]int)
	fmt.Printf("%T, %v\n", a, a == nil)

	b := *new(map[string]int)
	fmt.Printf("%T, %v\n", b, b == nil)

	c := *new(chan int)
	fmt.Printf("%T, %v\n", c, c == nil)
}

程序返回:

[]int, true
map[string]int, true
chan int, true

虽然new函数也可以为切片、字典和通道分配内存,但没有意义,因为它分配以后的地址还是nil:


package main

import "fmt"

func main() {
	a := *new([]int)
	fmt.Printf("%T, %v\n", a, a == nil)

	b := *new(map[string]int)
	fmt.Printf("%T, %v\n", b, b == nil)

	c := *new(chan int)
	fmt.Printf("%T, %v\n", c, c == nil)

	b["123"] = 123

	fmt.Println(b)
}

这里使用new函数初始化以后,为字典变量b赋值,系统报错:

panic: assignment to entry in nil map

提示无法为nil的字典赋值,所以这就是make函数存在的意义:


package main

import "fmt"

func main() {
	a := *new([]int)
	fmt.Printf("%T, %v\n", a, a == nil)

	b := make(map[string]int)
	fmt.Printf("%T, %v\n", b, b == nil)

	c := *new(chan int)
	fmt.Printf("%T, %v\n", c, c == nil)

	b["123"] = 123

	fmt.Println(b)
}

这里字典b使用make函数进行初始化之后,就可以为b进行赋值了。

程序返回:

[]int, true
map[string]int, false
chan int, true
map[123:123]

这也是make和new的区别,make可以为这三种类型分配内存,并且设置好其对应基本数据类型的零值,所以只要记住切片、字典和通道声明后需要赋值的时候,需要使用make函数为其先分配内存空间。

不用New或者Make会怎么样

有人会说,为什么非得纠结分配内存的问题?用海象操作符不就可以直接赋值了吗?

// example1.go
package main

import "fmt"

func main() {

	a := map[int]string{}
	fmt.Printf("%T, %v\n", a, a == nil)

	a[1] = "ok"

	fmt.Println(a)
	
}

程序返回:

map[int]string, false
map[1:ok]

没错,就算没用make函数,我们也可以“人为”的给字典分配内存,因为海象操作符其实是声明加赋值的连贯操作,后面的空字典就是在为变量申请内存空间。

但为什么系统还要保留new和make函数呢?事实上,这是一个分配内存的时机问题,声明之后,没有任何规定必须要立刻赋值,赋值后的变量会消耗系统的内存资源,所以声明以后并不分配内存,而是在适当的时候再分配,这也是new和make的意义所在,所谓千石之弓,引而不发,就是这个道理。

结语

new和make函数都可以为引用类型分配内存,起到“仙人指路”的作用,变量声明后“引而不发”就是使用它们的时机,make函数作用于创建 slice、map 和 channel 等内置的数据结构,而 new函数作用是为类型申请内存空间,并返回指向内存地址的指针。

相关推荐

《保卫萝卜2》安卓版大更新 壕礼助阵世界杯

《保卫萝卜2:极地冒险》本周不仅迎来了安卓版本的重大更新,同时将于7月4日本周五,带来“保卫萝卜2”安卓版本世界杯主题活动的火热开启,游戏更新与活动两不误。一定有玩家会问,激萌塔防到底进行了哪些更新?...

儿童手工折纸:胡萝卜,和孩子一起边玩边学carrot

1、准备两张正方形纸,一橙一绿,对折出折痕。2、橙色沿其中一条对角线如图折两三角形。3、把上面三角折平,如图。4、绿色纸折成三角形。5、再折成更小的三角形。6、再折三分之一如图。7、打开折纸,压平中间...

《饥荒》食物代码有哪些(饥荒最新版代码总汇食物篇)

饥荒游戏中,玩家们需要获取各种素材与食物,进行生存。玩家们在游戏中,进入游戏后按“~”键调出控制台使用代码,可以直接获得素材。比如胡萝卜的代码是carrot,玉米的代码是corn,南瓜的代码是pump...

Skyscanner:帮你找到最便宜机票 订票不求人

你喜欢旅行吗?在合适的时间、合适的目的地,来一场说走就走的旅行?机票就是关键!Skyscanner这款免费的手机应用,在几秒钟内比较全球600多家航空公司的航班安排、价格和时刻表,帮你节省金钱和时间。...

小猪佩奇第二季50(小猪佩奇第二季英文版免费观看)

Sleepover过夜Itisnighttime.现在是晚上。...

我在民政局工作的那些事儿(二)(我在民政局上班)

时间到了1997年的秋天,经过一年多的学习和实践,我在处理结婚和离婚的事情更加的娴熟,也获得了领导的器重,所以我在处理平时的工作时也能得心应手。这一天我正在离婚处和同事闲聊,因为离婚处几天也遇不到人,...

夏天来了就你还没瘦?教你不节食13天瘦10斤的哥本哈根减肥法……

好看的人都关注江苏气象啦夏天很快就要来了你是否和苏苏一样身上的肉肉还没做好准备?真是一个悲伤的故事……下面这个哥本哈根减肥法苏苏的同事亲测有效不节食不运动不反弹大家快来一起试试看吧~DAY1...

Pursuing global modernization for peaceful development, mutually beneficial cooperation, prosperity for all

AlocalworkeroperatesequipmentintheChina-EgyptTEDASuezEconomicandTradeCooperationZonei...

Centuries-old tea road regains glory as Belt and Road cooperation deepens

FUZHOU/ST.PETERSBURG,Oct.2(Xinhua)--NestledinthepicturesqueWuyiMountainsinsoutheastChi...

15 THE NUTCRACKERS OF NUTCRACKER LODGE (CONTINUED)胡桃夹子小屋里的胡桃夹子(续篇)

...

AI模型部署:Triton Inference Server模型部署框架简介和快速实践

关键词:...

Ftrace function graph简介(flat function)

引言由于android开发的需要与systrace的普及,现在大家在进行性能与功耗分析时候,经常会用到systrace跟pefetto.而systrace就是基于内核的eventtracing来实...

JAVA历史版本(java各版本)

JAVA发展1.1996年1月23日JDK1.0Java虚拟机SunClassicVM,Applet,AWT2.1997年2月19日JDK1.1JAR文件格式,JDBC,JavaBea...

java 进化史1(java的进阶之路)

java从1996年1月第一个版本诞生,到2022年3月最新的java18,已经经历了27年,整整18个大的版本。很久之前有人就说java要被淘汰,但是java活到现在依然坚挺,不知道java还能活...

学习java第二天(java学完后能做什么)

#java知识#...

取消回复欢迎 发表评论: