2015-03-09 21 views
7

Oggi ho riscontrato un problema con le strutture di Swift. Vedere il seguente struct per esempio:Swift calcola la dimensione errata della struttura

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

presumo che la dimensione di questa struct in totale è di 6 byte, perché UInt16 è 2 byte e UInt32 è 4 byte grande.

stampe il seguente codice 6 (che è corretto):

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

Ma se controllo la dimensione della struttura con

println(sizeof(TestStruct)) 

esso stampa 8 e non 6.

Se provo lo stesso con la seguente struttura:

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

Poi

println(sizeof(TestStruct2)) 

stampa il valore corretto: 6. Ma la dimensione del TestStruct dovrebbe essere lo stesso come la dimensione della TestStruct2.

Perché la prima struct 8 byte è grande? Sto facendo qualcosa di sbagliato o si tratta di un bug?

(testato con Xcode 6.1.1 (6A2008a) su OS X Mavericks, comando normale applicazione linea per Mac)

+0

Google "struct alignment" e vedrai il motivo. È un comportamento generale. Ad esempio: https://msdn.microsoft.com/en-us/library/71kf49f1.aspx –

+0

Confronta http://stackoverflow.com/questions/28898277/why-does-some-types-float80-have-a- memory-allineamento-grande-che-word-size. –

+0

Grazie per avermi indicato nella giusta direzione! Non ero a conoscenza di questo, mi dispiace. Sono ancora nuovo per linguaggi di livello inferiore come C e Objective-C. Ma poi, come posso ottenere dati da NSData in questa struttura allineata? Se utilizzo data.getBytes (e aStruct, length: sizeof (AStruct)), ho valori errati. – user4650218

risposta

8

Questo perché Swift allinea UInt32 campo ad un limite di 4 byte inserendo due byte " gap "tra UInt16 e UInt32. Se si passa i campi in modo che il campo a 32 bit viene prima, il divario sarebbe eliminata:

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 

È anche possibile inserire un altro UInt16 in quel divario senza modificare le dimensioni della vostra struttura:

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

Ma come posso ottenere i dati da NSData in questa struttura allineata? Se utilizzo data.getBytes(&aStruct, length: sizeof(AStruct)), ho valori errati.

Non necessariamente: se è stato salvato AStruct con lo stesso metodo, quindi le lacune sarebbero lì in NSData pure, ottenendo così i dati binari sarebbe come mettere "un cookie cutter" sulla matrice pre-riempita di byte con lo stesso allineamento.

+0

Grazie! Ma cosa succede se non posso cambiare i campi. Prendiamo ad esempio il record di risorse DNS, ha un nome/puntatore a 16 bit, un tipo a 16 bit e una classe a 16 bit seguito da un campo TTL a 32 bit, quindi un campo a 16 bit e altri campi.Se ho i "dati" per questo record in un oggetto NSData, come posso fare qualcosa come 'data.getBytes (& aStruct, length: sizeof (AStruct))'? Attualmente ottengo valori errati per ttl e lunghezza a causa di questo allineamento. – user4650218

+3

Un fatto interessante (penso) è che la funzione 'sizeof()' di Swift non * include * il trailing * struct padding - quindi è diverso dal C 'sizeof'. Per questo è necessario 'strideof()'. Questo è stato discusso in https://devforums.apple.com/message/1086107#1086107. –

+0

@ user4650218 Se il formato dei dati è specificato per voi a livello di byte, non provare a inserirlo direttamente in una struttura dati digitato. Prendi la matrice di byte e interpreta la tua struttura un campo alla volta: leggi il primo 16 bit, interpretalo come il nome; poi prendi il successivo 16 bit e interpretalo come un tipo; continuare fino all'interpretazione dell'intera struttura. Puoi assegnare campi che hai letto ai campi nella tua 'struct', ma uno alla volta. – dasblinkenlight

Problemi correlati