包括指针的序列化结构



问题描述

我必须序列化以下结构,并将其存储在不同的内存位置(例如闪存(。当新的内存位置是只读的时,解决方案必须工作:

------------
| Header   |
------------
| object 1 |
------------
| object 2 |
------------
| object n |
------------

Header结构具有指向已分配对象的指针,例如

struct Header {
int* object1;
};

我知道一个合适的解决方案是存储偏移量而不是指针,但我在现有的代码库上工作,如果没有其他方法可以实现这一点,这只是一种选择。上面的例子非常简单。在实际使用中,对象列表由自定义内存池实现使用。它可以包括数百个嵌套结构,其中包括指向彼此的指针(用户之间的顺序+数量差异很大。它可以是几KB到几MB的数据(。最终,实现必须能够返回指针+大小,这样用户就可以将结构存储在例如闪存中。

当前解决问题的方法

为了实现这一点,我存储头的原始基指针,并在将结构复制到新的内存位置后从新的基指针中减去它:

struct Header {
char* base_ptr;
char* object1;

char* get_object1(char* new_base_ptr) {
ptrdiff_t offset = (ptrdiff_t)new_base_ptr - (ptrdiff_t)base_ptr;
return (char*)object1 + offset;
}

char* get_object2(char* new_base_ptr) {
ptrdiff_t offset = (ptrdiff_t)object1 - (ptrdiff_t)base_ptr;
return new_base_ptr + offset;
}
};
int main() {
void* alloc = malloc(sizeof(Header) + sizeof(char));
Header* header = new(alloc) Header;
header->base_ptr = (char*)alloc;
header->object1 = (char*)alloc + sizeof(Header);
*header->object1 = 5;
std::cout << (int)*header->get_object1((char*)alloc) << std::endl;
std::cout << (int)*header->get_object2((char*)alloc) << std::endl;
void* alloc2 = malloc(sizeof(Header) + sizeof(char));
memcpy(alloc2, alloc, sizeof(Header) + sizeof(char));
free(alloc);
Header* header2 = (Header*)alloc2;
std::cout << (int)*header2->get_object1((char*)alloc2) << std::endl;
std::cout << (int)*header2->get_object2((char*)alloc2) << std::endl;
}

我确实看到了实现get_object1get_object2的以下原因:

获取对象1(_O(:

+偏移量可以计算一次,然后重复使用

-减去指向两个不同数组的指针(一个在闪存中,一个指向旧内存位置(,这可能是未定义的行为。看见https://en.cppreference.com/w/cpp/types/ptrdiff_t:

只有指向同一数组元素的指针(包括经过数组末尾的指针(才能相互减去。

偏移量大于数组大小,根据C++11规范的§5.7¶5,这可能是未定义的行为:

如果指针操作数和结果都指向同一数组对象的元素,或者超过数组对象最后一个元素的元素,则求值不应产生溢出;否则,行为是未定义的。

get_object3:

+偏移量和最终指针都是在数组的边界内计算的。因此,它不应该有未定义的行为。

问题

我更喜欢get_object1中的实现,因为我可以重用偏移量。然而,我认为这个实现有未定义的行为。get_object2实现中是否存在我没有说明的类似问题?当页眉不是标准布局类型时,这是否保证能正常工作?有没有更好的替代方法来实现这一点?

有更好的替代方法吗?

不要试图绕过memcpy。编写自己的复制函数。

Header * copyHeader(const Header * source, void * where) {
Header * dest = new (where) Header;
dest->object1 = new (where + sizeof(Header)) int(source->object1);
return dest;
}

和/或工厂

Header * makeHeader(void * where) {
Header * dest = new (where) Header;
dest->object1 = new (where + sizeof(Header)) int;
return dest;
}

最新更新