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

时间:2016-09-03 02:58:42

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

2 个答案:

答案 0 :(得分:17)

是的,内存中struct元素的顺序是 他们的声明。细节可以找到 在Type Layout (重点补充)。但请注意"当前"的使用,所以这个 可能会在未来的Swift版本中发生变化:

  

脆弱的结构和元组布局

     

结构和元组目前共享相同的布局算法,标记为" Universal"编译器实现中的布局算法。算法如下:

     
      
  • 从0开始,对齐为1。
  •   
  • 迭代通过   字段,按元组的元素顺序或 in var declaration order for   结构。对于每个领域:      
        
    • 通过向上舍入到对齐来更新大小   该领域,即将其增加到最大值或更大   等于大小,并且可以被场地的对齐整除。
    •   
    • 将字段的偏移量指定为当前的大小值。
    •   
    • 更新   通过添加字段大小来确定大小。
    •   
    • 将对齐更新为最大值   对齐和场的对齐。
    •   
  •   
  • 最终尺寸和对齐方式   是聚合的大小和对齐方式。这种类型的步幅是   最终尺寸四舍五入到对齐。
  •   

填充/对齐与C:

不同
  

请注意,这与C或LLVM的正常布局规则不同,因为该尺寸和步幅是不同的;而C布局要求嵌入式结构的大小填充到其对齐并且没有任何布局,Swift布局允许外部结构在内部结构的尾部填充中布置字段,对齐允许

只有当结构从C导入时才能保证具有 相同的内存布局。来自Apple的Joe Groff写道 [swift-users] Mapping C semantics to Swift

  

如果您依赖于特定的布局,则应该在C中定义结构并将其导入到Swift中。

later in that discussion

  

您可以保留在C中定义的结构并将其导入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开始,sizestride都返回相同的值 (包括结构填充)用于从C,导入的结构 即与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, b: 0xbbbbbbbb, c: 0xcc)
showMemory(&ca)   // <aa000000 bbbbbbbb cc000000>

var cb = CB(ca: ca, d: 0xdd)
showMemory(&cb)   // <aa000000 bbbbbbbb cc000000 dd000000>

答案 1 :(得分:1)

订单似乎有保证。

给出以下结构:

struct s {
    var a: UInt32
    var c: UInt8
    var b: UInt64
}

sizeof(s) // 16

仍然正在进行调整。 cb之间丢失了8位。

当然,目前尚不清楚这些位是否实际位于c和...之间。 b或刚刚结束...直到我们重新排列:

struct s {
    var a: UInt32
    var b: UInt64
    var c: UInt8
}

sizeof(s) // 17

我认为此行为与您在问题中提到的其他语言相符。