【问题标题】:How do I safely put two variable sized datatypes (structs) in a single struct?如何安全地将两个可变大小的数据类型(结构)放在一个结构中?
【发布时间】:2015-11-28 02:23:37
【问题描述】:

我正在尝试构建一些自身需要容纳多个结构的 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 扩展,我宁愿避免。我能做些什么来解决这个问题?有没有一种安全的方法可以将多个结构放入一个结构中?当然不是这样的。

【问题讨论】:

  • 你不能。但是,您可以拥有一个带有指针的结构,指向具有灵活数组成员的结构。
  • 你能给出A_tB_t的完整声明
  • 只有结构的最后一个字段可以有可变大小。您可能需要为要“保留”的一个或两个结构使用指向另一个对象的指针。
  • 您提供的编译器诊断与您提供的代码不对应。 A_tB_t 类型不是可变大小的。有了这些声明,C_t 类型的声明应该没有任何问题。如果clang 真的对此表示反对,正如您所介绍的那样,那么它将构成一个错误。但是,我认为您更有可能从示例中简化了真正的问题。
  • @LPs 这是一个功能,而不是一个“功能”。

标签: c struct


【解决方案1】:

C 结构只能在它可能包含“灵活数组成员”作为其最后一个成员的意义上是可变大小的。 C 禁止这种结构类型成为 any 结构成员或数组元素的类型,尽管 GCC 允许将其作为扩展。

即使 GCC(或 clang)接受了您的声明,我也怀疑它是否意味着您认为的意思。结构的每个成员相对于结构的开头都有一个固定的偏移量,在编译时静态确定。结果,您的msg_t 无法为任意hdr.f 提供足够的空间,而且很可能根本没有提供空间,尤其是在您启用结构打包的情况下。因此,访问msg_thdr.f 可以轻松访问消息data,我想这不是您所期望的。

我想重点是将结构映射到字节缓冲区,但如果底层数据格式在中间有可变长度元素,那么你就不能直接将单个 C 结构映射到它。但是,您可以创建和使用索引结构:

typedef struct {
    hdr_t *hdr;
    pyld_t *pyld;
} msg_index_t;

这样可以更轻松地处理将结构映射到缓冲区。

【讨论】:

  • @chux,请进一步阅读。 C11 和 C99 都禁止包含灵活数组成员的结构成为数组元素或另一个结构的成员。在 C11 中,这是 6.7.2.1/3 的末尾。在这方面,C11 实际上比 C99 更具限制性,因为它对包含此类结构的联合应用同样的禁令,可能是递归的,就像它直接对此类结构所做的那样。
  • 请原谅我对这个好答案的错误阅读。删除之前的评论。
【解决方案2】:

你可以使用联合:

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;

【讨论】:

    【解决方案3】:

    有很多解决方案可以解决这个问题。对于我的具体问题,只需从这里更改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;
    

    我不确定为什么原始代码使用数组,但没有必要。

    【讨论】:

    • 如果您将结构布局直接映射到数据包格式,例如您正在对整个结构执行readwrite,那么数组 必要的。如果您正在与个别成员进行 I/O,则不是。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-11
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    相关资源
    最近更新 更多