首页 文章

通过更改窗口大小获得不同的标头大小

提问于
浏览
7

我有一个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;
}

如果我运行这个程序,我得到这个 Headers 的大小为24字节,这不是我期望的大小 . 如果我将字段“wWindow”的类型更改为“unsigned int wWindow:16”,它具有与unsigned short相同的位数,程序会告诉我结构的大小现在是20个字节,正确的大小 . 为什么是这样?

我在32位x86计算机上使用Microsoft Visual Studio 2005 SP1 .

9 回答

  • 2

    看到这个问题:Why isn't sizeof for a struct equal to the sum of sizeof of each member? .

    我相信当你使用“unsigned int wWindow:16”语法时,编译器会提示禁用填充 .

    另请注意,短路不能保证为16位 . 保证是:16位<=短的大小<= int的大小 .

  • 6

    因为编译器将您的位域打包成32位int,而不是16位实体 .

    通常,您应该避免使用位域并使用其他显式常量(枚举或其他)与显式位屏蔽并移位以访问字段中的“子字段” .

    这就是为什么应该避免使用位域的一个原因 - 即使对于同一平台,它们在编译器之间也不是很容易移植 . 来自C99标准(C90标准中有类似的措辞):

    实现可以分配任何足够大的可寻址存储单元来保存位域 . 如果剩余足够的空间,则紧跟在结构中的另一个位字段之后的位字段将被打包到相同单元的相邻位中 . 如果剩余的空间不足,则是否将不适合的位域放入下一个单元或重叠相邻单元是实现定义的 . 单元内的位域分配顺序(高阶到低阶或低阶到高阶)是实现定义的 . 未指定可寻址存储单元的对齐 .

    你不能保证位字段是否会“跨越”一个int边界,你不能指定一个位域是从int的低端开始还是从int的高端开始(这与处理器是否是独立的无关) big-endian或little-endian) .

  • 4

    您的一系列“unsigned int:xx”位域仅占用int中32位的16位 . 其他16位(2个字节)在那里,但未使用 . 接下来是无符号short,它位于int边界,然后是WORD,它在int边界上对齐,这意味着它们之间有2个字节的填充 .

    当您切换到“unsigned int wWindow:16”时,编译器使用前一个位域的未使用部分,而不是单独的短路,因此在短路后没有浪费,没有短路,也没有填充,因此可以节省四个字节 .

  • 0

    编译器将非位域结构成员填充为32位 - 本机字对齐 . 要解决此问题,请在结构之前执行#pragma pack(0),然后执行#pragma pack() .

  • 0

    内存中的结构边界可以由编译器填充,具体取决于字段的大小和顺序 .

  • 0

    在包装方面,不是C / C专家 . 但是我想在规范中有一个规则,即当一个非位域跟随一个位域时,它必须在字边界上对齐,而不管它是否适合剩余的空间 . 通过使它成为一个明确的位向量,你可以避免这个问题 .

    再一次,这是一种经验的推测 .

  • 0

    有意思 - 我认为“WORD”会评价为“unsigned short”,所以你在不止一个地方就会遇到这个问题 .

    还要注意,您需要处理超过8位的任何值的字节序问题 .

  • 0

    我认为迈克B做对了,但不是很清楚 . 当你要求“短”时,它在32位边界上对齐 . 当你要求int:16时,它不是 . 所以int:16紧跟在ebit字段之后,而short跳过2个字节并从下一个32位块开始 .

    他所说的其余内容完全适用 - 必须永远不要使用位域来编码外部可见的结构,因为无法保证它们的分配方式 . 充其量,它们属于嵌入式程序,其中保存字节很重要 . 即使在那里,你也不能使用它们来实际控制内存映射端口中的位 .

  • 0

    由于编译器打包规则,您会看到不同的值 . 您可以看到特定于visual studio here的规则 .

    如果您的结构必须打包(或遵守某些特定的对齐要求),则应使用#pragma pack()选项 . 对于您的代码,您可以使用#pragma pack(0)将在字节边界上对齐所有结构成员 . 然后,您可以使用#pragma pack()将结构打包重置为默认状态 . 您可以在pack pragma here上查看更多信息 .

相关问题