new(T)
不会初始化内存,只会将内存置零。 也就是说,new(T)
会为类型为T
的新项分配已置零的内存空间, 并返回它的地址。每当获取一个复合字面的地址时,都将为一个新的实例分配内存。// 少数情况下,若复合字面不包括任何字段,它将创建该类型的零值。表达式 new(File) 和 &File{} 是等价的。
f := new(File)
return &File{fd, name, nil, 0}
make(T, args)
的目的不同于new(T)
。它只用于创建切片、映射和信道,并返回类型为T
(而非*T
)的一个已初始化 (而非置零)的值。出现这种用差异的原因在于,这三种类型本质上为引用数据类型,它们在使用前必须初始化。// 切片是一个具有三项内容的描述符,包含一个指向(数组内部)数据的指针、长度以及容量, 在这三项被初始化之前,该切片为 nil。
// 对于切片、映射和信道,make 用于初始化其内部的数据结构并准备好将要使用的值。
// 如下会分配一个具有100个 int 的数组空间,接着创建一个长度为10, 容量为100并指向该数组中前10个元素的切片结构。与此相反,
// new([]int) 会返回一个指向新分配的,已置零的切片结构, 即一个指向 nil 切片值的指针。
make([]int, 10, 100)
数组是值。将一个数组赋予另一个数组会复制其所有元素。
特别地,若将某个数组传入某个函数,它将接收到该数组的一份副本而非指针。
数组的大小是其类型的一部分。类型[10]int
和[20]int
是不同的。
// 数组为值的属性很有用,但代价高昂;若你想要C那样的行为和效率,你可以传递一个指向该数组的指针。
// 但这并不是Go的习惯用法,切片才是。
func Sum(a *[3]float64) (sum float64) {
for _, v := range *a {
sum += v
}
return
}
array := [...]float64{7.0, 8.5, 9.1}
x := Sum(&array) // 注意显式的取址操作
切片保存了对底层数组的引用,若你将某个切片赋予另一个切片,它们会引用同一个数组。 若某个函数将一个切片作为参数传入,则它对该切片元素的修改对调用者而言同样可见, 这可以理解为传递了底层数组的指针。
只要切片不超出底层数组的限制,它的长度就是可变的,只需将它赋予其自身的切片即可。 切片的容量可通过内建函数 cap 获得,它将给出该切片可取得的最大长度。若数据超出其容量,则会重新分配该切片。返回值即为所得的切片。 该函数中所使用的len
和cap
在应用于nil
切片时是合法的,它会返回0。
func Append(slice, data[]byte) []byte {
l := len(slice)
if l + len(data) > cap(slice) { // 重新分配
// 为了后面的增长,需分配两份。
newSlice := make([]byte, (l+len(data))*2)
// copy 函数是预声明的,且可用于任何切片类型。
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:l+len(data)]
for i, c := range data {
slice[l+i] = c
}
return slice
}
切片不能用作映射键,因为它们的相等性还未定义。
与切片一样,映射也是引用类型。 若将映射传入函数中,并更改了该映射的内容,则此修改对调用者同样可见。
若试图通过映射中不存在的键来取值,就会返回与该映射中项的类型对应的零值。有时你需要区分某项是不存在还是其值为零值,你可以使用多重赋值的形式来分辨这种情况。
func offset(tz string) int {
if seconds, ok := timeZone[tz]; ok {
return seconds
}
log.Println("unknown time zone:", tz)
return 0
}
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("%s does not exist\n", path)
}
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// 为了执行该包下的init函数
import _ "net/http/pprof"
// 在此声明中,我们调用了一个 *RawMessage 转换并将其赋予了 Marshaler,以此来要求 *RawMessage 实现 Marshaler,
// 这时其属性就会在编译时被检测。 若 json.Marshaler 接口被更改,此包将无法通过编译, 而我们则会注意到它需要更新。
var _ json.Marshaler = (*RawMessage)(nil)
// 当 bufio.ReadWriter 的 Read 方法被调用时, 它与之前写的转发方法具有同样的效果;接收者是 ReadWriter 的 reader
// 字段,而非 ReadWriter 本身。
func (rw *ReadWriter) Read(p []byte) (n int, err error) {
return rw.reader.Read(p)
}
X
会隐藏该类型中更深层嵌套的其它项X
。// 若 log.Logger 包含一个名为 Command 的字段或方法,Job 的 Command 字段会覆盖它。
type Job struct {
Command string
*log.Logger
}
// 若 Job 结构体中包含名为 Logger 的字段或方法,再将 log.Logger 内嵌到其中的话就会产生错误。
// 某种保护: 如果修改外部嵌套类型字段名时出现重名现象,并使用了该字段,编译器会产生一个错误提示。
// 引用计数通过为整数变量添加互斥锁来很好地实现。 但作为一种高级方法,通过信道来控制访问能够让你写出更简洁,正确的程序。
参考链接: 实效Go编程