【问题标题】:How to manage memory for inserting and deleting content? [closed]如何管理用于插入和删除内容的内存? [关闭]
【发布时间】:2013-07-26 20:46:39
【问题描述】:

为方便起见,我只使用纯文本示例。例如,对于句子I have a cat,我需要malloc 13 个char 变量槽,以便它存储所有带有最终\0 的字母。

但是,如果现在我想在 cat 之前插入 lovely 怎么办?看来我必须创建一个足够大的新数组并复制所有内容。

更糟糕的是,由于计算机无法预测会添加多少东西,所以每次添加新字母时我似乎都必须做这个重新malloc并复制东西,即为每个字母lovely,结果证明这不是一个聪明的解决方案。 (电脑不会提前知道“可爱”这个词吧?)

一个“更好”的解决方案似乎是首先创建一个足够大的数组,这样每次插入一个新字母时,程序只会复制并移动它后面的所有内容。但是,这仍然是低效的,尤其是当文档很长并且我从头开始添加内容时。

这同样适用于“删除”,每次删除一个字母时,我都必须复制它之后的所有内容并缩小数组大小,看来。

使用节点而不是数组来存储内容似乎是一个同样糟糕的解决方案,因为现在每次我想在内容中间做某事时,我都必须从头开始走一条路。

那么在这种情况下,管理内存的正确或有效方法是什么?我想要在 C 等低级别编程的答案,这需要直接内存分配和取消分配,而不需要已经为您处理所有事情的“魔术”函数或库。

【问题讨论】:

  • 如果你问十个人,你会得到十个不同的答案。我们需要有关您的方案的更多信息。关键因素是什么?性能有多重要,您是否知道整个进程或空间所需的内存总量?你期望这些东西多久增长一次等等。人们写关于这个主题的博士学位。也就是说,一些经典的想法是循环缓冲区,指数增长。如果连续内存不是问题,那么您可以查看其他表示形式,例如链表和树。但这取决于您的要求。
  • 我认为关键因素是快速插入/删除内容的能力。内存总量可能无法预测,因为容量应根据需要增长。您可以将其设想为类似于文档编辑器的东西,您可以在其中快速进入段落中的任何位置并在那里插入/删除内容,并且您不知道用户的写作时间有多长,对吗?这是一个类似的场景。

标签: c memory memory-management operating-system


【解决方案1】:

一种有效的解决方案是使用循环数组列表。

http://en.wikipedia.org/wiki/Circular_buffer

在预先分配了一些大小的数组之后,您还可以跟踪指向列表“开始”的指针(首先是“c”的索引,然后是“l”的索引)。这样,要在开头插入或删除,您可以添加到内存的物理末尾并更改指针。

要索引到数组中,您只需索引到数组[(开始指针 + 索引)%size]。

如果字母数量太大,您仍然需要复制到新数组。 就预分配多少而言,不需要太多时间的系统是每次数组变满时将其大小加倍。这不会增加太多开销。

编辑: 如果您需要将数据插入列表的中间,则循环数组列表将没有用。但是,它对于将数据添加到列表的开头和结尾以及修改或访问中间很有用。

【讨论】:

  • 数组列表要求每个元素彼此相邻,那么如何在数组中间允许insert操作呢?或者,如果我使用链接列表(可能是圆形的),那么我认为似乎没有办法随机索引列表。另外,我认为我无法控制 malloc 实际生成内存的位置,那么如何将内存“添加到内存的物理端”?由于内容的编辑可能是绝对不可预测的,所以我想要一个不会在任何随机编辑后弄乱整个数据结构的解决方案,比如记事本。
【解决方案2】:

使用内存块的链表听起来像是一个很好的中间解决方案。每个节点将是一定大小的内存“页面”。为了加快修改中间页面中的内容,您可以有一个索引数组,其中包含指向整个文档中绝对位置的页面指针。

只应在整个页面为空时执行删除。在那一刻,您应该执行以下操作:

prevPage->next = nextPage;
pageFree(page_to_delete);

【讨论】:

    【解决方案3】:

    如果你想轻松处理字符插入和删除,而不需要一遍又一遍地重新分配,我认为最好的解决方案是双链表。

    在这里查看:DoublyLinkedListExample(我在学校学过,但我认为这个教程很简单地解释了它的工作原理和使用方法)

    这些只是包含数据的结构(节点)、指向前一个元素的指针和指向下一个元素的指针。如果您不明白它是如何工作的,请先查看简单链表的教程,然后对您来说会更容易。

    只是练习它,因为一开始很难理解。继续训练,你会达到目标的:)

    【讨论】:

    • 我对使用链表犹豫不决,因为它不允许随机访问。如何快速进入列表中间,而不是每次都从列表的任一端选择路线?
    • 如果你想进入中间位置,我认为无论如何你都必须翻遍你的名单。以前,如果您想快速进入列表,可以对其进行排序,但我认为这是更快的方法
    • 我很困惑。在这种情况下,为什么我需要对列表进行排序?对一个句子列表进行排序对我来说没有意义。它不是简单数字的数组/列表。
    • 抱歉,我没这么理解。忘记排序。
    【解决方案4】:

    鉴于您在 cmets 中澄清您的用例的回应,我的建议是考虑内容的链接列表,在您的纯文本示例的隐喻中,链接列表的元素是单词或段落或页面,并且单词本身是连续的数组。

    虽然它们之间的导航速度不是很快,但您的性能要求似乎是快速插入和删除。通过使用小的连续单词,通过控制小的n,重新分配/缩小和复制内容的O(n) 成本被最小化。这是通过拥有许多n 来实现的,这些n 是链表元素。

    这将通过内容空间局部性的“单个”部分带来的性能改进结合在一起,同时允许您选择更高级别的列表/树结构来帮助获得时间局部性的好处。

    这实际上没有解决的一件事是在处理这些数据之后需要对这些数据执行什么操作,以及真正可以容忍的性能水平。持续的 malloc 调用对延迟不利,因为它是阻塞系统调用;因此您可以进一步考虑使用已经提到的另一种解决方案,例如循环缓冲区或管理您自己的更大内存块来将自己分配给这些元素。这样,您只需要在需要更大的内存块来使用时才需要 malloc,并且仍然不必从一页到另一页重新复制所有内容,而只是一个不适合的较小块。

    再次,正如我在评论中所说,人们撰写关于这类事情的论文,它是操作系统设计和系统理解的主要组成部分。所以,把这一切都放在一粒盐里。有很多事情需要考虑,这里无法涵盖。

    【讨论】:

      【解决方案5】:

      尚不完全清楚您的用例是什么。

      既然你提到了文本操作和高效的插入、删除和随机访问操作,我猜你可以使用rope data structure,它是一个二叉树,它基本上在其节点中存储短字符串片段(大致)。有关详细信息,请参阅链接文章。

      【讨论】:

        猜你喜欢
        • 2015-11-01
        • 2012-06-14
        • 2011-07-19
        • 2018-08-17
        • 2010-09-17
        • 2012-11-15
        • 2010-12-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多