我有一个C++程序将TCP标头表示为结构:
#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;
}
Run Code Online (Sandbox Code Playgroud)
如果我运行这个程序,我得到这个标题的大小为24字节,这不是我期望的大小.如果我将字段"wWindow"的类型更改为"unsigned int wWindow:16",它具有与unsigned short相同的位数,程序会告诉我结构的大小现在是20个字节,正确的大小.为什么是这样?
我在32位x86计算机上使用Microsoft Visual Studio 2005 SP1.
因为编译器将您的位域打包成32位int,而不是16位实体.
通常,您应该避免使用位域并使用其他显式常量(枚举或其他)与显式位屏蔽并移位以访问字段中的"子字段".
这就是为什么应该避免使用位域的一个原因 - 即使对于同一平台,它们在编译器之间也不是很容易移植.来自C99标准(C90标准中有类似的措辞):
实现可以分配足够大的任何可寻址存储单元来保持位域.如果剩余足够的空间,则紧跟在结构中的另一个位字段之后的位字段将被打包到相同单元的相邻位中.如果剩余的空间不足,则是否将不适合的位域放入下一个单元或重叠相邻单元是实现定义的.单元内的位域分配顺序(高阶到低阶或低阶到高阶)是实现定义的.未指定可寻址存储单元的对齐.
你不能保证位字段是否会"跨越"一个int边界,你不能指定一个位域是从int的低端开始还是从int的高端开始(这与处理器是否是独立的无关) big-endian或little-endian).
请参阅这个问题:为什么结构体的 sizeof 不等于每个成员的 sizeof 之和?。
我相信当您使用“unsigned int wWindow:16”语法时,编译器会提示禁用填充。
另请注意,短路不保证为 16 位。保证是:16 位 <= Short 的大小 <= int 的大小。