在 Go 語言中,new()
和 make()
是兩個常用的函式,用於建立和初始化不同型別的變數。本文將深入探討 new()
和 make()
的區別、使用場景以及底層實現原理。
引言
- Go 中的
new()
和make()
函式是用於建立和初始化變數的重要工具。 new()
用於建立指定型別的零值變數,並返回該變數的指標。make()
用於建立並初始化引用型別的變數,如切片、對映和通道。
new()
函式
new()
函式的基本語法及用法。new()
建立的變數是指定型別的零值,並返回該變數的指標。new()
適用於建立引用型別以外的其他型別變數。
package main
import "fmt"
func main() {
// 使用 new() 建立一個 int 型別的零值變數的指標
numPtr := new(int)
fmt.Println(*numPtr) // 輸出 0
}
make()
函式
make()
函式的基本語法及用法。make()
用於建立並初始化引用型別的變數。make()
適用於建立切片、對映和通道等引用型別的變數。make()
建立的變數不是零值,而是根據型別進行初始化。
package main
import "fmt"
func main() {
// 使用 make() 建立一個切片,並初始化長度為 3 的切片
slice := make([]int, 3)
fmt.Println(slice) // 輸出 [0 0 0]
}
new()
和 make()
的區別
new()
用於建立任意型別的變數,而make()
僅用於建立引用型別的變數。new()
返回的是指標,而make()
返回的是初始化後的值。new()
建立的變數是零值,make()
建立的變數是根據型別進行初始化。
package main
import "fmt"
func main() {
// 使用 new() 建立一個結構體的指標
personPtr := new(Person)
personPtr.Name = "Alice"
personPtr.Age = 30
fmt.Println(personPtr) // 輸出 &{Alice 30}
// 使用 make() 建立一個對映,並初始化鍵值對
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m) // 輸出 map[one:1 two:2]
}
type Person struct {
Name string
Age int
}
new()
和 make()
的底層實現原理
在 Go 語言中,new()
和 make()
的底層實現原理略有不同。
new()
的底層實現原理
new()
函式在底層使用了 Go 的runtime.newobject
函式。runtime.newobject
函式會分配一塊記憶體,大小為指定型別的大小,並將該記憶體清零。- 然後,
runtime.newobject
函式會返回這塊記憶體的指標。
下面是 new()
函式的簡化版本的底層實現原理示例程式碼:
package main
import (
"fmt"
"unsafe"
)
func main() {
// 使用 new() 建立一個 int 型別的零值變數的指標
numPtr := new(int)
// 獲得指標的值
ptrValue := uintptr(unsafe.Pointer(numPtr))
// 輸出指標的值
fmt.Println(ptrValue)
}
在上述示例程式碼中,我們使用了 unsafe
包中的 Pointer
和 uintptr
型別來操作指標。我們首先使用 new(int)
建立一個 int 型別的零值變數的指標 numPtr
,然後透過 unsafe.Pointer
將指標轉換為 unsafe.Pointer
型別,再透過 uintptr
將 unsafe.Pointer
值轉換為 uintptr
型別,最後輸出指標的值。這個值就是我們所建立的變數的記憶體地址。
make()
的底層實現原理
make()
函式在底層使用了 Go 的runtime.makeslice
、runtime.makemap
和runtime.makechan
函式。runtime.makeslice
函式用於建立切片,它會分配一塊連續的記憶體空間,並返回切片結構體。runtime.makemap
函式用於建立對映,它會分配一塊雜湊表記憶體,並返回對映結構體。runtime.makechan
函式用於建立通道,它會分配一塊通道記憶體,並返回通道結構體。
下面是 make()
函式的簡化版本的底層實現原理示例程式碼:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
// 使用 make() 建立一個切片,並初始化長度為 3 的切片
slice := make([]int, 3)
// 獲得切片的值和長度
sliceValue := reflect.ValueOf(slice)
sliceData := sliceValue.Elem().UnsafeAddr()
sliceLen := sliceValue.Len()
// 輸出切片的值和長度
fmt.Println(sliceData, sliceLen)
}
在上述示例程式碼中,我們使用了 reflect
包中的 Value
、Elem
和 UnsafeAddr
方法來操作切片。我們首先使用 make([]int, 3)
建立一個長度為 3 的切片 slice
,然後透過 reflect.ValueOf
將切片轉換為 reflect.Value
型別,再透過 Elem
方法獲取切片的元素,並透過 UnsafeAddr
方法獲取切片的底層陣列的指標,最後透過 Len
方法獲取切片的長度。這樣,我們就可以獲得切片的底層陣列的指標和長度。
請注意,上述示例程式碼中使用了 reflect
和 unsafe
包,這是為了演示 make()
的底層實現原理而引入的,實際開發中並不需要經常使用這些包。
總結:
透過深入瞭解 new()
和 make()
函式的區別、使用場景以及底層實現原理,讀者可以更好地理解和運用這兩個函式,並完美解決掉面試官的問題,並在實際開發中做出準確的選擇。
本文由mdnice多平臺釋出