哪些C标准库函数在内部使用了malloc函数

11

我想知道哪些C标准库函数在底层使用mallocfree。 我认为printf会使用malloc,但是当我使用valgrind测试程序时,注意到printf调用没有使用malloc分配任何内存。那么它是如何管理内存的呢?


1
你可以阅读源代码。例如,从git://sourceware.org/git/glibc.git获取glibc源代码的副本。 - buddhabrot
1
我自己阅读了一下,它在堆栈上使用了一个缓冲区。 - buddhabrot
1
我有点匆忙了:如果字符串变得太长,它会开始使用malloc进行动态分配。代码非常难以阅读,所以我将停止对它的评论。 - buddhabrot
5个回答

9
通常情况下,C99标准中唯一可能使用malloc()的例程是标准I/O函数(在中,文件结构和所使用的缓冲区通常被分配为类似于malloc()的方式)。某些语言环境处理可能会使用动态内存。所有其他例程通常不需要动态内存分配。
现在,这些内容是否有正式文件记录呢?我认为没有。没有任何总体限制“库函数不得使用malloc()”。(但是,其他函数存在限制,例如strtok()、srand()和rand();实现可能不使用它们,并且实现可能不使用任何其他可能返回指向静态内存位置的指针的函数。)然而,极其有用的strdup()函数不在标准C库中的原因之一是(据报道)它执行内存分配。至于TR 24731-2中的asprintf()和vasprintf()等例程未能进入C1x,这也不完全清楚,但它可能是一个因素。

5
标准没有对实现提出任何要求,据我所知。
我不确定printf是如何实现的,但就我所知,我想不到它需要动态分配内存的原因。你可以查看你的平台源代码。

1
查看http://www.netlib.org/fp/dtoa.c中malloc的用法很有启发性--即使使用了!Omit_Private_Memory,一些malloc调用仍然存在。 - Doug Currie
@JonathanLeffler:你是指 fprintf 吗?缓冲区不是在其他地方管理吗? - Oliver Charlesworth
1
@Doug:这是我见过的最丑陋的代码之一...(dtoa也不是标准C语言。) - Oliver Charlesworth
我想到了 printf("%.*g", number, precision")。例如尝试使用 number=1e-300precision=1000。你肯定需要动态分配内存来完成这个任务。 - Gunther Piez
我特别考虑了像 fgetc() 这样的函数,但是任何操作 FILE * 后面的标准 I/O 函数都可以。你可以使用 setbuf()setvbuf() 函数提供缓冲区来覆盖系统提供的缓冲区,但是大多数人大多数时间不这样做,标准 I/O 库会为他们提供一个缓冲区。在至少一些系统上,该缓冲区是通过 malloc() 分配的。(今天没有检查就从记忆中工作:我相信 Solaris 就是这样的系统之一。) - Jonathan Leffler
@JonathanLeffler:这很可能是真的。 - Oliver Charlesworth

3
这取决于您使用的 libc 版本。对于 C 规范和实现,不应有任何限制。
例如,newlib 的 printf 通常使用堆栈帧上的内存,但当它确实需要时,它会直接调用内部函数 _malloc_r()
我没有使用过 valgrind,不确定它是否能检测到 _malloc_r() 的使用。

“这取决于您使用的libc版本”是唯一正确的答案。Newlib在其官方文档中实际上按函数列出了所需的操作系统例程。如果一个函数将sbrk列为必需的操作系统函数,则它使用某种形式的malloc - Brian McFarland

2
既然C标准和POSIX标准都不强制实现者使用malloc(),所以你的问题没有通用的答案。然而,每个理智的标准库实现都会在其函数之一中使用malloc(),如果malloc()失败,则会将errno设置为ENOMEM。因此,您可以根据文档确定库函数是否使用malloc()。例如,在我的系统上,mmap()可能会使用malloc(),因为mmap()可能会将errno设置为ENOMEM。
话虽如此,使用valgrind并不是查找特定函数是否调用malloc()的好方法。考虑以下代码片段:
void foo(int x)
{
    if (!x) malloc(1);
}

如果您使用非0的参数调用此函数,则valgrind将不会注意到它可能实际调用了malloc()。可以把valgrind想象成一个虚拟机(因为它就是这样):它不查看您的代码,只看到机器实际执行的内容。


我认为mmap()系统调用不太可能使用malloc()库函数。 - Jonathan Leffler

1

printf不需要一次性形成整个输出字符串,它可以逐个部分地发送到输出,并在遇到格式说明符时,可以将该数据片段作为已形成的数据输出,并继续处理其余字符串。

最多只需要一个本地定义的字符数组(在堆栈上)足够大,以容纳它可以处理的最大整数或浮点数,这并不是很大。


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