struct s { int32_t a; /* 2 bytes of padding to align a 64 bit integer */ int64_t b; }
这种排序保证存在于C结构,C类(和结构)和Objective-C类中.
对于Swift类和结构中的字段,存储顺序是否同样有保证?或者(鉴于该语言不支持与列出的其他语言相同的指针),编译器是否在编译时为您重新安排它们?
他们的声明.细节可以找到
在 The Swift ABI
(重点补充).但请注意使用“当前”,所以这个
可能会在未来的Swift版本中发生变化:
Fragile Struct and Tuple Layout
Structs and tuples currently share the same layout algorithm,noted as the “Universal” layout algorithm in the compiler implementation. The algorithm is as follows:
- Start with a size of 0 and an alignment of 1.
- Iterate through the
fields,in element order for tuples,or in var declaration order for
structs. For each field:
- Update size by rounding up to the alignment
of the field,that is,increasing it to the least value greater or
equal to size and evenly divisible by the alignment of the field.- Assign the offset of the field to the current value of size.
- Update
size by adding the size of the field.- Update alignment to the max of
alignment and the alignment of the field.- The final size and alignment
are the size and alignment of the aggregate. The stride of the type is
the final size rounded up to alignment.
填充/对齐与C不同:
Note that this differs from C or LLVM’s normal layout rules in that size and stride are distinct; whereas C layout requires that an embedded struct’s size be padded out to its alignment and that nothing be laid out there,Swift layout allows an outer struct to lay out fields in the inner struct’s tail padding,alignment permitting.
只有从C导入结构,才能保证具有
相同的内存布局.来自Apple的Joe Groff写道
[swift-users] Mapping C semantics to Swift
If you depend on a specific layout,you should define the struct in C and import it into Swift for Now.
和later in that discussion:
You can leave the struct defined in C and import it into Swift. Swift will respect C’s layout.
例:
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中定义相同的结构
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是在结构CA sa的尾部填充之后布局的.
从Swift 3开始,尺寸和步幅都返回相同的值
从C导入的结构(包括struct padding),
即与C中的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,c: 0xcc) showMemory(&ca) // <aa000000 bbbbbbbb cc000000> var cb = CB(ca: ca,d: 0xdd) showMemory(&cb) // <aa000000 bbbbbbbb cc000000 dd000000>