简单的C语言动态内存分配

14

虽然在C/C++中有很多不同的复杂实现方法来实现malloc / free,但我正在寻找一种非常简单并且(特别是)小型的实现方法,它可以在固定大小的缓冲区上运行,并支持realloc。线程安全等功能不需要,我的对象小而且大小变化不大。您能推荐任何实现吗?

编辑:

我将在接收器的通信缓冲区中使用该实现来传输具有可变大小(接收器未知)的对象。分配的对象寿命不长,但可能同时使用几个对象。

由于每个人都建议使用标准的malloc函数,因此我应该重新表达我的问题。我需要在缓冲区上实现“最简单”的malloc函数,以便我可以开始根据自己的需要进行优化。也许最初的问题不太清楚,因为我不是在寻找经过优化的malloc函数,我只想要一个轻量级的实现。我不想从glibc-malloc函数开始扩展,而是希望使用一个轻量级的实现。


3
您能否澄清一下您所说的“支持realloc”的含义?符合规范的realloc实现只使用malloc、free和memcpy,这样的实现是否符合您的要求?严格来讲,还有另一种符合规范的实现会始终返回NULL,但显然您并不指的是那种情况。 - Pascal Cuoq
2
你需要解释为什么不能使用编译器自带的malloc函数。如果这是因为你在某种环境下工作,你的编译器没有提供malloc函数(例如嵌入式软件),你需要描述这个环境,以便回答对你有用。目前,除了Martin York的建议使用编译器捆绑的malloc函数外,这个问题无法令人满意地回答。 - David Thornley
是的,一个非常简单的realloc(如果缓冲区中有足够的空间,则不返回NULL)是可以接受的。 - Thomas
1
realloc的支持似乎与在固定大小的缓冲区上工作不一致 - 你是指“在可用内存的固定子集中工作”还是“使用固定大小的对象” - 还是其他什么? - Steve Townsend
@Thomas - 谢谢!你使用的是哪个平台? - Steve Townsend
显示剩余6条评论
7个回答

28

Kerninghan & Ritchie 在他们的 C 语言书中提供了一个小型的 malloc / free 函数 - 这正是我在寻找的 (这里找到了重新实现的版本)。我只会添加一个简单的 realloc。

如果有其他同样简单明了的实现建议(例如使用双向链表),我仍然很乐意听取。


@Ben:OP自己回答了问题。 :-) - R.. GitHub STOP HELPING ICE
你可以分享一下你的realloc代码吗? - pts

16

我建议使用随编译器捆绑的标准库提供的方法。

需要注意的是,没有合法的方法可以重新定义malloc/free函数。


1
@Thomas:任何事情都是可能的。但其他实现是为解决其实施者所遇到的特定问题而设计的。除非您具有与他们完全相同的特征,否则它不太可能有所帮助。 - Martin York
1
@Thomas:很可能你的C库附带的malloc版本已经经过多年的精心优化,以便在大多数常见使用情况下尽可能快地运行。你可能会发现它已经足够快了。 - JeremyP
我绝对不否认标准实现是可以的 - 我只是想看看区别,而且我没有能够在谷歌/源代码中找到那个特定的“小而简单”的实现。 - Thomas
请问您能否详细说明重新定义malloc/free的合法性?使用自己的分配器目标文件而不是stdlib的目标文件链接,这样做是否“合法”? - ulidtko
@kris: 我有完全相同的问题。即使使用gcc nano库,提供的malloc/free仍然浪费了近100字节的RAM,而我确实需要它们。我真的无法理解为什么人们不能回答问题,而是否认别人的问题。 - calandoa
显示剩余4条评论

4
你的编译器自带的malloc/free/realloc函数几乎肯定比你要插入的一些函数更好。对于固定大小的对象,有可能进行改进,但通常不涉及尝试替换malloc,而是通过补充内存池来实现。通常,您可以使用malloc获取一大块内存,然后将其划分为适当大小的离散块,并管理这些块。

4

CCAN中有一个相对简单的内存池实现:

http://ccodearchive.net/info/antithread/alloc.html

这看起来符合您的要求。确实,alloc.c代码有1230行,但其中很大一部分是测试代码和列表操作。它比您实现的代码稍微复杂一些,但良好的内存分配确实很复杂。


3

我觉得你正在寻找一个内存池。 Apache Runtime库 有一个非常好的内存池,而且它也是跨平台的。

它可能不是非常轻量级,但源代码是开放的,你可以修改它。


1
是的,apr_pools.c 中的 2601 行代码并不轻量级。也许我应该花一个小时写自己的小型实现。我只是在想,也许很多人已经写了这样一个基本的 malloc,所以我可以重用并扩展它。 - Thomas

3

通常情况下,我不会重新发明分配函数,除非我的内存使用模式不受malloc等支持,或者内存可以分成一个或多个预分配的区域,每个区域包含一个或两个LIFO堆栈(释放任何对象都会释放在其之后分配的同一堆栈中的所有对象)。在后一种情况的常见版本中,只有在释放所有内容时才会释放任何东西;在这种情况下,malloc()可能会被有用地重写为:

char *malloc_ptr;
void *malloc(int size)
{
  void *ret;
  ret = (void*)malloc_ptr;
  malloc_ptr += size;
  return ret;
}

每个分配对象零字节开销。使用自定义内存管理器的一个例子是应用程序生成可变长度的测试记录和结果记录(可能更长或更短);该应用程序需要支持在批处理过程中获取结果并添加更多测试。测试以从缓冲区底部开始的递增地址存储,而结果以从顶部开始的递减地址存储。作为后台任务,当前测试后的测试将被复制到缓冲区的开头(由于只有一个用于读取处理测试的指针,因此复制逻辑将根据需要更新该指针)。如果应用程序使用malloc/free,则可能会导致测试和结果的分配交错,从而导致内存碎片,但使用该系统则不存在这种风险。


1

回应建议,先测量性能,只有在性能不佳时才进行专门化 - 应该很容易将您的malloc/free/reallocs抽象化,以便替换变得简单。

鉴于专用平台,我无法评论运行时的有效性。如果您自己调查,则对象池(请参见其他答案)或小对象分配(例如Lokithis)值得一看。第二个链接还对此问题发表了一些有趣的评论。


第二个链接(“micro-allocator”)看起来很不错,但它仍然有太多的功能:线程支持、不同大小块的不同处理等等。我真的不需要这些。我想要大约100-200行代码来完成基本任务, 不新增其他任何功能,但是必须保证稳定并经过测试。 - Thomas
考虑到你似乎正在按照FIFO的方式处理传入数据,这个可能适用于你的情况吗?小而完美 :-) http://www.boost.org/doc/libs/1_44_0/libs/circular_buffer/doc/circular_buffer.html - Steve Townsend
是的 - 我已经开始编写一个简单的malloc,并使用了循环缓冲区,但我意识到有一个空闲块列表不够用了,而且我没有考虑使用两个列表或扩展我的“头”结构来分配块,所以我认为最好从现有的malloc实现开始。 - Thomas
我怀疑无论你找到什么有效的代码,它都会大于你所期望的代码行数。同时,你的最终结果也将是如此。 - Steve Townsend

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接