我试图构建一些自己需要保存多个结构的C结构。它看起来像这样:
typedef struct hdr_t {
uint16_t a;
uint16_t b;
uint8_t c;
uint8_t d[3];
uint64_t e;
uint8_t f[];
} hdr_t;
typedef struct {
uint64_t data;
} pyld_t;
typedef struct {
hdr_t hdr;
pyld_t pyld;
} msg_t;
当我编译它时,根据编译器和设置,我收到警告。
./file.h:55:24: warning: field 'hdr' with variable sized type 'hdr_t'
(aka 'struct hdr_t') not at the end of a struct or class is a GNU extension
[-Wgnu-variable-sized-type-not-at-end]
hdr_t hdr;
对于这个例子,我使用了clang 6.1.0:
$ clang --version
Apple LLVM版本6.1.0(clang-602.0.53)(基于LLVM 3.6.0svn)
目标:x86_64-apple-darwin14.5.0
线程模型:posix
警告抱怨说我正在做的是一个非便携式GNU扩展,我宁愿避免。我该怎么做才能解决这个问题?是否有一种安全的方法可以在结构中放置多个结构?当然不是这样。
答案 0 :(得分:2)
C结构只能在其包含“灵活数组成员”作为其最后成员的意义上是可变大小的。 C禁止这种结构类型为任何结构成员或数组元素的类型,尽管GCC允许它作为扩展。
即使GCC(或clang)接受了你的声明,我怀疑这意味着你认为它意味着什么。结构的每个成员都有一个相对于结构开头的固定偏移量,在编译时静态确定。因此,msg_t
无法为任意hdr.f
提供足够的空间,并且很可能根本不提供任何空间,尤其是在启用结构打包的情况下。因此,访问hdr.f
的{{1}}可以轻松访问消息msg_t
,我认为这不是您所期望的。
我想整个观点是将结构映射到字节缓冲区,但如果底层数据格式在中间有可变长度元素,那么你就不能直接将单个C结构映射到它。但是,您可以创建和使用索引结构:
data
这样可以更轻松地将对结构映射到缓冲区。
答案 1 :(得分:1)
你可以使用联盟:
typedef struct {
..stuff
}type_f1;
typedef struct {
..stuff
}type_f2;
typedef struct {
..stuff
}type_f3;
typedef union {
type_f1 f1;
type_f2 f2;
type_f3 f3;
uint8_t rawdata[MAX_RAWDATA];
}type_f;
typedef struct hdr_t {
uint16_t a;
uint16_t b;
uint8_t c;
uint8_t d[3];
uint64_t e;
type_f f;
} hdr_t;
typedef struct {
uint64_t data;
} pyld_t;
typedef struct {
hdr_t hdr;
pyld_t pyld;
} msg_t;
答案 2 :(得分:0)
有很多解决方案可以解决这个问题。对于我的具体问题,只需更改hdr_t
的声明:
typedef struct hdr_t {
uint16_t a;
uint16_t b;
uint8_t c;
uint8_t d[3];
uint64_t e;
uint8_t f[];
} hdr_t;
到此:
typedef struct hdr_t {
uint16_t a;
uint16_t b;
uint8_t c;
uint8_t d[3];
uint64_t e;
uint8_t *f; //Use pointer instead of variable sized array
} hdr_t;
我不确定为什么原始代码使用的是数组,但这并不是必需的。