2016-09-03 16 views
6

Trong C, thứ tự mà bạn xác định các trường trong cấu trúc là thứ tự mà chúng sẽ được khởi tạo trong bộ nhớ. Có tính đến sự liên kết bộ nhớ tài khoản, cấu trúc sau sẽ có kích thước 8 byte trong bộ nhớ như được hiển thị, nhưng chỉ 6 byte nếu các trường được đảo ngược vì không cần phải có bất kỳ đệm căn chỉnh nào.Swift có đảm bảo thứ tự lưu trữ của các trường trong các lớp và cấu trúc không?

struct s { 
    int32_t a; 
    /* 2 bytes of padding to align a 64 bit integer */ 
    int64_t b; 
} 

Đảm bảo đặt hàng này có trong các cấu trúc C, lớp C++ (và cấu trúc) và lớp Objective-C.

Thứ tự lưu trữ có được đảm bảo tương tự cho các trường trong lớp và cấu trúc Swift không? Hoặc (với điều kiện ngôn ngữ không hỗ trợ con trỏ theo cùng cách với các ngôn ngữ khác được liệt kê), trình biên dịch có tối ưu sắp xếp lại chúng cho bạn tại thời gian biên dịch không?

+0

Tại sao bạn dựa vào vị trí của các thuộc tính của một đối tượng? Bạn muốn làm gì? – FredericP

+0

Tôi không muốn làm bất cứ điều gì với nó đặc biệt, nó chỉ là khi bạn bắt đầu để nhanh chóng [m | b] illions của cấu trúc nhỏ, sự khác biệt bắt đầu để thêm lên, đặc biệt là trên các thiết bị di động. – Ephemera

Trả lời

12

Có, thứ tự của các phần tử cấu trúc trong bộ nhớ là thứ tự của tuyên bố của chúng. Chi tiết có thể được tìm thấy trong The Swift ABI (được thêm vào). Tuy nhiên lưu ý việc sử dụng "hiện", do đó này có thể thay đổi trong một phiên bản tương lai của Swift:

Fragile Struct và tuple Layout

Struct và tuples hiện phần thuật toán bố trí tương tự, lưu ý là thuật toán bố cục "Phổ" trong việc triển khai trình biên dịch. Thuật toán như sau:

  • Bắt đầu với một kích thước từ 0 và một liên kết của 1.
  • Duyệt qua các lĩnh vực, theo thứ tự nguyên tố cho các bộ, hoặc trong var để khai cho cấu trúc. Đối với mỗi lĩnh vực:
    • Cập nhật kích thước bằng cách làm tròn lên đến sự liên kết của lĩnh vực này, có nghĩa là, tăng nó với giá trị tối thiểu lớn hơn hoặc bằng kích thước và chia hết cho sự liên kết của lĩnh vực này.
    • Gán độ lệch của trường cho giá trị hiện tại của kích thước.
    • Cập nhật kích thước bằng cách thêm kích thước của trường.
    • Cập nhật căn chỉnh với mức tối đa là căn chỉnh và căn chỉnh.
  • Kích thước và căn chỉnh cuối cùng là kích thước và căn chỉnh của tổng hợp. Bước đi của loại là kích thước cuối cùng được làm tròn lên để căn chỉnh.

Các padding/phù hợp là khác nhau từ C:

Lưu ý rằng điều này khác với quy tắc bố trí bình thường C hoặc LLVM trong đó kích thước và sải chân là khác biệt; trong khi bố cục C yêu cầu kích thước của một cấu trúc nhúng được chèn vào căn chỉnh của nó và không có gì được đặt ở đó, bố cục Swift cho phép cấu trúc bên ngoài bố trí các trường trong phần đuôi của cấu trúc bên trong, cho phép căn chỉnh.

Chỉ khi một struct là nhập khẩu từ C sau đó nó được đảm bảo để có cách bố trí bộ nhớ tương tự. Joe Groff từ Apple viết tại [swift-users] Mapping C semantics to Swift

Nếu bạn phụ thuộc vào bố cục cụ thể, bạn nên xác định cấu trúc trong C và nhập nó vào Swift ngay bây giờ.

later in that discussion:

Bạn có thể để lại các cấu trúc được định nghĩa trong C và nhập nó vào Swift. Swift sẽ tôn trọng bố cục của C.

Ví dụ:

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 

Đây var d: UInt8 được đặt ra trong đệm đuôi của var sa: A. Nếu bạn xác định các cấu trúc tương tự trong C

struct CA { 
    uint8_t a; 
    uint32_t b; 
    uint8_t c; 
}; 

struct CB { 
    struct CA ca; 
    uint8_t d; 
}; 

và nhập nó vào Swift sau đó

// 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 được đặt ra sau khi đệm đuôi của struct CA sa.

Tính Swift 3, cả hai sizestride trả về giá trị cùng (bao gồm struct đệm) cho các cấu trúc nhập khẩu từ C, ví dụ: giá trị tương tự như sizeof trong C sẽ trở lại.

Đây là một chức năng đơn giản giúp để chứng minh ở trên (Swift 3):

func showMemory<T>(_ ptr: UnsafePointer<T>) { 
    let data = Data(bytes: UnsafeRawPointer(ptr), count: MemoryLayout<T>.size) 
    print(data as NSData) 
} 

Các cấu trúc quy định tại 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ác cấu trúc nhập khẩu từ 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

Rất toàn diện, cảm ơn bạn rất nhiều! – Ephemera

1

Dường như đơn hàng được đảm bảo.

Với struct sau:

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

sizeof(s) // 16 

Alignment vẫn xảy ra. Có 8 bit bị mất ở giữa cb.

Tất nhiên, nó không rõ liệu các bit thực sự giữa c & b hoặc chỉ tacked ở cuối ... cho đến khi chúng tôi sắp xếp lại:

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

sizeof(s) // 17 

tôi tin rằng hành vi này phù hợp với các ngôn ngữ khác mà bạn đề cập trong câu hỏi của bạn.

+0

"Có 8 bit bị mất ..." Tôi nghĩ bạn có nghĩa là 3 byte. – RenniePet

Các vấn đề liên quan