【问题标题】:How to make a dynamic structure in C如何在C中制作动态结构
【发布时间】:2020-12-06 17:30:02
【问题描述】:

例如,如果我有这样的结构:

typedef struct __attribute__ ((packed))
{
  uint16_t    ObjectPropertyCode;
  uint16_t    DataType;
  uint8_t     GetSet;
  union
  {
    uint8_t     DefaultValue_u8 ;
    uint16_t    DefaultValue_u16 ;
    uint32_t    DefaultValue_u32 ;
  }DefValue  ;
  uint32_t    GroupCode;
  uint8_t     FormFlag;
}
MTP_ObjectPropDescTypeDef;

我只需要 DefValue 中的一个元素,这取决于我的 DataType。 例如,如果我的 DataType 是 uint16 ,那么我将像这样发送我的结构:

typedef struct __attribute__ ((packed))
{
  uint16_t    ObjectPropertyCode;
  uint16_t    DataType;
  uint8_t     GetSet;
  uint16_t    DefaultValue_u16 ;
  uint32_t    GroupCode;
  uint8_t     FormFlag;
}
MTP_ObjectPropDescTypeDef;

【问题讨论】:

  • 如图所示,DefValue 的大小将始终保持uint32_t,无论您分配什么,但您需要知道存储的类型,以便准确检索存储在那里的最后一个值。
  • 我(可能是错误地)认为问题在于将数据与中间的可变大小成员紧密地打包在一起。
  • 是的,你是对的@vmt,在MTP规范(媒体传输协议)中,这个值可以是不同的类型,并且结构必须定义好,才能被主机识别。
  • 嗯,这仍然不能说明整个故事,即主持人识别它意味着什么?如果您的意思是 DefaultValue 必须具有不同的大小,请在下面查看我的答案。

标签: c struct dynamic union mtp


【解决方案1】:

除非您将数据布局分解为多个部分,或者更改数据布局以便您的可变大小类型位于最后,否则您的建议无法完成(好吧,无论如何都是合理的)。

有了这些,我们就可以进入不合理的部分。您可以(ab)使用预处理器在一定程度上有效地实现这一目标。 (例如):

#define JOIN3(a, b, c) a##b##c
#define MYSTRUCT(n) \
    struct __attribute__ ((packed)) { \
        uint16_t ObjectPropertyCode; \
        uint16_t DataType; \
        uint8_t GetSet; \
        JOIN3(uint, n, _t) DefaultValue; \
        uint32_t GroupCode; \
        uint8_t FormFlag; \
    }

// ...

{
    MYSTRUCT(8) obj; // uint8_t DefaultValue
    printf("%lu\n", sizeof(obj)); // 11

    MYSTRUCT(16) obj2; // uint16_t DefaultValue
    printf("%lu\n", sizeof(obj2)); // 12

    MYSTRUCT(32) obj3; // uint32_t DefaultValue
    printf("%lu\n", sizeof(obj3)); // 14
}
   

但是,它实现了与仅以三个不同的结构开始类似的效果。考虑在适用的情况下使用替代方法,即将结构分解为多个部分,使用不同的部分,或仅使用普通缓冲区。

考虑下面的方法,该方法将根据提供的数据构建缓冲区。例如:

void build_output_buffer(const MTP_ObjectPropDescTypeDef *def, uint8_t *out)
{
    size_t header_size = offsetof(MTP_ObjectPropDescTypeDef, DefValue);
    memcpy(out, def, header_size);
    out += header_size;
    switch (def->DataType) {
        // Copy the appropriate value, e.g.:
        case DataType_U32: { // For demonstration's sake
            memcpy(out, &def->DefValue, 4);
            out += 4;
        } break;
    }
    size_t footer_offset = offsetof(MTP_ObjectPropDescTypeDef, GroupCode);
    size_t footer_size = sizeof(MTP_ObjectPropDescTypeDef) - footer_offset;
    memcpy(out, (uint8_t*)def + footer_offset, footer_size);
}

并确保传入的缓冲区足够大以容纳所有数据。或者在函数内部分配它并返回它。或者只是跳过缓冲区并使用类似的逻辑将数据发送出去。

【讨论】:

  • 感谢您的回复,这个答案可以提供帮助,但不完全是我想要的,因为在我的示例中我只放置了 3 种类型(uint8_t、uint16_t、uint32_t),但实际上,它可能需要超过3, (int8_t, char[], uint64_t,....)。所以我认为例如制作 8 struct 不是一个好主意。
  • 也许如果我可以添加更多的一种类型(char[] 或 uint8_t[] 大小可变),那么我将只支持它们。 (我需要这个,因为有时我必须存储长字符串(文件名))
  • 谢谢@vmt,我从你的回答中得到启发,为我的问题找到了解决方案。
猜你喜欢
  • 2013-03-17
  • 2020-12-30
  • 2013-09-23
  • 1970-01-01
  • 2021-01-31
  • 2020-05-26
  • 1970-01-01
  • 2018-04-09
  • 1970-01-01
相关资源
最近更新 更多