在C语言中,在结构中定义字段的顺序是在内存中实例化它们的顺序。考虑到内存对齐,以下结构在内存中的大小将为8个字节,如图所示,但是如果将字段反转,则只有6个字节,因为不需要任何对齐填充。
struct s { int32_t a; /* 2 bytes of padding to align a 64 bit integer */ int64_t b; }
这种顺序保证存在于C结构,C ++类(和结构)和Objective-C类中。
Swift类和结构中的字段是否同样保证了存储顺序?或者(鉴于该语言不支持与列出的其他指针相同的指针),编译器是否在编译时为您最佳地重新安排了指针?
是的,结构元素在内存中的顺序就是它们声明的顺序。详细信息可以在“ 类型布局” (已添加重点)中找到。但是请注意使用“当前”,因此在以后的Swift版本中可能会发生变化:
脆弱的结构和元组布局 结构和元组 当前 共享相同的布局算法,在编译器实现中称为“通用”布局算法。算法如下: 以0的大小和1的对齐方式开始。 以元组的元素顺序或结构的 var声明顺序 遍历字段。对于每个字段: 通过四舍五入到字段的对齐方式来更新大小,即将其增大到大于或等于大小的最小值,并且可以被字段的对齐方式整除。 将字段的偏移量分配给size的当前值。 通过添加字段的大小来更新大小。 将对齐方式更新为最大对齐方式和字段的对齐方式。 最终大小和对齐方式是聚合的大小和对齐方式。该类型的跨度是将最终大小四舍五入以对齐。
脆弱的结构和元组布局
结构和元组 当前 共享相同的布局算法,在编译器实现中称为“通用”布局算法。算法如下:
填充/对齐方式与C不同:
请注意,这与C或LLVM的常规布局规则不同,因为大小和跨度是不同的。C布局要求将嵌入式结构的大小填充为其对齐方式,并且在那里不进行任何布局,而Swift布局允许外部结构在内部结构的尾部填充中布局字段,并允许对齐。
仅当从C 导入 结构时,才能保证它具有相同的内存布局。Apple的Joe Groff在[swift- users]上写道:将 C语义映射到Swift
如果您依赖于特定的布局,则应该在C中定义该结构并将其导入到Swift中。
而后来在讨论:
您可以保留在C中定义的结构并将其导入Swift。 Swift将尊重C的布局。
例:
struct A { var a: UInt8 = 0 var b: UInt32 = 0 var c: UInt8 = 0 } struct B { var sa: A var d: UInt8 = 0 } // Swift 2: print(sizeof(A), strideof(A)) // 9, 12 print(sizeof(B), strideof(B)) // 10, 12 // Swift 3: print(MemoryLayout<A>.size, MemoryLayout<A>.stride) // 9, 12 print(MemoryLayout<B>.size, MemoryLayout<B>.stride) // 10, 12
这var d: UInt8是在的尾部填充中布置的var sa: A。如果您在C中定义相同的结构
var d: UInt8
var sa: A
struct CA { uint8_t a; uint32_t b; uint8_t c; }; struct CB { struct CA ca; uint8_t d; };
然后将其导入到Swift
// Swift 2: print(sizeof(CA), strideof(CA)) // 9, 12 print(sizeof(CB), strideof(CB)) // 13, 16 // Swift 3: print(MemoryLayout<CA>.size, MemoryLayout<CA>.stride) // 12, 12 print(MemoryLayout<CB>.size, MemoryLayout<CB>.stride) // 16, 16
因为uint8_t d是在尾巴填充后布局的struct CA sa。
uint8_t d
struct CA sa
从Swift 3开始,对于 从C导入 的结构,两者size和都stride返回相同的值( 包括 结构填充) , 即sizeof返回与C中相同的值。
size
stride
sizeof
这是一个简单的函数,有助于演示以上内容(Swift 3):
func showMemory<T>(_ ptr: UnsafePointer<T>) { let data = Data(bytes: UnsafeRawPointer(ptr), count: MemoryLayout<T>.size) print(data as NSData) }
Swift中定义的结构:
var a = A(a: 0xaa, b: 0xbbbbbbbb, c: 0xcc) showMemory(&a) // <aa000000 bbbbbbbb cc> var b = B(sa: a, d: 0xdd) showMemory(&b) // <aa000000 bbbbbbbb ccdd>
从C导入的结构:
var ca = CA(a: 0xaa, b: 0xbbbbbbbb, c: 0xcc) showMemory(&ca) // <aa000000 bbbbbbbb cc000000> var cb = CB(ca: ca, d: 0xdd) showMemory(&cb) // <aa000000 bbbbbbbb cc000000 dd000000>