xmalloc和malloc有什么区别?

71

xmalloc()malloc()在内存分配上有什么区别?
使用xmalloc()有什么优点吗?


2
malloc() 存在于标准 C 中。xmalloc() 不存在。这个 xmalloc() 函数定义在哪里? - Keith Thompson
4
你在哪里看到了xmalloc?这两者的主要区别是malloc是C标准的一部分,而xmalloc不是。 - Praetorian
这是xmalloc使用的示例:https://github.com/kanwei/algorithms/blob/f3920ee34e3183ec0284d80ee8f870e0ecc31c04/ext/algorithms/string/string.c#L29 - film42
那么 @KeithThompson 我们应该使用 malloc 而不是 xmalloc() 吗?如果是,请说明原因。 - simon
@lllllllllllll 在这种情况下,“抛出错误”究竟是什么意思? - Keith Thompson
显示剩余2条评论
6个回答

91

xmalloc() 是一种非标准的函数,其座右铭是“成功或死亡”。如果它无法分配内存,它将终止您的程序并向stderr打印错误消息。

分配本身没有区别; 只有在无法分配内存的情况下行为不同。

请使用 malloc(),因为它更友好和标准化。


1
学到了新东西。谢谢!我只想解释一下xmalloc,然后就这样了。或者说,使用任何对你的情况有意义的方法。例如,如果我正在为特定的操作系统编写程序,并且在OOM时不会丢失数据,那么使用xmalloc是有意义的。 - legends2k

30

xmalloc 不是标准库的一部分,通常是一个对懒惰程序员非常有害的函数名称,常见于许多GNU软件中,如果malloc失败,则调用abort。根据程序/库的不同,它可能还会将malloc(0)转换为malloc(1),以确保xmalloc(0)返回一个唯一的指针。

无论如何,在malloc失败时使用abort行为都非常糟糕,特别是对于库代码而言。最臭名昭著的例子之一是GMP(GNU多精度算术库),每当计算运行出现内存不足时就会中止调用程序。

正确的库级代码应始终通过回退任何部分完成的操作并向调用者返回错误代码来处理分配失败。然后,调用程序可以决定要做什么,这可能涉及保存关键数据。


8
当然,保存关键数据和调用程序可能需要的几乎所有其他操作都需要分配内存页(即使您实际上不进行分配,库函数也需要这样做)。我不认为在任何情况下你都有很大可能性可以恢复。 - Frerich Raabe
46
称那些使用xmalloc()的开发人员为“懒惰的程序员”是不公平的。检测分配错误并非总是可能的。如果你的目标操作系统启用了超额提交,导致malloc()(实质上)几乎从不失败,那么将代码基础充斥着对malloc()返回值的检查是否是一种浪费时间和金钱?在为游戏编写内部级别编辑器时,将其编写得像空中交通管制系统一样只会增加成本,而没有任何真正的好处。在安全关键的系统之外,有时放松一下也是可以的,我个人认为。 - evadeflow
12
请将以下内容翻译成通俗易懂的中文,但不要改变原意:@evadeflow:请告诉那些因为不小心打开一个太大而无法放入内存的第二个文件而丢失文档的人(昨天刚发生在一个不相信我的人身上)。 - R.. GitHub STOP HELPING ICE
12
@evadeflow说的在应用程序层面上是一种有效的方法,但在库级别上则不然。一个库(例如glib)假设调用该库的应用程序没有任何非日志数据且不关心其丢失是不合理的。如果库以这种方式运作,那么它应该在分配函数的文档中突出说明此要求,而不是将其隐藏起来。当然,他们不会这样做,因为没有人会有意使用这样的库;为了让人们使用这样低质量的库,你必须隐藏这个问题。 - R.. GitHub STOP HELPING ICE
6
-pedantic: GCC 5.2 调用 exit,而不是 abort: https://dev59.com/22sz5IYBdhLWcg3w5MA0#31556623,这将运行 atexit 和 GNU xatexit 回调函数。 - Ciro Santilli OurBigBook.com
显示剩余6条评论

27
正如其他人所提到的,xmalloc 很常被实现为一个调用操作系统提供的 malloc 的包装函数,并且如果失败了就会盲目地调用 abort 或者 exit。然而,许多项目包含一个在退出之前尝试保存应用程序状态的 xmalloc 函数(例如请看 neovim)。
个人认为 xmalloc 是一种特定于项目的扩展版的 malloc,而不是退出版的 malloc。虽然我不记得曾经看过一个没有最终调用 abort 或者 exit 的版本,但是有些版本做了更多的事情。
所以回答问题 " xmallocmalloc 之间的区别是什么 ":这取决于情况。 xmalloc 是一个非标准的、特定于项目的函数,所以它可以做任何事情。唯一确定的方法是阅读代码。

9

xmalloc是libiberty的一部分

https://gcc.gnu.org/onlinedocs/libiberty/index.html是GNU工具库。

malloc是ANSI C。

xmalloc经常被包含在许多重要的GNU项目中,包括GCC和Binutils,两者都大量使用它。但是,也可以将其构建为动态库以在您的程序中使用。例如,Ubuntu有libiberty-dev软件包。

xmalloc的文档位于:https://gcc.gnu.org/onlinedocs/libiberty/Functions.html,在GCC 5.2.0上实现为libiberty/xmalloc.c

PTR
xmalloc (size_t size)
{
  PTR newmem;

  if (size == 0)
    size = 1;
  newmem = malloc (size);
  if (!newmem)
    xmalloc_failed (size);

  return (newmem);
}

void
xmalloc_failed (size_t size)
{
#ifdef HAVE_SBRK
  extern char **environ;
  size_t allocated;

  if (first_break != NULL)
    allocated = (char *) sbrk (0) - first_break;
  else
    allocated = (char *) sbrk (0) - (char *) &environ;
  fprintf (stderr,
       "\n%s%sout of memory allocating %lu bytes after a total of %lu bytes\n",
       name, *name ? ": " : "",
       (unsigned long) size, (unsigned long) allocated);
#else /* HAVE_SBRK */
  fprintf (stderr,
       "\n%s%sout of memory allocating %lu bytes\n",
       name, *name ? ": " : "",
       (unsigned long) size);
#endif /* HAVE_SBRK */
  xexit (1);
}

/* This variable is set by xatexit if it is called.  This way, xmalloc
   doesn't drag xatexit into the link.  */
void (*_xexit_cleanup) (void);

void
xexit (int code)
{
  if (_xexit_cleanup != NULL)
    (*_xexit_cleanup) ();
  exit (code);
}

正如其他人所提到的那样,这非常简单:

  • 尝试使用malloc
  • 如果失败
    • 打印错误消息
    • 调用exit

为什么他们这里不使用__builtin_expect?用那个代码,每次成功它都会多做一个jmp...它应该相反(每次失败多做一个jmp) - hanshenrik
@hanshenrik 如果你对其进行基准测试并注意到差异,请告诉我设置,我会在此处提及:https://dev59.com/pGs05IYBdhLWcg3wQvuq#31540623 :-) - Ciro Santilli OurBigBook.com
1
不幸的是,由于现代CPU中分支预测器的大小,对其进行基准测试很困难。要看到差异,您可能需要在每次xmalloc调用之间执行大量操作,以使分支预测器忘记上次采取的分支。=/ - hanshenrik
@hanshenrik 我知道 :-) 然后很可能就无法衡量了。 - Ciro Santilli OurBigBook.com

8

K&R C 中xmalloc.c的一个原始示例

#include <stdio.h>
extern char *malloc ();
void *
xmalloc (size)
    unsigned size;
{
  void *new_mem = (void *) malloc (size);
  if (new_mem == NULL)    
    {
      fprintf (stderr, "fatal: memory exhausted (xmalloc of %u bytes).\n", size);
      exit (-1);
    }
  return new_mem;
}

然后在您的代码头(早期)中放置

#define malloc(m) xmalloc(m)

以在编译之前静默地重写源代码。 (您可以直接调用C预处理器并保存输出来查看重写的代码。)

如果崩溃您的程序不是您想要的,您可以采取不同的方法:

  • 使用垃圾收集器
  • 重新设计您的代码,使其更少地占用内存
  • 在您的程序中具有错误检查代码,以优雅地处理内存不足或其他分配错误。

用户不喜欢由于程序内置的崩溃命令而丢失他们的数据。


1
@MilesRout:这是K&R C - 非常古老的东西。 - orlp
+1,但垃圾回收和C语言可能不太兼容。 - thb
2
在专业应用中,这可能很好。当然,并不是所有的东西都适用,但有些东西它可以很好地工作。另外... 在语言运行时中的垃圾回收就像在C语言中一样,因为运行时本身就是用C编写的。 - Paul Stelian

2
我在IBM AIX上工作时见过xmalloc。 xmalloc是由AIX提供的内核服务。
在我看来,没有什么比函数的man页更能解释一个函数了。因此,我将从man页中复制下面的详细信息:
目的:分配内存。
语法:
caddr_t xmalloc (size, align, heap)
参数:
size:指定要分配的字节数。
align:指定所分配内存的对齐特性。
heap:指定要从中分配内存的堆的地址。
描述:
xmalloc内核服务从由heap参数指定的堆中分配一块内存区域。该区域的长度由size参数指定,按照align参数指定的字节边界对齐。实际上,align参数是所需地址边界的以2为底数的对数。例如,align值为4表示请求将分配的区域对齐到2^4(16)字节边界。
内核提供多个堆供内核扩展使用。两个主要的内核堆是kernel_heap和pinned_heap。内核扩展应在分配不被固定的内存时使用kernel_heap值,并在分配始终需要固定或长时间固定的内存时使用pinned_heap值。在从pinned_heap堆中分配时,xmalloc内核服务将在成功返回之前固定内存。当内存只需要固定一段时间时,应使用pin和unpin内核服务从kernel_heap堆中固定和取消固定内存。从kernel_heap堆中分配的内存必须在释放之前取消固定。不应取消固定从pinned_heap堆中分配的内存。
如果您对这个功能更加感兴趣,可以访问以下链接:IBM AIX Support

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