Ottenere diverse dimensioni dell'intestazione modificando le dimensioni della finestra
Domanda
Ho un programma C ++ che rappresenta un'intestazione TCP come struct:
#include "stdafx.h"
/* TCP HEADER
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
typedef struct { // RFC793
WORD wSourcePort;
WORD wDestPort;
DWORD dwSequence;
DWORD dwAcknowledgment;
unsigned int byReserved1:4;
unsigned int byDataOffset:4;
unsigned int fFIN:1;
unsigned int fSYN:1;
unsigned int fRST:1;
unsigned int fPSH:1;
unsigned int fACK:1;
unsigned int fURG:1;
unsigned int byReserved2:2;
unsigned short wWindow;
WORD wChecksum;
WORD wUrgentPointer;
} TCP_HEADER, *PTCP_HEADER;
int _tmain(int argc, _TCHAR* argv[])
{
printf("TCP header length: %d\n", sizeof(TCP_HEADER));
return 0;
}
Se eseguo questo programma ottengo la dimensione di questa intestazione come 24 byte, che non è la dimensione che mi aspettavo. Se cambio il tipo di campo & Quot; wWindow & Quot; a " unsigned int wWindow: 16 " ;, che ha lo stesso numero di bit di un short senza segno, il programma mi dice che la dimensione della struttura è ora di 20 byte, la dimensione corretta. Perché questo?
Sto utilizzando Microsoft Visual Studio 2005 con SP1 su una macchina x86 a 32 bit.
Soluzione
Vedi questa domanda: Perché sizeof per una struttura non è uguale alla somma di sizeof di ciascun membro? .
Credo che il compilatore prenda un suggerimento per disabilitare il riempimento quando si usa " unsigned int wWindow: 16 " sintassi.
Inoltre, si noti che non è garantito che un corto sia di 16 bit. La garanzia è che: 16 bit & Lt; = size of a short & Lt; = size of an int.
Altri suggerimenti
Perché il compilatore sta comprimendo il tuo bitfield in un int a 32 bit, non in un'entità a 16 bit.
In generale dovresti evitare i bitfield e usare altre costanti manifest (enum o quant'altro) con mascheramento e spostamento dei bit espliciti per accedere ai "sottocampi" in un campo.
Ecco un motivo per cui i bitfield dovrebbero essere evitati: non sono molto portatili tra i compilatori anche per la stessa piattaforma. dallo standard C99 (c'è una formulazione simile nello standard C90):
Un'implementazione può allocare qualsiasi unità di archiviazione indirizzabile abbastanza grande per tenere un bitfield. Se abbastanza spazio rimane, un bit-field che immediatamente segue un altro campo bit in a la struttura deve essere imballata in bit adiacenti della stessa unità. Se rimane spazio insufficiente, sia a viene inserito un campo bit che non si adatta nell'unità successiva o si sovrappone unità adiacenti è implementazione definita. L'ordine di allocazione di campi bit all'interno di un'unità (da alto a basso o basso all'ordine elevato) è implementazione definita. L'allineamento dell'unità di archiviazione indirizzabile è non specificato.
Non puoi garantire se un campo di bit 'si estenda' su un limite di int o no e non puoi specificare se un campo di bit inizia dalla parte bassa dell'int o dalla parte alta dell'int (questo è indipendente dal fatto che il processore è big-endian o little-endian).
La tua serie di " unsigned int: xx " i bitfield utilizzano solo 16 dei 32 bit in un int. Gli altri 16 bit (2 byte) sono presenti, ma non utilizzati. Questo è seguito dal corto senza segno, che si trova su un confine int, e quindi da una WORD, che è allineata su un confine int, il che significa che ci sono 2 byte di riempimento tra di loro.
Quando passi a " unsigned int wWindow: 16 " ;, invece di essere un corto separato, il compilatore usa le parti inutilizzate del bitfield precedente, quindi niente spreco, niente corto e no imbottitura dopo il corto, quindi risparmi quattro byte.
Il compilatore sta riempiendo il membro struct non bitfield a 32 bit - allineamento delle parole nativo. Per risolvere questo problema, esegui #pragma pack (0) prima di struct e #pragma pack () dopo.
I limiti di Struct in memoria possono essere riempiti dal compilatore in base alla dimensione e all'ordine dei campi.
Non è un esperto C / C ++ per quanto riguarda l'imballaggio. Ma immagino che ci sia una regola nella specifica che dice che quando un non-bitfield segue un bitfield deve essere allineato sul confine della parola indipendentemente dal fatto che si adatti o meno allo spazio rimanente. Rendendolo un bitvector esplicito stai evitando questo problema.
Anche in questo caso si tratta di speculazioni con un tocco di esperienza.
Interessante - Penso che " WORD " valuterebbe " unsigned short " ;, quindi avresti quel problema in più di un posto.
Inoltre, tieni presente che dovrai affrontare i problemi di endian con qualsiasi valore superiore a 8 bit.
Penso che Mike B abbia capito bene, ma non è del tutto chiaro. Quando chiedi & Quot; short & Quot ;, è allineato sul confine a 32 bit. Quando chiedi int: 16, non lo è. Quindi int: 16 si inserisce subito dopo i campi ebit, mentre short salta 2 byte e inizia al successivo blocco a 32 bit.
Il resto di ciò che sta dicendo è perfettamente applicabile - il campo bit non deve mai essere usato per codificare una struttura visibile esternamente, perché non ci sono garanzie su come siano allocati. Nella migliore delle ipotesi, appartengono a programmi integrati in cui è importante salvare un byte. E anche lì, non puoi usarli per controllare effettivamente i bit nelle porte mappate in memoria.
Stai visualizzando valori diversi a causa delle regole di imballaggio del compilatore. Puoi vedere le regole specifiche per Visual Studio qui .
Quando si dispone di una struttura che deve essere imballata (o aderire ad alcuni requisiti di allineamento specifici), è necessario utilizzare l'opzione #pragma pack (). Per il tuo codice, puoi usare #pragma pack (0) che allineerà tutti i membri della struttura sui confini dei byte. È quindi possibile utilizzare #pragma pack () per reimpostare l'imballaggio della struttura allo stato predefinito. Puoi vedere maggiori informazioni sul pacchetto pragma qui .