我在由 perl 脚本生成的文件中有以下声明(在编译期间):
struct _gamedata
{
short res_count;
struct
{
void * resptr;
short id;
short type;
} res_table[3];
}
_gamecoderes =
{
3,
{
{ &char_resource_ID_RES_welcome_object_ID,1002, 1001 },
{ &blah_resource_ID_RES_another_object_ID,1004, 1003 },
{ &char_resource_ID_RES_someting_object_ID,8019, 1001 },
}
};
我的问题是
struct _gamedata
是在编译时生成的,并且 res_table
中的项目数会有所不同。所以我不能提供一个提前声明 res_table
大小的类型。我需要解析这个结构的一个实例,最初我是通过一个指向 char 的指针(而不是将
struct _gamedata
定义为类型。但我正在定义 res_table
。例如
char * pb = (char *)_gamecoderes;
// i.e. pb points to the instance of `struct _gamedata`.
short res_count = (short *)pb;
pb+=2;
res_table * entry = (res_table *)pb;
for( int i = 0; i < res_count; i++ )
{
do_something_with_entry(*entry);
}
我得到了奇怪的结果。我不确定如何声明
_struct gamedata
类型,因为我需要能够在编译时处理 res_table
的可变长度。 最佳答案
理想情况下使用 memcpy(3)
,至少使用类型 _gamedata
,或者定义一个协议(protocol)
我们可以考虑两个用例。在我可能称之为程序员 API 类型中,序列化是一种内部便利,记录格式由编译器和库决定。在更正式的定义和防弹实现中,定义了一个协议(protocol)并编写了一个专用库来可移植地读取和写入流。
最佳实践将根据创建版本化协议(protocol)和开发流 I/O 操作是否有意义而有所不同。
应用程序接口(interface)
从编译器对象序列化流读取时,最好和最完全可移植的实现是声明或动态分配精确或最大大小的 _gamedata
,然后使用 memcpy(3)
从串行流或设备内存或其他任何内容中提取数据。这让编译器分配由编译器代码访问的对象,并让开发人员分配由开发人员(即 char *
)逻辑访问的对象。
但至少,设置一个指向 _gamedata
的指针,编译器会为你做所有事情。另请注意,无论 res_table[n]
数组的大小如何,res_table[]
将始终位于“正确”地址。这不像让它更大改变第一个元素的位置。
一般序列化最佳实践
如果 _gamedata
对象本身在缓冲区中并且可能未对齐,即,如果它不是由编译器或由真实分配器动态分配给 _gamedata
类型的对象,那么您仍然存在潜在的对齐问题,并且唯一正确的解决方案是 memcpy(3)
缓冲区中的每个离散类型。
一个典型的错误是无论如何都使用未对齐的指针,因为它在 x86 上工作(缓慢)。但它可能不适用于移动设备或 future 的架构,或者在内核模式下或启用了高级优化的某些架构上。最好坚持使用真正的 C99。
这是一个协议(protocol)
最后,当以任何方式序列化二进制数据时,您实际上是在定义一个协议(protocol)。因此,为了获得最大的稳健性,不要让编译器定义您的协议(protocol)。由于您使用的是 C,您通常可以在不降低速度的情况下离散地处理每个基本对象。如果作者和读者都这样做,那么只有开发人员必须就协议(protocol)达成一致,而不是开发人员、编译器和构建团队、C99 作者、Dennis M. Ritchie 以及其他一些人。
关于c++ - 结构中的结构数组 - 指针类型是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4009580/