2015-03-09 32 views
7

Dzisiaj napotkałem problem ze strukturą Swift. Patrz następujące struct na przykład:Swift oblicza nieprawidłowy rozmiar struktury

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

I że wielkość tej struktury w sumie 6 bajtów jest, ponieważ Uint16 wynosi 2 bajtów i UInt32 wynosi 4 bajty duża.

następujący kod drukuje 6 (która jest poprawna):

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

Ale gdybym sprawdzić rozmiar struktury z

println(sizeof(TestStruct)) 

drukuje 8, a nie 6.

Jeśli spróbuję tego samego z następującą strukturą:

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

Następnie

println(sizeof(TestStruct2)) 

drukuje poprawną wartość: 6. Ale wielkość TestStruct powinna być taka sama jak wielkość TestStruct2.

Dlaczego pierwsza struktura ma 8 bajtów? Czy robię coś nie tak, czy to błąd?

(test z Xcode 6.1.1 (6A2008a) na OS X Indywidualistów, Zastosowanie linii normalnej polecenie Mac)

+0

Google "wyrównanie struktury", a zobaczysz przyczynę. To ogólne zachowanie. Na przykład: https://msdn.microsoft.com/en-us/library/71kf49f1.aspx –

+0

Porównaj http://stackoverflow.com/questions/28898277/why-does-some-types-float80-have-a- wyrównanie pamięci - większy niż rozmiar słowa. –

+0

Dzięki za wskazanie mnie we właściwym kierunku! Nie wiedziałem o tym, przepraszam. Wciąż jestem nowy w językach niższego poziomu, takich jak C i Objective-C. Ale jak mogę uzyskać dane z NSData do tej wyrównanej struktury? Jeśli używam data.getBytes (& aStruct, length: sizeof (ASctruct)), to mam błędne wartości. – user4650218

Odpowiedz

8

To dlatego Swift wyrównuje UInt32 pola na granicy 4 bajtów poprzez wstawienie dwubajtowych " luka "między UInt16 i UInt32. Jeśli przełącznik pola tak, że 32-bitowe pole przychodzi pierwszy, luka zostanie wyeliminowane:

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 

Można również umieścić inną UInt16 do tej luki bez zmiany rozmiaru struktury:

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

Ale jak mogę uzyskać dane z NSData w tej dopasowanej strukturze? Jeśli używam data.getBytes(&aStruct, length: sizeof(AStruct)), mam złe wartości.

Niekoniecznie: jeśli zapisane AStruct stosując tę ​​samą metodę, a następnie luki będzie tam w NSData, tak więc uzyskanie danych binarnych będzie jak umieszczenie „cookie cutter” na ampułko-tablicy bajtów z tym samym wyrównaniem.

+0

Dzięki! Ale co, jeśli nie mogę zmienić pól. Weźmy na przykład rekord zasobu DNS, który ma 16-bitową nazwę/wskaźnik, typ 16-bitowy i 16-bitowy, a następnie 32-bitowe pole ttl, następnie pole 16-bitowe i kilka innych pól.Jeśli mam "dane" dla tego rekordu w obiekcie NSData, jak mogę zrobić coś takiego jak 'data.getBytes (& aStruct, length: sizeof (ASctruct))'? Obecnie otrzymuję błędne wartości ttl i długość z powodu tego wyrównania. – user4650218

+3

Interesujący fakt (myślę) jest to, że funkcja Swift 'sizeof()' nie * zawiera * dopełnianie * strukturalnego wypełniania - więc różni się od C 'sizeof'. Do tego potrzebny jest 'strideof()'. Zostało to omówione na stronie https://devforums.apple.com/message/1086107#1086107. –

+0

@ user4650218 Jeśli format danych jest określony dla ciebie na poziomie bajtów, nie powinieneś próbować umieszczać go bezpośrednio w tekstowej strukturze danych. Weź tablicę bajtów i zinterpretuj swoją strukturę po jednym polu na raz: odczytaj pierwszy 16 bit, zinterpretuj go jako nazwę; następnie zrób następny 16 bit i zinterpretuj go jako typ; Kontynuuj, aż cała struktura zostanie zinterpretowana. Możesz przypisać pola, które czytasz do pól w 'struct', ale po jednym na raz. – dasblinkenlight