【问题标题】:serializing structure which includes pointers包含指针的序列化结构
【发布时间】: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_object1get_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-&gt;object1 = 5; 可能未与 int 对齐。
  • 为什么要使用指针?我知道这将是正确的解决方案,但我在现有代码库上工作,我无法将整个实现更改为使用偏移量。
  • “怎么做”。确实,也欢迎任何不同的更合适的方法来实现这一点(将其添加到问题中)。 *header-&gt;object1 = 5; may not be aligned to int。在实际实现中,我们现在只需将所有内容都对齐到 64 位。我更新了问题以使用 char 代替,因此在这个简单的示例中它正确对齐。

标签: c++ cross-platform undefined-behavior c++98


【解决方案1】:

有没有更好的替代方法来实现这一点?

不要费心尝试解决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;
}

【讨论】:

  • 在我的具体用例中,这有两个问题:1)对象列表可能变得非常大,因此更新所有指针的自定义复制函数将变得非常复杂(可能比重写完整使用索引的代码库)2)API应该只返回一个指针和大小供用户复制到一些不同的内存位置。目前还不清楚如何将内存写入存储(这可能会因硬件和操作系统而异)
  • @maxbachmann “API 应该只返回一个指针和大小供用户复制到某个不同的内存位置。”为什么?你有指针。复制指针会使它们指向错误的位置。
  • 另外,为什么要使用指针?为什么不struct Data { int value1; /* etc */ }
  • 1) 指针,因为这就是当前设计结构的方式。正如有人已经指出的那样,正确的解决方案是将所有指针转换为偏移量并存储这些偏移量。 get_object2 将指针破解为索引,这是一种侵入性较小的更改。 2)Alternatively, why pointers at all?我在问题中添加了这个。这些是嵌套结构(通常带有结构列表 my_struct** 在创建结构时会增长)。如果是整数,它们会按照您建议的方式存储。
猜你喜欢
  • 2018-05-30
  • 1970-01-01
  • 1970-01-01
  • 2020-09-12
  • 1970-01-01
  • 1970-01-01
  • 2020-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多