I just noticed that valgrind is showing that this code allocates memory:
#include <stdio.h>
int main(void)
{
puts("Hello world");
}
Result when compiled with 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)
Why does it show that puts
allocates memory?
The GNU C Library ("glibc", the usual C runtime on Ubuntu) allocates memory for internal use the first time stdout
is used for anything. (It probably does the same for stderr
and stdin
.) To see this happening, run your program under gdb
and, after it reaches main
, set breakpoints on brk
and mmap
, which are the system calls that ultimately get used to allocate memory. Then continue.
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
You can see that the call stack goes through a bunch of internal stuff and ends up at puts
.
Perhaps you are wondering why the library does this. The reason is hinted at in Chuck Walbourn's answer: the memory will be used for buffering output, and it isn't allocated until stdout
is used for the first time, because some programs never use stdout
and they didn't want to waste memory. (This design decision may have made more sense 20 years ago.)
Standard output stdout is allowed by the standard to be buffered by default. You can change this with setvbuf.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With