在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类和结构中的字段是否同样保证了存储顺序?或者(鉴于该语言不支持指针与列出的其他指针相同),编译器是否在编译时为您重新安排它们?
答案 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中。
您可以保留在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开始,size
和stride
都返回相同的值
(包括结构填充)用于从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
仍然正在进行调整。 c
和b
之间丢失了8位。
当然,目前尚不清楚这些位是否实际位于c
和...之间。 b
或刚刚结束...直到我们重新排列:
struct s {
var a: UInt32
var b: UInt64
var c: UInt8
}
sizeof(s) // 17
我认为此行为与您在问题中提到的其他语言相符。