2015-03-09 2 views
7

Сегодня у меня возникла проблема с структурами Swift. См следующую структуру: напримерSwift вычисляет неправильный размер структуры

struct TestStruct { 
    var test: UInt16 
    var test2: UInt32 
} 

Я хотел бы предположить, что размер этой структуры в общей сложности 6 байт, так как UInt16 2 байта и UInt32 составляет 4 байта большой.

Следующий код печатает 6 (что правильно):

println(sizeof(UInt32) + sizeof(UInt16)) 

Но если я проверить размер структуры с

println(sizeof(TestStruct)) 

печатает 8, а не 6.

Если я попробую сделать то же самое со следующей структурой:

struct TestStruct2 { 
    var test: UInt16 
    var test2: UInt16 
    var test3: UInt16 
} 

Тогда

println(sizeof(TestStruct2)) 

печатает правильное значение: 6. Но размер TestStruct должен быть таким же, как размер TestStruct2.

Почему первая структура размером 8 байтов? Я делаю что-то неправильно или это ошибка?

(протестировано с Xcode 6.1.1 (6A2008a) на OS X Mavericks, нормальные командная строка приложение для Mac)

+0

Google «выравнивание структуры», и вы увидите причину. Это общее поведение. Например: https://msdn.microsoft.com/en-us/library/71kf49f1.aspx –

+0

Сравните http://stackoverflow.com/questions/28898277/why-does-some-types-float80-have-a- память выравнивания больше, чем слова размера. –

+0

Спасибо, что указал мне в правильном направлении! Я не знал об этом, извините. Я все еще новичок в языках более низкого уровня, таких как C и Objective-C. Но тогда, как я могу получить данные из NSData в эту выровненную структуру? Если я использую data.getBytes (& aStruct, length: sizeof (AStruct)), то у меня неправильные значения. – user4650218

ответ

8

Это происходит потому, что Swift выравнивает UInt32 поля на границе 4 байт, вставив двухбайтный " разрыв "между UInt16 и UInt32. При переключении поля, так что 32-битовое поле приходит первым, разрыв будет ликвидирован:

struct TestStruct1 { 
    var test: UInt16 
    var test2: UInt32 
} 
println(sizeof(TestStruct1)) // <<== Prints 8 

struct TestStruct2 { 
    var test2: UInt32 
    var test: UInt16 
} 
println(sizeof(TestStruct2)) // <<== Prints 6 

Вы можете также поместить другой UInt16 в этот промежуток, не изменяя размер вашей структуры:

struct TestStruct3 { 
    var test0: UInt16 
    var test1: UInt16 
    var test2: UInt32 
} 
println(sizeof(TestStruct3)) // <<== Prints 8 

Но тогда, как я могу получить данные от NSData в эту выровненную структуру? Если я использую data.getBytes(&aStruct, length: sizeof(AStruct)), то у меня неправильные значения.

Не обязательно: если вы сохранили AStruct, используя тот же метод, то зазоры будут там в NSData, так что получение ваших двоичных данных будет как размещение «резак печенья» на предварительно заполненный массив байтов с тем же выравниванием.

+0

Спасибо! Но что, если я не могу переключать поля. Например, возьмите запись ресурса DNS, она имеет 16-битное имя/указатель, 16-битный тип и 16-битный класс, а затем 32-битное поле ttl, затем поле длиной 16 бит и еще несколько полей.Если у меня есть «данные» для этой записи в объекте NSData, как я могу сделать что-то вроде 'data.getBytes (& aStruct, length: sizeof (AStruct))? В настоящее время я получаю неправильные значения для ttl и длины из-за этого выравнивания. – user4650218

+3

Интересным фактом (я думаю) является то, что функция Swift 'sizeof()' does * not * включает * trailing * struct padding, поэтому она отличается от C 'sizeof'. Для этого нужен 'strideof()'. Это обсуждалось в https://devforums.apple.com/message/1086107#1086107. –

+0

@ user4650218 Если формат данных указан для вас на уровне байта, вы не должны пытаться поместить его в типизированную структуру данных напрямую. Возьмите массив байтов и интерпретируйте свою структуру по одному полю за раз: прочитайте первый 16 бит, интерпретируйте его как имя; затем возьмите следующий 16 бит и интерпретируйте его как тип; до тех пор, пока не будет интерпретирована вся структура. Вы можете назначать поля, которые вы читаете, полям в вашей 'struct', но по одному за раз. – dasblinkenlight

Смежные вопросы