C:重新分配结构的更新地址,但不分配结构中的指针地址

时间:2020-07-30 09:56:37

标签: c pointers realloc

我有一个指向结构的指针,该结构的大小逐渐增加,因此我在需要时重新分配。在某一点之后,realloc返回一个新地址,并移动结构。但是,问题在于,结构中有更多的指针,这些指针指向该块内另一个结构的地址。当该块移到新地址时,指针地址保持不变,因此现在所有指向原始块中的无效位置。

是否有减轻此问题的好方法?我能想到的是,不是在结构中存储指针,而是存储偏移量值以将指针定向到下一个结构。这是可行的,但我想在这种情况下人们必须进行某种形式的正常操作,因为这种情况肯定不会罕见吗?

否则,如果与realloc一起使用,则在分配的块中有一个指向该块中另一个地址的指针是毫无用处的

4 个答案:

答案 0 :(得分:0)

没有内置的解决方案,因为realloc()的一般用例是在“所有者”使用某些不透明内容时增加其大小。运行时不知道是谁引用了它,或者不知道动态内存的其他块中隐藏了什么,因此所有引用都需要手动更新。

不幸的是,您在注释中描述程序的体系结构的方法,即使是最传统的方法(建立一个将“句柄”或其他虚假指针映射到真实指针的哈希表或等效的数据结构)也不会让您走得更远,因为听起来您仍然需要遍历内存并每次重新构建整个表。

如果您愿意一次为所有对象分配内存,那么最好的选择可能是使用数组索引将列表托管在数组(或您像数组一样对待的内存)中而不是指针。

也就是说,由于您在评论中提到的担心是释放链表,因此请硬着头皮手动进行操作。取消分配只是标记了可用空间,因此即使有数千个元素也不会花费很多时间,而且即使 exit 程序运行缓慢,也不会有人在意数十亿个元素。

答案 1 :(得分:0)

最基本的是,如果只有一个结构链接列表,则只需知道第一个结构的地址和当前正在读取的结构的地址,即可浏览列表的所有成员

如果您有多个链接列表,则需要存储相同的数据,但该列表除外。

例如(未经检查的伪代码):

struct ListIndexEntry
{
  ListEntry* startOfLinkedList;
  ListEntry* currentLinkedListEntryBeingRead;
};

//fixed size data for structure, but could be variable if the structure has a size of data indicator.
stuct ListEntry
{
  ListEntry* previousEntry;
  ListEntry* nextEntry;
  char Data[255]; 
}

struct ListIndexEntry ListTable[20]; //fixed table for 20 linked lists

因此,您为第一个链接列表的第一个元素分配了内存,并相应地更新了ListTable中的第一个条目。

好的-我在输入文字时看到的评论中您可能已经知道了。

您可以使用多层链表来加快大型链表的导航。因此,您可以分配一个大的内存块来存储多个ListEntry,例如

struct ListBlock
{
  void* previousBlock;
  void* nextBlock;
  ListEntry Entries[2000]; 
}

然后,您为ListBlock分配内存,并将ListEntry连续存储在其中。

ListIndexEntry现在应该是:

struct ListIndexEntry
{
  ListBlock* startOfLinkedList;
  ListBlock* currentListBlock;
  ListEntry* currentLinkedListEntryBeingRead;
}

然后您可以通过ListBlock粗略分配内存,导航和取消分配内存。

您仍然需要手动处理表中的指针和当前的ListBlock

诚然,这意味着列表的最后ListBlock中可能有一些未使用的内存,因此您需要仔细选择ListBlock.Entries的大小。

答案 2 :(得分:0)

根据描述,我将此模式描述为一个对象池。您不想分配许多小结构,而是将它们存储在单个连续数组中的想法意味着您基本上需要高效的malloc。但是,对象池背后的前提(或“契约”)是调用者正在从池中“借用”项目,并且要求借用的对象在返回到池之前必须不被池实现破坏。

因此,您可以做三件事:

  1. 使此结构数组与实现耦合,并存储数组索引而不是指针。这意味着链接列表代码“知道”它使用了内部的数组,并且编写了整个代码来适应这一情况。访问列表项始终是通过索引数组来完成的。

  2. 编写一个内存池,该内存池将通过向池中添加结构块而不是调整大小和移动现有块来根据需要增长。这是一个有用的数据结构,我已经使用了很多,但是当您很少需要增长并且需要大量借入和返回时,它是最有用的-它比malloc更快,不会出现碎片,并且可能表现得更好参考位置。但是,如果您很少需要将项目返回到池中,则与一般的malloc实现相比,这可能是过大了。

  3. 很显然,第三种方法是只使用malloc,而不必担心它,除非您对其进行了概要分析并将其确定为实际瓶颈。

答案 3 :(得分:0)

正如评论员指出并确认的那样,“不要混合使用嵌入式指针和realloc()。这从根本上来说是一个糟糕的设计。” 。我最终更改了嵌入式指针,改为保留偏移值,以将指针定向到适当的节点。许多其他人都发表了很棒的 list 建议,但由于我使用的是 trie (我来不及说这个,我的错),我发现这是一个更容易解决的问题,而没有完全解决重做我代码的所有常规基础