Go语言中new和make是内建的两个函数,主要用来创建分配类型内存。
new可以用于对任何类型的内存分配,并返回指向该内存的指针,且内存中存储的值为对应类型的零值。new不常用,一般不用它。
make函数只用于slice、map以及channel的内存分配和初始化(非零值)。make无可替代,我们在使用slice、map和channel的时候用make进行初始化。
理解new和make首先从Golang的零值开始。
零值就是变量只有声明没有初始化时系统默认设置的值。以下变量即只有声明没有初始化值,所以会被默认赋值为其对应的零值。
var i int var f float64 var b bool var s string var ip *int
类型 | 零值 |
---|---|
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 |
// 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,即分配的内存里存放的变量是什么类型
输出:返回指向内存的指针,并且内存中存放的是该类型的零值
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的指针: %p\n", 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的指针: %p\n", 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("%p\n", (*m)) //0xc00007e4b0 fmt.Printf("%p\n", &(*m)) //0xc0000ca028
同样,看下面的这张图便一目了然。
// 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的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型。
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] }
func main() { //先看make的初始化 mp1 := make(map[string]string) mp1["乔峰"] = "少林寺" fmt.Println(mp1) //再看new的置零值 mp := new(map[string]string) fmt.Printf("指向map的指针是否为nil: %t\n", (*mp) == nil) //true //以下这行会报错,因为*mp被置为零值nil,没有初始化, (*mp)["张三丰"] = "武当山" }
new函数:返回指向map的指针,而map是零值nil,故而无法进行set操作
make函数:返回map(map本质就是指向hmap的指针),而map已经被初始化(非零值),故而可以直接进行set操作
https://www.zhihu.com/question/446317882/answer/3155926299
最新评论: