如何在C中制作动态结构
How to make a dynamic structure in C
例如,如果我有这样的结构:
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 中有一个元素,具体取决于我的数据类型。
例如,如果我的数据类型是 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;
除非您将数据布局分成多个部分,或者更改数据布局,以便您的可变大小类型位于末尾,否则您的建议无法完成(好吧,无论如何合理)。
有了这个,我们就可以进入不合理的部分了。您可以(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);
}
并确保传入的缓冲区足够大以容纳所有数据。或者在函数内部分配它并 return 它。或者只是跳过缓冲区并使用类似的逻辑将数据发送出去。
例如,如果我有这样的结构:
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 中有一个元素,具体取决于我的数据类型。 例如,如果我的数据类型是 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;
除非您将数据布局分成多个部分,或者更改数据布局,以便您的可变大小类型位于末尾,否则您的建议无法完成(好吧,无论如何合理)。
有了这个,我们就可以进入不合理的部分了。您可以(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);
}
并确保传入的缓冲区足够大以容纳所有数据。或者在函数内部分配它并 return 它。或者只是跳过缓冲区并使用类似的逻辑将数据发送出去。