Valgrind: stdio.h 的函数 "puts" 分配内存

Valgrind: stdio.h's function "puts" allocates memory

我刚刚注意到 valgrind 显示这段代码分配了内存:

#include <stdio.h>

int main(void)
{
    puts("Hello world");
}

使用 gcc (Ubuntu 5.3.1-14ubuntu2.1) 编译时的结果:

==25080== Memcheck, a memory error detector
==25080== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==25080== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==25080== Command: ./a.out
==25080== 
Hello world
==25080== 
==25080== HEAP SUMMARY:
==25080==     in use at exit: 0 bytes in 0 blocks
==25080==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==25080== 
==25080== All heap blocks were freed -- no leaks are possible
==25080== 
==25080== For counts of detected and suppressed errors, rerun with: -v
==25080== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

为什么显示puts分配内存?

标准输出stdout is allowed by the standard to be buffered by default. You can change this with setvbuf.

GNU C 库("glibc",通常的 C 运行time on Ubuntu)在第一次 stdout 用于任何事情时分配内存供内部使用。 (它可能对 stderrstdin 执行相同的操作。)为了看到这种情况发生,运行 你的程序在 gdb 下,并且在它到达 main 后,设置brkmmap 上的断点,它们是最终用于分配内存的系统调用。然后继续。

Reading symbols from ./a.out...done.
(gdb) b main
Breakpoint 1 at 0x4004ea: file test.c, line 5.
(gdb) r
Starting program: /home/zack/a.out 

Breakpoint 1, main () at test.c:5
5     puts("Hello world");
(gdb) b brk
Breakpoint 2 at 0x7ffff7b183c0: file ../sysdeps/unix/sysv/linux/x86_64/brk.c, line 31.
(gdb) b mmap
Breakpoint 3 at 0x7ffff7b1bc00: file ../sysdeps/unix/syscall-template.S, line 84.
(gdb) c
Continuing.

Breakpoint 3, mmap64 () at ../sysdeps/unix/syscall-template.S:84
84  ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0  mmap64 () at ../sysdeps/unix/syscall-template.S:84
#1  0x00007ffff7a9f49c in __GI__IO_file_doallocate (
    fp=0x7ffff7dd6620 <_IO_2_1_stdout_>) at filedoalloc.c:128
#2  0x00007ffff7aac550 in __GI__IO_doallocbuf (
    fp=fp@entry=0x7ffff7dd6620 <_IO_2_1_stdout_>) at genops.c:418
#3  0x00007ffff7aab9d8 in _IO_new_file_overflow (
    f=0x7ffff7dd6620 <_IO_2_1_stdout_>, ch=-1) at fileops.c:820
#4  0x00007ffff7aaab8a in _IO_new_file_xsputn (
    f=0x7ffff7dd6620 <_IO_2_1_stdout_>, data=0x400584, n=11) at fileops.c:1331
#5  0x00007ffff7aa18d8 in _IO_puts (str=0x400584 "Hello world") at ioputs.c:41
#6  0x00000000004004f4 in main () at test.c:5

您可以看到调用堆栈经过了一堆内部内容并最终到达 puts

也许您想知道为什么图书馆这样做。 Chuck Walbourn 的回答暗示了原因:内存将用于缓冲输出,直到第一次使用 stdout 才会分配内存,因为有些程序从不使用 stdout 并且它们不想浪费记忆。 (这个设计决定在 20 年前可能更有意义。)