错误:字段类型不完整

时间:2014-11-12 05:41:03

标签: c struct

我正在尝试将库移植到Mac OS X.编译器报告了一个不完整的类型错误。具体来说:字段具有不完整类型' 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;

2 个答案:

答案 0 :(得分:9)

类型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中的灵活数组成员完全相同,如果您已分配了适当的内存量,则可以访问通常达到最大大小的阵列成员。但是如果您访问之后的任何成员(特别是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 ++中的非标准编译器扩展。 / p>

答案 1 :(得分:0)

struct header_t

的定义

更改

header_t  *hdr_subheader;   // Index of the first subheader in packet_t

struct header_t  *hdr_subheader;   // Index of the first subheader in packet_t