一尘不染

Swift是否保证类和结构中字段的存储顺序?

swift

在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类和结构中的字段是否同样保证了存储顺序?或者(鉴于该语言不支持与列出的其他指针相同的指针),编译器是否在编译时为您最佳地重新安排了指针?


阅读 217

收藏
2020-07-07

共1个答案

一尘不染

是的,结构元素在内存中的顺序就是它们声明的顺序。详细信息可以在“
类型布局”
(已添加重点)中找到。但是请注意使用“当前”,因此在以后的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中定义相同的结构

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

从Swift 3开始,对于 从C导入 的结构,两者size和都stride返回相同的值( 包括 结构填充)
sizeof返回与C中相同的值。

这是一个简单的函数,有助于演示以上内容(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>
2020-07-07