Go 常量 · 2023年8月16日 0

Golang分享(二):new和make的区别【】

0. 先说结论

Go语言中new和make是内建的两个函数,主要用来创建分配类型内存。

  • new可以用于对任何类型的内存分配,并返回指向该内存的指针,且内存中存储的值为对应类型的零值。new不常用,一般不用它。
  • make函数只用于slice、map以及channel的内存分配和初始化(非零值)。make无可替代,我们在使用slice、map和channel的时候用make进行初始化。

理解new和make首先从Golang的零值开始。


1. 零值

1.1 何为零值

零值就是变量只有声明没有初始化时系统默认设置的值。以下变量即只有声明没有初始化值,所以会被默认赋值为其对应的零值。

var i int
var f float64
var b bool
var s string

var ip *int

1.2 零值表

类型 零值
bool false
uint/uint8/uint16/uint32/uint64 0
int/int8/int16/int32/int64 0
float32/float64 0
complex64/complex128 0+0i
uintptr 0
byte 0(对应空字符)
rune 0
string ""
struct 内部属性全部是其对应0值
interface nil
slice nil
map nil
chan nil
func nil
指针 nil

2. 内置函数new

2.1 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函数:

  • 输入:只有一个参数Type,即分配的内存里存放的变量是什么类型
  • 输出:返回指向内存的指针,并且内存中存放的是该类型的零值

2.2 new函数Demo

func main() {
  ip := new(int)
  fmt.Println("指向int的指针:", ip) //0xc00001a098
  fmt.Println("int的零值:", *ip)  //0

  sp := new(string)
  fmt.Println("指向string的指针:", sp) //0xc00004e260
  fmt.Println("string的零值:", *sp)  //""

  pp := new(Person)
  fmt.Printf("指向person的指针: %pn", pp)          //0xc0000503e0
  fmt.Println("person.name的零值:", (*pp).name)   //""
  fmt.Println("person.phone的零值:", (*pp).phone) //0
  fmt.Println("person.id的零值:", (*pp).address)  //nil

  ipp := new(*int)
  fmt.Println("指向int指针的指针:", ipp) //0xc00000a030
  fmt.Println("int指针的零值:", *ipp)  //nil

  mp := new(map[string]string)
  fmt.Printf("指向map的指针: %pn", mp)               //0xc0000ca028
  fmt.Printf("指向map的指针是否为nil: %t", (*mp) == nil) //true
}

type Person struct {
  name    string
  phone   int
  address *string
}

看下面的这张图便一目了然。


其实,无论是map、slice还是channel其底层都是一个指针,map是一个指向hmap的指针,channel是一个指向hchan的指针,因为创建map、slice、channel时,底层分别返回的是*hmap、*reflect.SliceHeader、*hchan。其中*hchan在我的另一篇文章没名儿:Golang分享(一):channel底层原理中可以得到证明。所以我不建议大家去理解引用,也不用再纠结Go引用和指针的区别了,就是一个语法糖而已。

那么对上述代码我们在main函数最后新增三行初始化map。

(*m) = map[string]string{"张三丰": "武当山", "乔峰": "少林寺"}
  fmt.Printf("%pn", (*m))  //0xc00007e4b0
  fmt.Printf("%pn", &(*m)) //0xc0000ca028

同样,看下面的这张图便一目了然。



3. 内置函数make

3.1 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. For example, make([]int, 0, 10) allocates an underlying array
//  of size 10 and returns a slice of length 0 and capacity 10 that is
//  backed by this underlying array.
//  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函数:

  • 输入:一个仅限channel、map、slice的参数Type。第二个参数用于指定长度length,第三个参数用于指定容量capacity
  • 输出:一个指定长度和容量的channel、map或者slice

make也是用于内存分配的,但是和new不同,它只用于channel、map以及slice的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。

3.2 make函数的Demo

func main() {
  mapDemo := make(map[string]string)
  fmt.Println("mapDemo: ", mapDemo) //demo:  map[]

  chanDemo := make(chan int, 10)
  fmt.Println("chanDemo: ", chanDemo) //chanDemo:  0xc000116000

  sliceDemo := make([]int, 10)
  fmt.Println("sliceDemo: ", sliceDemo) //sliceDemo:  [0 0 0 0 0 0 0 0 0 0]
}

4.关于零值和初始化的理解

func main() {
  //先看make的初始化
  mp1 := make(map[string]string)
  mp1["乔峰"] = "少林寺"
  fmt.Println(mp1)
  
  //再看new的置零值
  mp := new(map[string]string)
  fmt.Printf("指向map的指针是否为nil: %tn", (*mp) == nil) //true
  //以下这行会报错,因为*mp被置为零值nil,没有初始化,
  (*mp)["张三丰"] = "武当山"
}

new函数:返回指向map的指针,而map是零值nil,故而无法进行set操作

make函数:返回map(map本质就是指向hmap的指针),而map已经被初始化(非零值),故而可以直接进行set操作

文章来源于互联网:Golang分享(二):new和make的区别

打赏 赞(0) 分享'
分享到...
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

文章目录