话不多说,直接上码:
// noCopy may be embedded into structs which must not be copied // after the first use. // See https://golang.org/issues/8005#issuecomment-190753527 // for details. type noCopy struct{} // Lock is a no-op used by -copylocks checker from `go vet`. func (*noCopy) Lock() {} func (*noCopy) Unlock() {} 意思就是说,如果你想禁止一个 struct 被拷贝,只需把 noCopy 嵌入即可,go vet 命令会做禁止拷贝的检查。
package main func main() { var t = TestNoCopy{} Test(t) } type noCopy struct{} func (*noCopy) Lock() {} func (*noCopy) Unlock() {} type TestNoCopy struct { noCopy } func Test(t TestNoCopy) {} 将上述代码保存为 main.
理解的不深,把自己得到的结论以及参考的博文记录了下来,日常开发中注意 struct 字段的排序,记住这么多也足够了。
什么是数据结构对齐 维基百科:数据结构对齐是代码编译后在内存的布局与使用方式。包括三方面内容:数据对齐、数据结构填充(padding)与包入(packing)。
挺模糊的定义,我就简单理解为:数据在内存中的存储位置,不是那么单纯地一个挨一个,或是为了性能,或是为了安全,产生了一系列分布规则。
为什么要进行对齐 要解释为什么需要对齐,需要先说明一下 CPU 访问内存的方式:以字长(machine word)为单位访问,而非以字节(Byte)为单位访问。
也就是说 32 位 CPU 以 32 位(4字节)为单位访问内存,64 位 CPU 以 64 位(8字节)为单位访问内存。
知道了 CPU 访问内存的方式,如果不进行内存对齐,会增加 CPU 访问内存的次数,严重时会触发总线错误
32 位操作系统下,a、b 各占 3 字节,结合 CPU 的访问方式来看,在内存未对齐时,若访问 b 元素,需要进行两次内存访问。内存对齐后,a、b 各占 4 字节,此时访问 b 元素,只需要一次内存访问。
Go 内存对齐 对齐保证 可以通过 unsafe 包的 Alignof() 方法查看对齐保证,示例如下:
var i8 int8 = 1 align := unsafe.Alignof(i8) var i32 int32 = 1 align = unsafe.Alignof(i32) type alignment guarantee bool, byte, uint8, int8 1 uint16, int16 2 uint32, int32 4 float32, complex64 4 arrays depend on element types structs depend on field types other types size of a native word 表格数据来源 概括来说就是:
普通指针:*T 对于任意类型 T,它所对应的指针类型就是 *T
var i int var ip *int var s string var sp *string Go 是强类型,不同类型对应的 *T 不可相互转换、不可相互赋值、不可做比较、普通指针不可参与计算。
万能指针:unsafe.Pointer unsafe.Pointer 与 *T 的关系,就好比 interface{} 和 T 的关系,也就是说 unsafe.Pointer 可以承载任意类型的 *T,它们之间可以互相转换,这就赋予了开发者直接操作指定内存的权力(效果不明显,配合 uintptr 服用效果最佳)
var i int = 1 pointer := unsafe.Pointer(&i) // *int -> pointer p := (*int)(pointer) // pointer -> *int *p = 2 fmt.Println(i) // 2 unsafe.Pointer 提供的操作
// Pointer(*T) 将 *T 转化为 Pointer,也是结构体对应的内存的开始地址 type Pointer *ArbitraryType // Sizeof(T) 返回 T 占用字节数 func Sizeof(x ArbitraryType) uintptr // Offsetof 返回结构体成员在内存中的位置离结构体起始处的字节数,所传参数必须是结构体的成员 func Offsetof(x ArbitraryType) uintptr 魔幻指针:uintptr uintptr 解除了指针无法参与计算的封禁。官方对其定义为: