我正在尝试将库移植到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;
Run Code Online (Sandbox Code Playgroud)
类型header_t很好,但编译器实际上抱怨类型header_t[],即"不确定长度的数组header_t",它具有不完整的类型,因为编译器不知道它有多大(它不可能).
C99(但不是C89)在结构中支持所谓的灵活数组成员,正是这样,但仅在结构的末尾:
struct X {
// any member declarations
...
AnyType lastMemberArray[]; // This must be the LAST member
};
Run Code Online (Sandbox Code Playgroud)
这是允许的,但是它使你声明的结构也是一个不完整的类型,因为再次,编译器不知道它有多大.使用它的唯一方法是动态分配所需大小的内存或者转换已分配的内存块.例如:
// 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
Run Code Online (Sandbox Code Playgroud)
为什么灵活的数组成员必须在结构中排在最后?想象一下,如果其他成员追随它.编译器如何生成访问这些成员的代码?
// 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);
}
Run Code Online (Sandbox Code Playgroud)
因此,您不能拥有具有多个灵活数组成员的结构,因为其中其中一个不会是最后一个结构成员,因此不允许使用您的定义.
无论你编写什么库代码,它都不能使用两个灵活的数组成员,或者它不会在任何平台上编译.我建议你调查原始代码,看看它做了什么; 如果它使用标准的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;
Run Code Online (Sandbox Code Playgroud)
编辑
我下载了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
Run Code Online (Sandbox Code Playgroud)
所以这里首先要做的是这是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
Run Code Online (Sandbox Code Playgroud)
那么g ++正在做什么(与C++标准相反)是将未指定大小的数组转换为大小为0的数组.第一个(pkt_headers)的工作方式与C99中的灵活数组成员完全相同,因为如果你已经分配了适当的数量对于内存,您可以访问通常最大大小的数组成员.但是,如果你曾经访问任何成员后说(特别是pkt_subheader_count和pkt_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;
Run Code Online (Sandbox Code Playgroud)
这将导致访问pkt_subheader_count(并且可能还访问pkt_subheaders)访问完全相同的内存pkt_headers[0].
为什么碰巧好了? 因为此项目中的代码永远不会访问pkt_subheader_count或pkt_subheaders在任何地方. 如果确实如此,除非得到非常幸运,否则代码将无法用于上述原因.它不是有效的C++,恰好被编译器接受了.
解?只是删除pkt_subheader_count,并pkt_subheaders从结构的声明.它们不在代码中的任何地方使用,删除它们允许pkt_headers[]成为结构的最后一个成员,因此它是一个有效的灵活数组成员,它是有效的C99或C++中的非标准编译器扩展.
| 归档时间: |
|
| 查看次数: |
13256 次 |
| 最近记录: |