如何安全地将两个可变大小的数据类型(结构)放在一个结构中?

时间:2015-09-02 14:09:45

标签: c struct

我试图构建一些自己需要保存多个结构的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扩展,我宁愿避免。我该怎么做才能解决这个问题?是否有一种安全的方法可以在结构中放置多个结构?当然不是这样。

3 个答案:

答案 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;

我不确定为什么原始代码使用的是数组,但这并不是必需的。