原文地址:https://www.douyacun.com/article/0f451f0e03545eef38b837d110d69797
疑问:
var a = make([]int, 1, 2)
fmt.Printf("%v\t\t%v\n", a, (*reflect.SliceHeader)(unsafe.Pointer(&a)))
a = append(a, 2)
fmt.Printf("%v\t\t%v\n", a, (*reflect.SliceHeader)(unsafe.Pointer(&a)))
a = append(a, 3, 4, 5)
fmt.Printf("%v\t%v\n", a, (*reflect.SliceHeader)(unsafe.Pointer(&a)))
// [0] &{824634327040 1 2}
// [0 2] &{824634327040 2 2}
// [0 2 3 4 5] &{824634368000 5 6}
如果切片容量不足的话,go扩容切片需要重新申请内存,内存地址会发生变化
如果切片容量充足,go不需要扩容,底层数组data内存地址也不会发生变化,func append(slice []Type, elems ...Type) []Type
传入的切片也是复制(切片数据结构), 函数的参数是传值, 底层数组部分是通过隐式指针传递(指针本身依然是传值的,但是指针指向的却是同一份的数据),所以被调用函数是可以通过指针修改掉调用参数切片中的数据。除了数据之外,切片结构还包含了切片长度和切片容量信息,这2个信息也是传值的。如果被调用函数中修改了Len
或Cap
信息的话,就无法反映到调用参数的切片中,这时候一般会通过返回修改后的切片来更新之前的切片。
执行从main.main开始,如果main导入了其他的包,会按照顺序将他们包含进mian包里
// 具名函数
func Add(a, b int) int {
return a+b
}
// 匿名函数
var Add = func(a, b int) int {
return a+b
}
func Inc() (v int) {
// 闭包,什么是闭包
defer func() {v++}()
return 42
}
// 43,这里为什么是43而不是42
func main() {
for i := 0; i < 3; i++ {
defer func(){ println(i) } ()
}
}
// Output:
// 3
// 3
// 3
// 关闭文件
func (f *File) Close() error {
// ...
}
// 读文件数据
func (f *File) Read(offset int64, data []byte) int {
// ...
}
Go语言在提供严格的类型检查的同时,通过接口类型实现了对鸭子类型的支持,使得安全动态的编程变得相对容易。
鸭子类型 - 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。- 维基百科
在Go语言中,对于基础类型(非接口类型)不支持隐式转换,无法将一个int类型的值直接赋值给int64类型的变量,也无法将int的值赋值给底层是int类型新定义命名类型的变量,go语言对类型的一致性要求非常严格,但是对接口转化则是非常的灵活,对象和接口之间的转换、接口和接口之间的转换都可能是隐式的转换。
有时候对象和接口之间太灵活了,导致我们需要人为地限制这种无意之间的适配。常见的做法是定义一个含特殊方法来区分接口。比如runtime
包中的Error
接口就定义了一个特有的RuntimeError
方法,用于避免其它类型无意中适配了该接口:
type runtime.Error interface {
error
// RuntimeError is a no-op function but
// serves to distinguish types that are run time
// errors from ordinary errors: a type is a
// run time error if it has a RuntimeError method.
RuntimeError()
}
type testing.TB interface {
Error(args ...interface{})
Errorf(format string, args ...interface{})
...
// A private method to prevent users implementing the
// interface and so future additions to it will not
// violate Go 1 compatibility.
private()
}
扩容和内存对齐
// 这个例子 讲讲扩容或内存
var a = make([]int, 1, 1)
fmt.Printf("%v\t%v\n", a, (*reflect.SliceHeader)(unsafe.Pointer(&a)))
a = append(a, 2, 3, 4, 5)
fmt.Printf("%v\t%v\n", a, (*reflect.SliceHeader)(unsafe.Pointer(&a)))
// [0] &{824634335232 1 1}
// [0 2 3 4 5] &{824634368000 5 6}
按照扩容的第二条,大于两倍旧容量,按理说应该是5但这里是6,那就是接下来的内存对齐了