错误:字段的类型不完整

杰米

我试图将库移植到Mac OSX。编译器报告一个不完整的类型错误。具体来说:字段的类型'header_t []不完整。但是,当我查看源代码时,header_t恰好在packet_state_t之前定义,其中packet_state_t引用header_t。因此,不应有任何前向引用错误,因为header_t在packet_state_t内部被引用的位置明确定义了。发生错误的行在下面用ERROR标记。怎么解决?

 typedef struct header_t {
    uint8_t  hdr_id;         // header ID

    uint8_t  hdr_prefix;     // length of the prefix (preamble) before the header
    uint8_t  hdr_gap;        // length of the gap between header and payload
    uint16_t  hdr_flags;      // flags for this header
    uint16_t hdr_postfix;    // length of the postfix (trailer) after the payload
    uint32_t hdr_offset;     // offset into the packet_t->data buffer
    uint32_t hdr_length;     // length of the header in packet_t->data buffer
    uint32_t hdr_payload;    // length of the payload

    uint8_t   hdr_subcount;  // number of sub-headers
    header_t  *hdr_subheader;   // Index of the first subheader in packet_t

    jobject  hdr_analysis;   // Java JAnalysis based object if not null
} header_t;

typedef struct packet_state_t {
    flow_key_t pkt_flow_key; // Flow key calculated for this packet, must be first
    uint8_t pkt_flags;       // flags for this packet
    jobject pkt_analysis;    // Java JAnalysis based object if not null
    uint64_t pkt_frame_num;  // Packet's frame number assigned by scanner
    uint64_t pkt_header_map; // bit map of presence of headers

    uint32_t pkt_wirelen;    // Original packet size
    uint32_t pkt_buflen;     // Captured length

    int8_t pkt_header_count; // total number of main headers found
    header_t pkt_headers[];  // One per header + 1 more for payload ERROR HERE!!!

    int8_t pkt_subheader_count;  // total number of sub headers found
    header_t pkt_subheaders[];  // One per header + 1 more for payload
} packet_state_t;
亚当·罗森菲尔德

该类型header_t很好,但是编译器实际上在抱怨该类型header_t[],即“不确定长度的数组header_t”,该类型具有不完整的类型,因为编译器不知道它的大小(不可能)。

C99(但不支持C89)在结构中支持所谓的灵活数组成员,正是这样,但在结构的末尾:

struct X {
  // any member declarations
  ...
  AnyType lastMemberArray[];  // This must be the LAST member
};

这是允许的,但是这会使您声明的结构也成为不完整的类型,因为同样,编译器也不知道它的大小。使用它的唯一方法是动态分配所需大小的内存或强制转换已分配的内存块。例如:

// Allocate an X instance with space for 3 members in lastMemberArray:
X *x = malloc(sizeof(X) + 3 * sizeof(AnyType));
// Can now use x->lastMemberArray[0] through x->lastMemberArray[2]
...

// Alternatively:
char buffer[sizeof(X) + 3 * sizeof(AnyType)];
X *x = (X *)buffer;
// Same as above

为什么柔性数组成员必须在结构中排在最后?想象一下其他成员是否追随了它。编译器如何生成代码以访问那些成员?

// If this were allowed:
struct X {
  AnyType flexibleArray[];
  int memberAfter;
};

void doSomething(X *x) {
  // How does the compiler generate code for this?  It doesn't know what offset
  // memberAfter is from the start of the object, because the array doesn't
  // have a known size
  printf("memberAfter = %d\n", x->memberAfter);
}

因此,一个结构中不能包含一个以上的柔性数组成员,因为显然其中一个不是最后一个结构成员,因此不允许您进行定义。

无论您编写的是什么库代码,它都不会使用两个灵活的数组成员作为开始,或者它不会在任何平台上进行编译。我建议您调查原始代码以了解它的作用。如果它使用标准ISO C功能而不依赖于任何特定于平台或特定于实现的行为或扩展,则在移植它时应该没有问题。

在看不到原始代码的情况下,我建议您从使用内联灵活数组成员切换到带有指针的动态分配数组,至少对于第一个数组(为了一致性也可能对第二个数组):

typedef struct packet_state_t {
    flow_key_t pkt_flow_key; // Flow key calculated for this packet, must be first
    uint8_t pkt_flags;       // flags for this packet
    jobject pkt_analysis;    // Java JAnalysis based object if not null
    uint64_t pkt_frame_num;  // Packet's frame number assigned by scanner
    uint64_t pkt_header_map; // bit map of presence of headers

    uint32_t pkt_wirelen;    // Original packet size
    uint32_t pkt_buflen;     // Captured length

    int8_t pkt_header_count; // total number of main headers found
    header_t *pkt_headers;   // POINTER here, not an array

    int8_t pkt_subheader_count;  // total number of sub headers found
    header_t *pkt_subheaders;    // POINTER here, not an array
} packet_state_t;

编辑

我下载了jnetpcap代码并在Linux上进行了编译,以查看发生了什么。令我惊讶的是,它编译了。调用的编译器命令为:

gcc -c -fPIC -DLIBPCAP_VERSION=0x1532 -I/tmp/jnetpcap/build/include -I/tmp/jnetpcap/src/c -I/usr/lib/jvm/default-java/include -I/usr/lib/jvm/default-java/include/linux /tmp/jnetpcap/src/c/jnetpcap.cpp /tmp/jnetpcap/src/c/packet_flow.cpp /tmp/jnetpcap/src/c/packet_jheader.cpp /tmp/jnetpcap/src/c/jnetpcap_pcap_header.cpp /tmp/jnetpcap/src/c/nio_jbuffer.cpp /tmp/jnetpcap/src/c/winpcap_stat_ex.cpp /tmp/jnetpcap/src/c/winpcap_send_queue.cpp /tmp/jnetpcap/src/c/winpcap_ext.cpp /tmp/jnetpcap/src/c/util_debug.cpp /tmp/jnetpcap/src/c/util_crc16.c /tmp/jnetpcap/src/c/jnetpcap_ids.cpp /tmp/jnetpcap/src/c/jnetpcap_dumper.cpp /tmp/jnetpcap/src/c/jnetpcap_utils.cpp /tmp/jnetpcap/src/c/util_in_cksum.cpp /tmp/jnetpcap/src/c/jnetpcap_beta.cpp /tmp/jnetpcap/src/c/nio_jmemory.cpp /tmp/jnetpcap/src/c/util_crc32.c /tmp/jnetpcap/src/c/packet_jsmall_scanner.cpp /tmp/jnetpcap/src/c/mac_addr_sys.c /tmp/jnetpcap/src/c/packet_protocol.cpp /tmp/jnetpcap/src/c/nio_jnumber.cpp /tmp/jnetpcap/src/c/packet_jheader_scanner.cpp /tmp/jnetpcap/src/c/library.cpp /tmp/jnetpcap/src/c/packet_jscan.cpp /tmp/jnetpcap/src/c/jnetpcap_pcap100.cpp /tmp/jnetpcap/src/c/mac_addr_dlpi.c /tmp/jnetpcap/src/c/util_checksum.cpp /tmp/jnetpcap/src/c/packet_jpacket.cpp /tmp/jnetpcap/src/c/winpcap_ids.cpp /tmp/jnetpcap/src/c/jnetpcap_bpf.cpp

因此,这里要做的第一件事是这是C ++,而不是C。C++根本不支持灵活的数组成员,尽管某些编译器支持将它们作为扩展。C ++ 03§9.2/ 8说:

[...]当数组用作非静态成员的类型时,应指定所有尺寸。

C ++ 11§9.2/ 9说:

9非静态(9.4)数据成员不应具有不完整的类型。[...]

当我在g ++ 4.8.2中以较高的警告级别(-Wall -Wextra pedantic编译此代码时,它会发出以下警告:

In file included from /tmp/jnetpcap/src/c/packet_jscan.cpp:28:0:
/tmp/jnetpcap/src/c/packet_jscanner.h:287:23: warning: ISO C++ forbids zero-size array ‘pkt_headers’ [-Wpedantic]
  header_t pkt_headers[];  // One per header + 1 more for payload
                       ^
/tmp/jnetpcap/src/c/packet_jscanner.h:290:26: warning: ISO C++ forbids zero-size array ‘pkt_subheaders’ [-Wpedantic]
  header_t pkt_subheaders[];  // One per header + 1 more for payload

因此,g ++的工作(与C ++标准相反)是将未指定大小的数组转换为大小为0的数组。第一个(pkt_headers)的工作方式与C99中的flexible数组成员完全相同,因为如果您分配了适当的数量内存,您通常可以访问最大大小的数组成员。但是,如果你曾经访问任何成员说(特别是pkt_subheader_countpkt_subheaders),编译器生成代码,如果pkt_headers有大小为0,即如果该结构是等价于:

typedef struct packet_state_t {
    flow_key_t pkt_flow_key; // Flow key calculated for this packet, must be first
    uint8_t pkt_flags;       // flags for this packet
    jobject pkt_analysis;    // Java JAnalysis based object if not null
    uint64_t pkt_frame_num;  // Packet's frame number assigned by scanner
    uint64_t pkt_header_map; // bit map of presence of headers

    uint32_t pkt_wirelen;    // Original packet size
    uint32_t pkt_buflen;     // Captured length

    int8_t pkt_header_count; // total number of main headers found
    // NOTHING HERE (array of size 0)

    int8_t pkt_subheader_count;  // total number of sub headers found
    header_t pkt_subheaders[];  // One per header + 1 more for payload
} packet_state_t;

这将导致对pkt_subheader_count(和也可能pkt_subheaders对的访问)访问与完全相同的内存pkt_headers[0]

为什么碰巧可以解决?因为此项目中的代码从不访问pkt_subheader_countpkt_subheaders在任何地方。如果确实如此,则该代码将不会由于上述原因而起作用,除非它变得非常幸运。而且它不是有效的C ++,只是碰巧被编译器接受。

解决方案?只需从结构声明中删除pkt_subheader_countpkt_subheaders它们没有在代码中的任何地方使用,并且删除它们允许pkt_headers[]成为结构的最后一个成员,因此它是有效的灵活数组成员,它是有效的C99或C ++中的非标准编译器扩展。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章