Swift 是否保证类和结构中字段的存储顺序? [英] Does Swift guarantee the storage order of fields in classes and structs?
问题描述
在 C 中,您在结构中定义字段的顺序就是它们在内存中实例化的顺序.考虑到内存对齐,如图所示,以下结构体在内存中的大小为 8 个字节,但如果字段颠倒,则只有 6 个字节,因为不需要任何对齐填充.
In C, the order in which you define fields in a struct is the order in which they will be instantiated in memory. Taking into account memory alignment, the following struct will have a size of 8 bytes in memory as shown, but only 6 bytes if the fields are reversed as there doesn't need to be any alignment padding.
struct s {
int32_t a;
/* 2 bytes of padding to align a 64 bit integer */
int64_t b;
}
这种顺序保证存在于 C 结构、C++ 类(和结构)和 Objective-C 类中.
This ordering guarantee is present in C structs, C++ classes (and structs), and Objective-C classes.
Swift 类和结构体中的字段的存储顺序是否同样得到保证?或者(鉴于该语言不支持与其他语言相同的指针),编译器是否会在编译时以最佳方式为您重新排列它们?
Is the order of storage similarly guaranteed for fields in Swift classes and structs? Or (given that the language doesn't support pointers in the same way as the others listed), does the compiler optimally re-arrange them for you at compile-time?
推荐答案
是的,struct元素在内存中的顺序就是他们的声明.详情可见在 类型布局(强调).但是请注意当前"的使用,所以这个在 Swift 的未来版本中可能会发生变化:
Yes, the order of the struct elements in memory is the order of their declaration. The details can be found in Type Layout (emphasis added). Note however the use of "currently", so this may change in a future version of Swift:
脆弱的结构和元组布局
结构和元组目前共享相同的布局算法,在编译器实现中被称为通用"布局算法.算法如下:
Structs and tuples currently share the same layout algorithm, noted as the "Universal" layout algorithm in the compiler implementation. The algorithm is as follows:
- 从大小 0 和对齐方式 1 开始.
- 遍历字段,元组的元素顺序,或 var 声明顺序结构.对于每个字段:
- 通过四舍五入对齐来更新大小的字段,即,将其增加到最小值大于或等于大小并且可以被字段的对齐整除.
- 将字段的偏移量分配给 size 的当前值.
- 更新通过添加字段的大小来调整大小.
- 更新对齐到最大值对齐和字段对齐.
填充/对齐方式与 C 不同:
The padding/alignment is different from C:
请注意,这与 C 或 LLVM 的正常布局规则的不同之处在于大小和步幅不同;而 C 布局要求嵌入结构的大小填充到其对齐方式并且没有任何布局,Swift 布局允许外部结构在内部结构的尾部填充中布置字段,对齐允许.
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] 将 C 语义映射到 Swift
Only if a struct is imported from C then it is guaranteed to have the same memory layout. Joe Groff from Apple writes at [swift-users] Mapping C semantics to Swift
如果你依赖于一个特定的布局,你应该在 C 中定义结构体并暂时将其导入 Swift.
If you depend on a specific layout, you should define the struct in C and import it into Swift for now.
和 稍后讨论一个>:
您可以保留在 C 中定义的结构并将其导入 Swift.Swift 会尊重 C 的布局.
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 中定义相同的结构Here
var d: UInt8
is layed out in the tail padding ofvar sa: A
. If you define the same structures in Cstruct CA { uint8_t a; uint32_t b; uint8_t c; }; struct CB { struct CA ca; uint8_t d; };
然后将其导入 Swift
and import it to Swift then
// 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
的尾部填充之后布局的.because
uint8_t d
is layed out after the tail padding ofstruct CA sa
.从 Swift 3 开始,
size
和stride
都返回相同的值(包括结构填充)用于从 C 导入的结构,即返回与 C 中的sizeof
相同的值.As of Swift 3, both
size
andstride
return the same value (including the struct padding) for structures imported from C, i.e. the same value assizeof
in C would return.这是一个有助于演示上述内容的简单函数(Swift 3):
Here is a simple function which helps to demonstrate the above (Swift 3):
func showMemory<T>(_ ptr: UnsafePointer<T>) { let data = Data(bytes: UnsafeRawPointer(ptr), count: MemoryLayout<T>.size) print(data as NSData) }
Swift 中定义的结构:
The structures defined in 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>
这篇关于Swift 是否保证类和结构中字段的存储顺序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!