ウィンドウサイズを変更して異なるヘッダーサイズを取得する
質問
構造体としてTCPヘッダーを表すC ++プログラムがあります:
#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;
}
このプログラムを実行すると、このヘッダーのサイズは24バイトになりますが、これは予想していたサイズではありません。フィールドのタイプを変更した場合<!> quot; wWindow <!> quot; to!これはなぜですか?
32ビットx86マシンでMicrosoft Visual Studio 2005 SP1を使用しています。
解決
この質問を参照してください:なぜstructのsizeofが各メンバーのsizeofの合計と等しくないのですか?。
<!> quot; unsigned int wWindow:16 <!> quot;を使用すると、コンパイラはパディングを無効にするためのヒントを取ると信じています。構文。
また、ショートが16ビットであるとは限らないことに注意してください。保証は次のとおりです:16ビット<!> lt; = shortのサイズ<!> lt; = intのサイズ
他のヒント
コンパイラがビットフィールドを16ビットエンティティではなく32ビットintにパックしているため。
一般に、ビットフィールドを避け、明示的なビットマスキングおよびシフトを伴う他のマニフェスト定数(enumまたは何でも)を使用して、フィールドの「サブフィールド」にアクセスする必要があります。
ビットフィールドを避けるべき理由の1つは、同じプラットフォームであってもコンパイラ間で移植性があまりないことです。 C99標準から(C90標準にも同様の表現があります):
実装は、 十分に大きいアドレス可能なストレージユニット ビットフィールドを保持します。十分なスペースがある場合 残っているビットフィールドはすぐに の別のビットフィールドに続く 構造はに詰められるものとします 同じユニットの隣接ビット。もし 十分なスペースが残っているかどうか、 適合しないビットフィールドが配置されます 次のユニットまたはオーバーラップに 隣接ユニットは 実装定義。の順 ユニット内のビットフィールドの割り当て (高位から低位または低位へ 高次へ)は 実装定義。アライメント アドレス可能なストレージユニットの 指定なし。
ビットフィールドがint境界を「スパン」するかどうかは保証できません。また、ビットフィールドがintのローエンドで始まるかintのハイエンドで始まるかを指定することはできません(これは、プロセッサはビッグエンディアンまたはリトルエンディアンです)。
一連の<!> quot; unsigned int:xx <!> quot;ビットフィールドは、intの32ビットのうち16ビットのみを使用します。他の16ビット(2バイト)はありますが、使用されていません。これに続いて、int境界にあるunsigned shortと、int境界に沿って整列したWORDが続きます。これは、それらの間に2バイトのパディングがあることを意味します。
<!> quot; unsigned int wWindow:16 <!> quot;に切り替えると、コンパイラは個別のshortではなく、前のビットフィールドの未使用部分を使用するため、無駄がなく、shortもnoもありませんショートの後のパディング、したがって4バイトを節約します。
コンパイラは、非ビットフィールド構造体メンバーを32ビットに埋め込みます-ネイティブワードアライメント。これを修正するには、構造体の前に#pragma pack(0)を実行し、その後に#pragma pack()を実行します。
メモリ内の構造体の境界は、フィールドのサイズと順序に応じてコンパイラによってパディングできます。
梱包に関しては、C / C ++の専門家ではありません。しかし、非ビットフィールドがビットフィールドに続く場合、それが残りのスペースに収まるかどうかに関係なく、単語の境界に合わせなければならないというルールが仕様にあると思います。明示的なビットベクトルにすることで、この問題を回避できます。
これもまた、少し経験を積んだ推測です。
興味深い-<!> quot; WORD <!> quot; <!> quot; unsigned short <!> quot;と評価されるため、この問題は複数の場所で発生します。
また、8ビットを超える値ではエンディアンの問題に対処する必要があることに注意してください。
マイクBは正しかったと思いますが、完全には明確ではありません。 <!> quot; short <!> quot;を要求すると、32ビット境界で整列されます。 int:16を要求すると、そうではありません。したがって、int:16はebitフィールドの直後に収まり、shortは2バイトをスキップして次の32ビットブロックから始まります。
彼が言っていることの残りは完全に適用可能です-ビットフィールドは、外部から見える構造をコーディングするために決して使用してはなりません。せいぜい、バイトを保存することが重要な組み込みプログラムに属します。そして、メモリマップされたポートの実際の制御ビットにそれらを使用することはできません。