2008-09-29 14 views
7

Ich habe ein C++ Programm einen TCP-Header als Struktur repräsentiert:Erste unterschiedliche Header-Größe, indem Fenstergröße

#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; 
} 

Wenn ich dieses Programm mir die Größe dieses Headers als 24 Bytes erhalten laufen, die nicht die Größe, die ich erwartet hatte. Wenn ich den Typ des Feldes "wWindow" zu "unsigned int wWindow: 16" ändere, das die gleiche Anzahl von Bits wie ein unsigned short hat, sagt das Programm mir, dass die Größe der Struktur jetzt 20 Bytes ist, die richtige Größe. Warum ist das?

Ich verwende Microsoft Visual Studio 2005 mit SP1 auf einem 32-Bit-x86-Computer.

Antwort

2

Siehe diese Frage: Why isn't sizeof for a struct equal to the sum of sizeof of each member?.

Ich glaube, dass Compiler einen Hinweis zum Deaktivieren der Auffüllung nimmt, wenn Sie die Syntax "unsigned int wWindow: 16" verwenden.

Beachten Sie auch, dass ein Kurzschluss nicht garantiert 16 Bit ist. Die Garantie ist, dass: 16 Bit < = Größe eines kurzen < = Größe eines Int.

+0

@andy: +1, könnte # pragma push/pop auf dem pack-Parameter enthalten, um ihm zu helfen. – user7116

+0

Mike B hat die richtige Antwort unten. Siehe auch diese Diskussion: http://groups.google.com/group/microsoft.public.dotnet.languages.vc/browse_frm/thread/7ea120d16c49611d/bdf918a490a6d61a?lnk=st&q=bitfield#bdf918a490a6d61a – ChrisN

6

Da der Compiler Ihr Bitfeld in eine 32-Bit-Int, nicht eine 16-Bit-Entität packen.

Im Allgemeinen sollten Sie Bitfelder vermeiden und andere Manifest-Konstanten (enums oder was auch immer) mit expliziter Bit-Maskierung und Verschiebung verwenden, um auf die "Unterfelder" in einem Feld zuzugreifen.

Hier ist ein Grund, warum Bitfields vermieden werden sollten - sie sind nicht sehr portabel zwischen Compilern, selbst für die gleiche Plattform. aus der C99-Standard (es gibt ähnliche Formulierung in der C90-Standard):

Eine Implementierung groß genug, um jede adressierbare Speichereinheit zuteilen einen Bitfeld zu halten. Wenn genügend Speicherplatz übrig bleibt, muss ein Bitfeld, das unmittelbar einem anderen Bitfeld in einer Struktur folgt, in benachbarte Bits derselben Einheit gepackt werden. Wenn nicht genügend Speicherplatz vorhanden ist, wird ein Bit-Feld, das nicht passt, in die nächste Einheit oder Überlappungen benachbarte Einheiten ist Implementierung-definiert. Die Reihenfolge der Zuordnung von Bit-Feldern innerhalb einer Einheit (hoch- zu niedrig- oder niedrigwertig zu höherwertig) ist implementierungsdefiniert. Die Ausrichtung der adressierbaren Speichereinheit ist nicht spezifiziert.

Sie können nicht garantieren, ob ein Bit-Feld wird ‚Spanne‘ int Grenze oder nicht und Sie können nicht angeben, ob ein Bitfeld beginnt bei den Low-End des int oder dem oberen Ende des int (dies ist unabhängig davon, ob der Prozessor Big-Endian oder Little-Endian ist).

+0

Ich frage mich, ob jemand jemals ein portables Mittel zur Spezifikation von Bitfeldern definieren wird, zumindest auf POSIX-kompatibler Hardware, z.B. mit einer Syntax von etwas wie "uInt32_t thing1, ding2; field1: ding1.28.4; field2: ding1.0.28; field3: ding2.12.20;" etc.? Solche Felddeklarationen würden keinen Speicherplatz zuweisen, würden jedoch auf zuvor zugewiesene Felder zugreifen. Ich habe Compiler mit Nomenklatur für das Überlagern von Bit-Flags auf statisch deklarierten Variablen gesehen; Ich frage mich, ob irgendwelche eine Syntax für das Überlagern von Bitfeldern auf anderen Strukturfeldern haben? – supercat

0

Der Compiler füllt das Nicht-Bitfeld-Strukturelement auf 32-Bit - native Wortausrichtung. Um dies zu beheben, führen Sie #pragma pack (0) vor der Struktur und # pragma pack() danach aus.

+0

#pragma pack (0) hat das Verhalten nicht geändert. –

0

Strukturgrenzen im Speicher können vom Compiler abhängig von der Größe und Reihenfolge der Felder aufgefüllt werden.

0

Kein C/C++ Experte wenn es um das Packen geht. Aber ich stelle mir vor, es gibt eine Regel in der Spezifikation, die besagt, dass wenn ein Nicht-Bitfeld einem Bitfeld folgt, es an der Wortgrenze ausgerichtet sein muss, unabhängig davon, ob es in den verbleibenden Platz passt oder nicht. Indem Sie es zu einem expliziten Bitvektor machen, vermeiden Sie dieses Problem.

Auch dies ist Spekulation mit einem Hauch von Erfahrung.

0

Interessant - ich würde denken, dass "WORD" zu "unsigned short" würde, so dass Sie dieses Problem an mehr als einem Ort haben.

Beachten Sie auch, dass Sie mit Endian-Problemen in jedem Wert über 8 Bits umgehen müssen.

0

Aufgrund der Compiler-Packregeln werden unterschiedliche Werte angezeigt. Sie können spezifische Regeln für Visual Studio here sehen.

Wenn Sie eine Struktur haben, die gepackt werden muss (oder bestimmte Ausrichtungsanforderungen erfüllen muss), sollten Sie die Option #pragma pack() verwenden. Für Ihren Code können Sie #pragma pack (0) verwenden, das alle Strukturelemente auf Bytegrenzen ausrichtet. Sie können dann #pragma pack() verwenden, um das Strukturpacken auf den Standardstatus zurückzusetzen. Sie können weitere Informationen zum Pack Pragma here sehen.

4

Ihre Reihe von "unsigned int: xx" -Bitfeldern verbraucht nur 16 der 32 Bits in einem int. Die anderen 16 Bits (2 Bytes) sind da, aber unbenutzt. Diesem folgt der vorzeichenlose short, der sich an einer int-Grenze befindet, und dann ein WORD, das entlang einer int-Grenze ausgerichtet ist, was bedeutet, dass dort 2 Bytes der Auffüllung zwischen ihnen sind.

Wenn Sie zu "unsigned int wWindow: 16" wechseln, verwendet der Compiler die nicht verwendeten Teile des vorherigen Bitfeldes, also keine Verschwendung, keine Abkürzung und keine Auffüllung nach der Abkürzung, also Sie Speichere vier Bytes.

0

Ich denke, Mike B hat es richtig, aber aber nicht ganz klar. Wenn Sie nach "short" fragen, ist es auf 32-Bit-Grenze ausgerichtet. Wenn Sie nach Int: 16 fragen, ist es nicht. So passt int: 16 direkt nach den eBit-Feldern, während Short 2 Bytes überspringt und beim nächsten 32-Bit-Block beginnt.

Der Rest von dem, was er sagt, ist perfekt anwendbar - das Bitfeld darf niemals verwendet werden, um eine extern sichtbare Struktur zu codieren, da es keine Garantie gibt, wie sie zugewiesen werden. Im besten Fall gehören sie in eingebettete Programme, bei denen das Speichern eines Bytes wichtig ist. Und selbst dort können Sie sie nicht verwenden, um tatsächlich Bits in Speicherkartenports zu steuern.

Verwandte Themen