c - 灵活数组成员上的结构
问题描述
我正在编写一个 C 程序(g++ 可编译),它必须处理许多不同的结构,所有这些都来自具有预定义格式的缓冲区。格式指定我应该加载哪种类型的结构。这可以使用联合来解决,但是结构大小的巨大差异使我决定使用一个带有 void * 的结构:
struct msg {
int type;
void * data; /* may be any of the 50 defined structures: @see type */
};
问题是我需要 2 个malloc
电话和 2个电话free
。对我来说,函数调用很昂贵而且malloc
很昂贵。free
从用户方面来说,简单的 msgs会很棒。所以我将定义更改为:
struct msg {
int type;
uint8_t data[]; /* flexible array member */
};
...
struct msg_10 {
uint32_t flags[4];
...
};
每当我需要反序列化消息时,我都会:
struct msg * deserialize_10(uint8_t * buffer, size_t length) {
struct msg * msg = (struct msg *) malloc(offsetof(struct msg, data) + sizeof(struct msg_10));
struct msg_10 * payload = (__typeof__(payload))msg->data;
/* load payload */
return msg;
}
并获得该结构的成员:
uint32_t msg10_flags(const struct msg * msg, int i)
{
return ((struct msg_10 *)(msg->data))->flags[i];
}
有了这个改变,gcc(和 g++)发出了一个很好的warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
消息。
我认为这是关于如何以某种有效的方式在 C 中表示一系列消息的常见问题(但我在这里没有找到答案)。
我理解为什么会出现警告,我的问题如下:
- 是否可以在没有警告的情况下实施这样的事情,或者它是否存在内在缺陷?(或不是唯一的:P,我几乎确信我应该重构)
使用以下代码表示消息会更好吗?
struct msg { int type; }; ... struct msg_10 { struct msg; /* or int type; */ uint32_t flags[4]; ... };
如果是,注意事项?我可以始终编写和使用以下内容吗?
struct msg * deserialize_10(uint8_t * buffer, size_t length) { struct msg_10 * msg = (struct msg_10 *) malloc(sizeof(struct msg_10)); /* load payload */ return (struct msg *)msg; } uint32_t msg10_flags(const struct msg * msg, int i) { const struct msg_10 * msg10 = (const struct msg_10 *) msg; return msg10->flags[i]; }
任何其他?
我忘了说这在低级系统上运行,性能是优先考虑的,但总而言之,真正的问题是如何处理这种“多消息”结构。我可能会重构一次,但要更改 50 种消息类型的反序列化的实现......
解决方案
为了避开严格的别名,你可以将你的结构包装在一个联合中。使用 C11,您可以使用匿名结构来摆脱访问“标志”所需的额外级别:
typedef union
{
struct
{
uint32_t flags[4];
};
uint8_t bytes[ sizeof(uint32_t[4]) ];
} msg_10;
现在您可以在不担心严格的别名违规的情况下进行msg_10* payload = (msg_10*)msg->data;
访问,因为联合类型包含与对象的有效类型兼容的类型 ( )。payload
uint8_t[]
但是请注意,在malloc
您通过指向特定类型的指针访问它之前,返回的指针没有有效类型。因此,或者,您可以确保在 malloc 之后访问具有正确类型的数据,这也不会产生严格的别名冲突。就像是
struct msg_10 * msg = malloc(sizeof(struct msg_10));
struct msg_10 dummy = *msg;
dummy
不会用到的地方,就是用来设置有效类型的。
推荐阅读
- c++ - 无法在图形场景上操作 QWidget
- python - 美丽的汤边界没有被取代
- mysql - 使用 JPA 规范的有子句
- mongodb - Mongodb添加条件过滤成聚合
- ios - 在创建 AVAudioPlayers 时了解 Swift 中的 Optionals
- java - 使用 GenerationType.AUTO 的 h2 测试未找到序列“HIBERNATE_SEQUENCE”
- apache-kafka - 生产者分区计数覆盖无效
- javascript - 为什么 readline 卡住了?
- typescript - Typescript 泛型类型参数约束为联合类型
- vba - 使用来自另一张工作表和不同列数的数据创建图表