作者回复: 正确✅
作者回复: 一语点题,直中要害!
作者回复: 1. 可以以一个具体例子来说明。假设b所在内存单元的地址为8,i的地址为16,那么i与b之间是7个格子还是8个格子呢?是不是应该是7个格子? 2. 这算是给后面做铺垫吧。b是结构体的第一个字段,b的地址起始就是结构体变量的地址。虽然b作为byte类型,其自身的对齐约束是1,但是考虑到整个结构体,实际上go编译器为b分配的地址必须是被8整除的。 3. 不要在一个包里用,代码前面说过:“一旦结构体中包含非导出字段,那么这种逐一字段赋值的方式就不再被支持了”。所以建立一个新包,导入T,创建T类型变量并赋值。 4. go是静态语言,对于一个类型,编译器要知道它的大小。如果嵌套T,那么编译器无法知道其大小。但如果是*T或[]T,编译器只需要知道指针大小以及切片这个“描述符”的大小即可。
作者回复: 不错!不过还差那么一点点:所有类型的指针的大小都是固定长度的。所以编译器可以得到这个指针类型的大小。即便在不知道T大小的情况下也可以。
作者回复: 编译器不会改变字段顺序的,只会基于现有次序做缝隙填充与结构体尾部padding,保证各个字段以及整个结构体都是对齐的。
作者回复: 是否是最优还不确认,但这是一种降低struct内存占用的技巧。github有一些struct布局优化的项目,可以探索一下它们使用的算法。
作者回复: 思路正确。
作者回复: go是静态语言,对于一个类型,编译器要知道它的大小。如果在T类型的定义中嵌套T,那么编译器无法知道其大小。但如果是*T或[]T,编译器只需要知道指针大小以及切片这个“描述符”的大小即可。而指针与切片的大小都是固定的,对编译器来说是已知的。
作者回复: type alias当初加入go,主要是为一些大型代码仓库的重构提供方便。大型代码仓库重构不是一蹴而就的,需要过渡期。 在过渡期内,API迁移后,应该依旧在原先的位置上可用。比如A-Z包都依赖foo.F1,但foo.F1迁移到了bar.F1,这时如果没有alias,所有依赖foo.F1的包编译都会失败。 但在大型代码仓库中,要想很好的完成这样的重构,更多是协调工作,需要A-Z包同时将依赖foo.F1替换为bar.F1,这很困难,除非是统一行动。于是为了支持一种过渡,原foo包的开发者就希望这个foo.F1 -> bar.F1的迁移对于依赖foo.F1的包来说是透明的,即便迁移完依旧可用。那么有了type alias后,只需在foo包中添加一行 type F1 = bar.F1即可。 这样一来,就满足了过渡期的要求。大型代码仓库重构都是这样循序渐进的。
作者回复: 占位符。让编译器分配对应的内存空间。