当前位置: 首页 > 面试题库 >

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

刘辰钊
2023-03-14
问题内容

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


问题答案:

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


 类似资料:
  • 主要内容:图的顺序存储结构C语言实现使用图结构表示的数据元素之间虽然具有“多对多”的关系,但是同样可以采用顺序存储,也就是使用数组有效地存储图。 使用数组存储图时,需要使用两个数组,一个数组存放图中顶点本身的数据(一维数组),另外一个数组用于存储各顶点之间的关系(二维数组)。 存储图中各顶点本身数据,使用一维数组就足够了;存储顶点之间的关系时,要记录每个顶点和其它所有顶点之间的关系,所以需要使用二维数组。 不同类型的图,存储的方式略

  • 主要内容:顺序表的初始化顺序表,全名 顺序存储结构,是 线性表的一种。通过《 什么是线性表》一节的学习我们知道,线性表用于存储逻辑关系为“一对一”的数据,顺序表自然也不例外。 不仅如此,顺序表对数据的物理存储结构也有要求。 顺序表存储数据时,会提前申请一整块足够大小的物理空间,然后将数据依次存储起来,存储时做到数据元素之间不留一丝缝隙。 例如,使用顺序表存储集合  ,数据最终的存储状态如图 1 所示: 图 1 顺序存储结

  • 问题内容: Elasticsearch是否保持多值字段的顺序? 即,如果我在字段中输入了以下值: (鉴于未分析字段) 我是否可以确定列表的内容将始终按照与放置列表相同的顺序返回? 在上面的示例中,我想确保“值”中第一个位置的“一个”将始终与“ values_original”等中的“ 1”相对应。 我也可以将其保留为嵌套对象,即 但我想避免开销。 如果可以保证保留多值字段中的值顺序,那么我保留两个

  • 二叉树的存储结构有两种,分别为顺序存储和链式存储。本节先介绍 二叉树的顺序存储结构。 二叉树的顺序存储,指的是使用 顺序表(数组)存储二叉树。需要注意的是,顺序存储只适用于完全二叉树。换句话说,只有完全二叉树才可以使用顺序表存储。 因此,如果我们想顺序存储普通二叉树,需要提前将普通二叉树转化为完全二叉树。 有读者会说,满二叉树也可以使用顺序存储。要知道,满二叉树也是完全二叉树,因为它满足完全二叉树

  • 问题内容: 我正在将JSON用户对象发布到我的Golang应用程序中,在其中我将“ req.body”解码为“ User”结构。 和“用户”结构: 虽然我不需要实际验证方面的帮助,但我想知道如何仅在用户名作为JSON对象的一部分包含时验证用户名。目前,如果不包含用户名,则该用户名仍然存在,但为空,即 如何检查是否已将“用户名”作为POSTed对象的一部分? 问题答案: 您可以使用指向字符串的指针:

  • 问题内容: 我特别想知道PostgreSQL。给出以下人为的示例: 从外部查询返回的名称是否保证与内部查询的顺序相同? 问题答案: 不,在外部查询中按以下顺序排序: 内部(子)查询返回结果集。如果按此顺序进行排序,则保证从内部(子)查询传递到外部查询的中间结果集将按照您指定的方式进行排序,但是在外部查询中不对结果进行排序通过处理内部查询结果集生成的结果集,不能保证以任何方式进行排序。