GO 記憶體分配new和make的區別
func new
func new(Type) *Type
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 make
func make(Type, size IntegerType) Type
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, so make([]int, 0, 10) allocates a slice of length 0 and capacity 10. 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.
1、Allocation with new
Go has two allocation primitives, the built-in functions
new
and make
.
They do different things and apply to different types, which can be confusing, but the rules are simple. Let's talk about new
first.
It's a built-in function that allocates memory, but unlike its namesakes in some other languages it does not new(T)
allocates
zeroed storage for a new item of type T
and returns its address, a value of type *T
.
In Go terminology, it returns a pointer to a newly allocated zero value of type T
.
Since the memory returned by
new
is
zeroed, it's helpful to arrange when designing your data structures that the zero value of each type can be used without further initialization. This means a user of the data structure can create one with new
and
get right to work. For example, the documentation for bytes.Buffer
states that "the
zero value for Buffer
is an empty buffer ready to use." Similarly, sync.Mutex
does
not have an explicit constructor or Init
method. Instead, the zero value for a sync.Mutex
is
defined to be an unlocked mutex.
The
zero-value-is-useful property works transitively(可傳遞的). Consider this type declaration.
type SyncedBuffer struct { lock sync.Mutex buffer bytes.Buffer }
Values of type SyncedBuffer
are also ready to use immediately upon allocation or just declaration. In the next snippet, both p
and v
will
work correctly without further arrangement.
p := new(SyncedBuffer) // type *SyncedBuffer var v SyncedBuffer // type SyncedBuffer
2、Constructors and composite literals(複合字面量)
Sometimes the zero value isn't good enough and an initializing constructor is necessary, as in this example derived from packageos
.func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := new(File) f.fd = fd f.name = name f.dirinfo = nil f.nepipe = 0 return f }There's a lot of boiler plate in there. We can simplify it using a composite literal, which is an expression that creates a new instance each time it is evaluated.
func NewFile(fd int, name string) *File { if fd < 0 { return nil } f := File{fd, name, nil, 0} return &f }Note that, unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. In fact, taking the address of a composite literal allocates a fresh instance each time it is evaluated, so we can combine these last two lines.
return &File{fd, name, nil, 0}The fields of a composite literal are laid out in order and must all be present. However, by labeling the elements explicitly as field
:
value pairs,
the initializers can appear in any order, with the missing ones left as their respective zero values. Thus we could sayreturn &File{fd: fd, name: name}As a limiting case, if a composite literal contains no fields at all, it creates a zero value for the type. The expressions
new(File)
and &File{}
are
equivalent.
Composite
literals can also be created for arrays, slices, and maps, with the field labels being indices or map keys as appropriate.
a:=[...]string{1:"noerror",2:"Eio",3:"invalidargument"}
s:=[]string{1:"noerror",2:"Eio",3:"invalidargument"}
m:=map[int]string{1:"noerror",2:"Eio",3:"invalidargument"}
3、Allocation with make
Back
to allocation. The built-in function
make(T,
args)
serves
a purpose different from new(T)
. It creates slices, maps, and channels only, and it
returns an initialized (not zeroed)
value of type T
(not *T
).
The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use. A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the
length, and the capacity, and until those items are initialized, the slice is nil
.
For slices, maps, and channels, make
initializes the internal data structure and prepares
the value for use. For instance,
make([]int, 10, 100)allocates an array of 100 ints and then creates a slice structure with length 10 and a capacity of 100 pointing at the first 10 elements of the array. (When making a slice, the capacity can be omitted; see the section on slices for more information.) In contrast,
new([]int)
returns
a pointer to a newly allocated, zeroed slice structure, that is, a pointer to a nil
slice
value.
These
examples illustrate the difference between
new
and make
.
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints // Unnecessarily complex: var p *[]int = new([]int) *p = make([]int, 100, 100) // Idiomatic(符合語言習慣的): v := make([]int, 100)Remember that
make
applies
only to maps, slices and channels and does not return a pointer. To obtain an explicit pointer allocate with new
or
take the address of a variable explicitly.