Får annerledes header størrelse ved å endre størrelsen på vinduet

stemmer
7

Jeg har en C ++ program som representerer en TCP header som en 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;
}

Hvis jeg kjører dette programmet får jeg størrelsen på denne overskriften som 24 bytes, som ikke er størrelsen jeg hadde forventet. Hvis jeg endre type feltet wWindow til unsigned int wWindow: 16, som har samme antall biter som en usignert kort, forteller programmet meg størrelsen på struct er nå 20 bytes, riktig størrelse. Hvorfor er det sånn?

Jeg bruker Microsoft Visual Studio 2005 SP1 på en 32-bits x86-maskin.

Publisert på 29/09/2008 klokken 18:21
kilden bruker
På andre språk...                            


9 svar

stemmer
6

Fordi kompilatoren er pakking din bitfield inn i en 32-bits int, ikke en 16-bit enhet.

Generelt bør man unngå bitfields og bruke andre åpen konstanter (enums eller hva som helst) med eksplisitt bits maskering og forskyvning for å få tilgang til de 'sub-felt' i et felt.

Her er en grunn til at bitfields bør unngås - de er ikke veldig portabel mellom kompilatorer selv for samme plattform. fra C99 standard (det er lik ordlyden i C90 standard):

En implementering kan allokere en adresserbar lagerenhet stor nok til å romme en bitfield. Hvis tilstrekkelig plass forblir skal en bit-felt som følger umiddelbart etter en annen bit-felt i en struktur som skal pakkes inn i tilstøtende biter i den samme enhet. Hvis utilstrekkelig plass fremdeles, hvorvidt en bit-felt som ikke passer blir satt inn i den neste enhet eller overlapper tilstøtende enheter, er implementerings definert. Rekkefølgen for tildeling av bit-felter i en enhet (høy orden til lav-orden eller av lav orden til høy orden), er implementerings definert. Justeringen av det adresserbare lagringsenheten er uspesifisert.

Du kan ikke garantere om litt felt vil 'span' en int grense eller ikke, og du kan ikke angi om en bitfield starter i low-end av int eller den høye enden av int (dette er uavhengig av om prosessoren er big-endian eller lite endian).

Svarte 29/09/2008 kl. 18:24
kilden bruker

stemmer
4

Din rekke "unsigned int: xx" bitfields bruke opp bare 16 av de 32 bits i en int. De andre 16 bits (2 bytes) er der, men ubrukt. Dette etterfølges av den usignerte kort, som er på et int grense, og deretter et ord som er innrettet langs med en int grense, som betyr at det 2 byter med polstring mellom dem.

Når du bytter til "unsigned int wWindow: 16", i stedet for å være et eget kort, bruker kompilatoren ubrukte deler av forrige bitfield, så ingen avfall, ingen kort, og ingen padding etter kort, dermed sparer du fire byte.

Svarte 29/09/2008 kl. 19:20
kilden bruker

stemmer
2

Se på dette spørsmålet: Hvorfor er ikke sizeof for en struct lik summen av sizeof av hvert medlem? .

Jeg tror at kompilatoren tar et hint for å deaktivere padding når du bruker "unsigned int wWindow: 16" syntaks.

Vær også oppmerksom på at en kort ikke er garantert å være 16 bits. Garantien er at: 16 bits <= størrelsen av et kort <= størrelsen av et int.

Svarte 29/09/2008 kl. 18:28
kilden bruker

stemmer
0

Jeg tror Mike B fikk den rett, men, men ikke helt klart. Når du spør om "kort", er det justert på 32bit grense. Når du ber om int: 16, er det ikke. Så int: 16 passer rett etter th EBIT felt, mens korte hopper 2 bytes og starter på neste 32-bit blokk.

Resten av det han sier er helt relevant - bit feltet må aldri brukes til å kode et eksternt synlig struktur, fordi det er ingen garanti for hvordan de er tildelt. I beste fall hører de i innebygde programmer der lagre en byte er viktig. Og selv der, kan du ikke bruke dem til å faktisk controly biter i minne-kartlagt porter.

Svarte 29/09/2008 kl. 20:56
kilden bruker

stemmer
0

Du ser forskjellige verdier på grunn av kompilatoren pakking regler. Du kan se regler for Visual Studio her .

Når du har en struktur som skal pakkes (eller følge noen spesifikke justerings krav), bør du bruke alternativet #pragma pakken (). For koden din, kan du bruke #pragma pakke (0) som vil justere alle strukturmedlemmer på byte grenser. Du kan deretter bruke #pragma pakke () for å tilbake struktur pakking til det er standard tilstand. Du kan se mer informasjon om pakken pragma her .

Svarte 29/09/2008 kl. 18:36
kilden bruker

stemmer
0

Interessant - Jeg vil tro at "WORD" ville vurdere å "unsigned short", slik du ville ha det problemet i mer enn ett sted.

Vær også oppmerksom på at du trenger å forholde seg til endian problemer i noen verdi over 8 bits.

Svarte 29/09/2008 kl. 18:29
kilden bruker

stemmer
0

Ikke en C / C ++ ekspert når det kommer til pakking. Men jeg antar det er en regel i spec som sier at når en ikke-bitfield følger en bitfield det må rettes på ordet grensen uavhengig av om det passer inn i den resterende plassen. Ved å gjøre det en eksplisitt bitvector er du unngår dette problemet.

Igjen er dette spekulasjoner med et snev av erfaring.

Svarte 29/09/2008 kl. 18:27
kilden bruker

stemmer
0

Struct grenser i minnet kan være polstret av kompilatoren avhengig av størrelsen og rekkefølgen på felt.

Svarte 29/09/2008 kl. 18:25
kilden bruker

stemmer
0

Kompilatoren er utfylling ikke-bitfield struct medlem til 32-bit - nativ ordinnrettingen. For å fikse dette, gjør #pragma pakke (0) før struct og #pragma pakke () etter.

Svarte 29/09/2008 kl. 18:24
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more