【发布时间】:2021-07-28 16:25:06
【问题描述】:
问题描述
我必须序列化以下结构并将其存储在不同的内存位置(例如闪存)。当新的内存位置为只读时,该解决方案必须有效:
------------
| Header |
------------
| object 1 |
------------
| object 2 |
------------
| object n |
------------
Header 结构具有指向已分配对象的指针,例如
struct Header {
int* object1;
};
我知道一个合适的解决方案是存储偏移量而不是指针,但是我在现有的代码库上工作,如果没有其他方法可以实现这一点,这只是一个选择。上面的例子非常简单。在实际使用中,对象列表由自定义内存池实现使用。它可以包含数百个嵌套结构,其中包括彼此的指针(用户之间的顺序+数量差异很大。它可以是几千字节到几兆字节的数据)。最后,实现必须能够返回指针 + 大小,以便用户可以存储结构,例如一闪而过。
当前解决问题的方法
为了实现这一点,我存储了 Header 的原始基指针,并在将结构复制到新的内存位置后从新的基指针中减去它:
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_object1 和get_object2 的以下原因:
get_object1:
+偏移量可以计算一次,然后重复使用
- 减去指向两个不同数组的指针(一个在闪存中,一个指向旧内存位置),这可能是未定义的行为。见https://en.cppreference.com/w/cpp/types/ptrdiff_t:
只有指向同一数组元素的指针(包括数组末尾之后的指针)才能相互减去。
- 偏移量大于数组大小,根据 C++11 规范的§5.7 ¶5,这可能是未定义的行为:
如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则计算不应产生溢出;否则,行为未定义。
get_object3:
+ 偏移量和最终指针均在数组边界内计算。因此它不应该有未定义的行为。
问题
我更喜欢get_object1 中的实现,因为我可以重用偏移量。但是我假设,这个实现有未定义的行为。 get_object2 实现中是否存在我没有考虑的类似问题?当 Header 不是标准布局类型时,这是否可以保证正常工作?有没有更好的替代方法来实现这一点?
【问题讨论】:
-
我猜你已经说不出话来了 :)
-
为什么要使用指针?如果您在内存块中从块开始的任意偏移处分配对象,请存储偏移量,而不是指针。
-
这越来越复杂了with each post。为什么不问“这段代码是否未定义”,而是问“怎么做”? ||
*header->object1 = 5;可能未与int对齐。 -
为什么要使用指针?我知道这将是正确的解决方案,但我在现有代码库上工作,我无法将整个实现更改为使用偏移量。
-
“怎么做”。确实,也欢迎任何不同的更合适的方法来实现这一点(将其添加到问题中)。
*header->object1 = 5; may not be aligned to int。在实际实现中,我们现在只需将所有内容都对齐到 64 位。我更新了问题以使用 char 代替,因此在这个简单的示例中它正确对齐。
标签: c++ cross-platform undefined-behavior c++98