从结构体中生成16位字

Form 16 bit words from a struct

本文关键字:16位字 结构体      更新时间:2023-10-16

所以我正在创建一个ICMPv4 echo请求,并决定滚动我自己的结构来保存数据包。为了便于在wireshark中识别数据包,我决定在data字段中添加abcde。

struct icmpPacket{
    u_int8_t icmp_type:8, icmp_code:8;
    u_int16_t icmp_checksum:16, icmp_id:16, icmp_seqnum:16;
    char icmp_data[6]; //cheat a little bit, set the field just large enough to store "abcde";
    } __attribute__((aligned (16))) icmppckt; // icmp has an 8 byte header + 6 bytes of data

我遇到的问题是如何使编译器将结构体读取为一系列16位字

符合标准的方法是通过memcpy:

icmpPacket packet = { /* ... */ };
uint16_t buf[sizeof(icmpPacket) / sizeof(uint16_t)];
memcpy(buf, &packet, sizeof(icmpPacket));
/* Now use buf */

现代编译器足够聪明,可以适当地优化这一点,而无需实际执行函数调用。参见clang和g++的例子)。

一个通用的编译器扩展允许你使用联合,尽管在c++标准下这是未定义的行为:

union packet_view{
    icmpPacket packet;
    uint16_t buf[sizeof(icmpPacket) / sizeof(uint16_t)];
};
icmpPacket packet = { /* ... */ };
packet_view view;
view.packet = packet;
/* Now read from view.buf. This is technically UB in C++ but most compilers define it. */

使用reinterpret_cast<uint16_t*>(&packet)或其C等价将打破严格的混叠规则并导致未定义的行为。(基本§3.10。c++标准:

如果程序试图通过该行为不是下列类型之一的全局值定义:

  • 对象的动态类型,
  • 对象动态类型的cv限定版本,
  • 与对象的动态类型相似的类型(定义见4.4),
  • 与对象的动态类型对应的有符号或无符号类型的类型,
  • 有符号或无符号类型,对应于对象动态类型的cv限定版本,
  • 聚合或联合类型,在其元素或非静态数据成员(包括:类的元素或非静态数据成员包含联盟),
  • 是对象动态类型的基类类型(可能是cv限定的),
  • 一个char或unsigned char类型。

同样,C11的§6.5/p7说:

对象的存储值只能由左值访问具有下列类型之一的表达式:

  • 与对象的有效类型兼容的类型,
  • 与对象的有效类型兼容的类型的限定版本,
  • 与对象的有效类型相对应的有符号或无符号类型的类型,
  • 与对象有效类型的限定版本相对应的有符号或无符号类型的类型,
  • 聚合或联合类型,在其成员中包含上述类型之一(递归地包括a子聚合或包含联合),或
  • 字符类型。

可以使用16位指针

  • 但是你需要添加对齐到1字节的结构元素!!
  • 在c++中你可以这样做:

    #pragma pack(1)
    struct icmpPacket
        {
        u_int8_t icmp_type:8, icmp_code:8;
        u_int16_t icmp_checksum:16, icmp_id:16, icmp_seqnum:16;
        char icmp_data[6]; //cheat a little bit, set the field just large enough to store "abcde";
        } icmppckt; // icmp has an 8 byte header + 6 bytes of data
    WORD *picmppckt16=(WORD*)((void*)&icmppckt);
    #pragma pack()
    
  • 将WORD更改为编译器知道的16位数据类型