【问题标题】:Fields in a struct skipping bytes结构中的字段跳过字节
【发布时间】:2010-08-18 14:38:45
【问题描述】:

我已经编写了一个结构,它应该代表一个完整的 UDP 数据包,包括以太网标头和所有内容。这里是:

#pragma pack(1)
struct UDPPacket {
    // an array to hold the destination mac address of the packet
    unsigned char dstmac[6];

    // an array to hold the source mac address of the packet
    unsigned char srcmac[6];

    // two bytes to hold the packet type, this is almost always IP (08 00)
    WORD ethtype;

    // each of the subfields of this take up 4 bits. ver, the first half,
    // is the ip version, which should usually be 4 for ipv4, and the second
    // is the length of the header divided by 4, which is almost always 5
    struct {
        unsigned ver : 4;
        unsigned len : 4;
    } verlen;

    // this isn't used in ipv4 and is 0
    BYTE tos;

    // the total length of the header + data
    WORD iplen;

    // the id of this datagram for reassembling fragmented packets
    WORD id;

    // the first subfield occupies 3 bits and is the flags of this packet, which is usually 0
    // the second subfield is the fragmentation offset for large datagrams that have been split up for sending, usually 0
    struct {
        unsigned flags : 3;
        unsigned fragmentation : 13;
    } flagfrag;

    // time to live; usually 35 or 128
    BYTE ttl;

    // the protocol with which this packet is being transported
    // 1 = ICMP, 2 = IGMP, 6 = TCP, 17 = UDP
    BYTE protocol;

    // the ip checksum of this packet
    WORD ipchecksum;

    // the source ip of this packet
    DWORD src;

    // the destination ip of this packet
    DWORD dest;
    // the port from which this packet is coming
    WORD srcport;

    // the port this packet is headed to
    WORD destport;

    // the length of the udp header + data, not including the ip header
    // so it's usually basically iplen - 20
    WORD udplen;

    // the udp checksum of this packet
    WORD udpchecksum;

    // a char pointer to the data of the packet
    unsigned char data[10000];
};
#pragma pack()

当然,这是一个真实的 UDP 数据包的表示,字节必须与数据包中的偏移量相同,指向这种类型的结构的指针将被转换为unsigned char*s 用于发送.
我的问题是,当我尝试在UDPPacket.verlen 之后分配任何内容时,它会向前跳过大约 5 个字节并从那里开始。例如,当我分配 iplen 字段时,不是将字节设置在偏移量 16 和 17,而是将它们分配在 23 和 24 之类的位置(我不能确切地说,因为我的程序在这里没有可用我的手机)。
是否有明显的原因导致我失踪了,或者我只是做错了什么?

【问题讨论】:

  • 顺便说一句,我在 x64 Windows 7 上使用 Visual Studio 2008,但为 x86 编译。

标签: c++ visual-studio-2008 struct field byte


【解决方案1】:

您的#pragmas 看起来不错。位域不会“自动打包”为适合明确指定的位数的最小类型。我怀疑verlen 正在采用您给定的类型“无符号”并假设它是大小为 unsigned int 的位恶魔,在您的编译器中听起来像是 32 位。尝试制作 verlen "unsigned char" 的字段。

这里有更多。在这里指定“无符号字符”的能力是一个 MSFT 扩展(到 ANSI C),但应该可以工作:http://msdn.microsoft.com/en-us/library/yszfawxh(VS.80).aspx

注意flagfrag 也一样,应该是“无符号短”。

【讨论】:

  • 以前,我认为它是什么类型并不重要,因为您只是指定它应该占用的位数,但现在我认为您指定了一个占用一定的类型字节(以及位)的数量,那么您可以在结构中拥有不同的位字段,只要它们全部加起来就是您之前指定的类型。对吗?
  • 我相信在 C++ 中这不是扩展,而是标准的要求。我将不得不再次仔细阅读语法,如果我有时间的话,我可能会在今晚晚些时候尝试。
  • 是的,基本上编译器只关心你用作位域的底层类型;命名单个位的能力对您来说只是一种语法上的便利——它不会引入新的“位”类型或类似的东西。如果这是您所要求的,那么您不太可能(尽管您应该尝试或阅读文档)在结构的位域中间拥有完整的较小类型。
  • 好的,谢谢,顺便说一句,它运行良好并解决了问题。我喜欢这样。
  • @Hock:如果位数大于类型,则会添加额外的字段,因此int a:16, b:16, c:16 将占用 2 个int 如果sizeof(int)==4。请注意,类或结构中字段的顺序必须相同,因此,如果有任何中间字段,即使它们可以打包到单个元素中,它们也不会:int a:1, b, c:1 将占用 3 int。位域的元素位于实际字段中的位置未定义int a:1, b:1, c:1; 将采用单个int,但不能保证哪个位代表每个变量。
【解决方案2】:

该标准不要求编译器将位域打包到单个存储单元(int、char 等)中。因此,即使使用编译指示,我也希望这 2 个位域占用 2 个字符或整数。我没有该编译器的文档 - 它是否说明了编译指示如何影响位域?

【讨论】:

    【解决方案3】:

    您的 #pragma pack 指令是正确的,但我认为您的双域(verlenflagfrag)的基础类型是 int,而不是您所期望的 charshort

    【讨论】:

      【解决方案4】:

      您必须检查编译器的填充和对齐设置。

      【讨论】:

        【解决方案5】:

        也许使用offsetof macro 会对您有所帮助。

        【讨论】:

          【解决方案6】:

          unsigned在这里等价于unsigned int,也就是4个字节。所以iplen 应该是偏移量 23,听起来就是这样。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-10-22
            • 1970-01-01
            • 1970-01-01
            • 2011-12-13
            • 1970-01-01
            • 1970-01-01
            • 2012-11-20
            • 1970-01-01
            相关资源
            最近更新 更多