我正在编写一个C程序(g ++编译),该程序必须处理许多不同的结构,所有结构都来自具有预定义格式的缓冲区。该格式指定了我应加载的结构类型。这可以使用联合来解决,但是结构尺寸的巨大差异使我决定选择其中带有*的结构:
struct msg {
int type;
void * data; /* may be any of the 50 defined structures: @see type */
};
问题是我需要2个malloc
呼叫和2个free
。对我来说,函数调用是昂贵的,而malloc
是昂贵的。从用户方面来说,简化free
消息是非常好的。所以我将定义更改为:
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中表示一系列消息的问题。
我了解为什么会出现警告,我的问题如下:
使用下面的代码来表示消息会更好吗?
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种消息类型的反序列化的实现...
答案 0 :(得分:3)
要躲避严格的别名,可以将结构包装在并集内。使用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
的地方,仅是设置有效类型的地方。
答案 1 :(得分:2)
我正在编写C程序(
g++
可编译)
这是一个误会。
C源文件应该由gcc
编译(不是由g++
编译)。 C ++源文件应由g++
编译(而不是gcc
)。请记住,GCC的意思是Gnu Compiler Collection(并且在适当配置时还包含gfortran
和gccgo
等。)因此,Fortran源文件应使用gfortran
进行编译(如果使用GCC),Go源文件应使用gccgo
进行编译(如果使用GCC),Ada代码应进行编译gnat
(如果使用GCC),依此类推。
了解有关Invoking GCC的信息。通过还将-v
传递到gcc
或g++
编译器命令(它应该调用cc1
编译器,而不是cc1plus
编译器命令)来检查发生了什么。>
如果您坚持使用g++
(不是gcc
)编译C99或C11源文件,这是恕我直言的错误和混乱,请确保至少传递-std=c99
(或-std=gnu11
等...)和-x c
标志。
但是您确实应该修复 build automation或构建过程以使用gcc
(而不是g++
)来编译C代码。真正的问题就是这一点(Makefile
中的某些错误或其他问题)。
在链接时,如果您混合使用C和C ++代码,请使用g++
。
请注意,flexible array members在C ++中不存在(甚至不存在),即使在将来的C ++ 20中也是如此。在C ++中,您可以使用0作为其声明的大小,例如代码:
#ifdef __cplusplus
#define FLEXIBLE_DIM 0
#else
#define FLEXIBLE_DIM /*empty flexible array member*/
#endif
然后声明:
struct msg {
int type;
uint8_t data[FLEXIBLE_DIM]; /* flexible array member */
};
但这只能起作用,因为uint8_t
是POD,并且您的g++
编译器可能(有时)发出“缓冲区溢出”或“索引超出范围”警告(您应该从不取决于该sizeof
字段的编译时间data
。
答案 2 :(得分:2)
您当然可以使用Makros建立类似的东西;
message_header
用作所有消息类型的父结构。作为此类结构的第一个成员,它们共享相同的地址。因此,在创建msg(int)
并将其强制转换为message_header
之后,您可以通过对其调用free来释放它。
(C ++的工作原理相同)
这是您想要的吗?
struct message_header {
int type;
};
#define msg(T) struct {struct message_header header; T data}
struct message_header* init_msg_int(int a) {
msg(int)* result = (msg(int)*)(malloc(sizeof(msg(int))));
result->data = a;
return (struct message_header*)result;
}
int get_msg_int(struct message_header* msg) {
return ((msg(int)*)msg)->data;
}
void free_msg(struct message_header* msg) {
free(msg);
}
答案 3 :(得分:0)
没有malloc,没有释放,没有别名,函数调用是内联的,对于简单或未填充的自然对齐结构,,内联函数等效于memcpy或寄存器副本(对于简单的小型结构)结构。对于更复杂的结构,编译器会进行所有繁重的工作。
由于要进行反序列化,缓冲区中的byes的alignemnet可能会被打包并且无法自然对齐。看看packed_struct.h(https://elixir.bootlin.com/linux/v3.8/source/include/linux/unaligned/packed_struct.h)的Linux内核源代码
为每个msg_0..msg_10..msg_(n-1)推出一个函数,而不是u16,u32,u64。从源文件中可以看到,使用一些简单的宏简化每个未对齐类型和内联函数的时机已经成熟。使用您的示例名称
struct msg {
int type;
};
...
struct msg_10 {
struct msg MsgStruct; /* or int type; */
uint32_t flags[4];
...
};
#define UNALIGNED_STRUCT_NAME(msg_struct_tag) \
UNA_##msg_struct_tag
#define DECLARE_UNALIGNED_STRUCT(msg_struct_tag) \
struct UNALIGNED_STRUCT_NAME(msg_struct_tag) \
{struct msg_struct_tag x;} __attribute__((__packed__))
#define DESERIALIZE_FN_NAME(msg_struct_tag) \
deserialize_##msg_struct_tag
#define CALL_DESERIALIZE_STRUCT_FN(msg_struct_tag, pbuf) \
DESERIALIZE_FN_NAME(msg_struct_tag)(pbuf)
#define DEFINE_DESERIALIZE_STRUCT_FN(msg_struct_tag) \
static inline \
struct msg_struct_tag DESERIALIZE_FN_NAME(msg_struct_tag)(const void* p) \
{ \
const struct UNALIGNED_STRUCT_NAME(msg_struct_tag) *ptr = \
(const struct UNALIGNED_STRUCT_NAME(msg_struct_tag) *)p; \
return ptr->x; \
}
...
DECLARE_UNALIGNED_STRUCT(msg_9);
DECLARE_UNALIGNED_STRUCT(msg_10);
DECLARE_UNALIGNED_STRUCT(msg_11);
...
...
DEFINE_DESERIALIZE_STRUCT_FN(msg_9)
DEFINE_DESERIALIZE_STRUCT_FN(msg_10)
DEFINE_DESERIALIZE_STRUCT_FN(msg_11)
...
反序列化消息10
struct msg_10 ThisMsg = CALL_DESERIALIZE_STRUCT_FN(msg_10, buffer);
或者在缓冲区9的字节9处反序列化消息13
struct msg_13 OtherMsg = CALL_DESERIALIZE_STRUCT_FN(msg_13, &(buffer[9]));