views:

169

answers:

2

Hi,

I do not see the reason of the leak below.

#include <iostream>
#include <cstdlib>

int fail(const std::string str)
{
    std::cerr<< str << std::endl;
    exit(1);
}

const std::string usage()
{
    std::string a = "a";
    return a;
}   

int main()
{
    fail(usage());
    return 0;
}

Valgrind says:

==7238== 14 bytes in 1 blocks are possibly lost in loss record 1 of 1
==7238==    at 0x402377E: operator new(unsigned) (vg_replace_malloc.c:224)
==7238==    by 0x40E7C03: std::string::_Rep::_S_create(unsigned, unsigned, 
std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.10)
==7238==    by 0x40E8864: (within /usr/lib/libstdc++.so.6.0.10)
==7238==    by 0x40E89D5: std::string::string(char const*, std::allocator<char> const&) 
(in /usr/lib/libstdc++.so.6.0.10)
==7238==    by 0x80488EC: usage() (main.cpp:12)
==7238==    by 0x804897C: main (main.cpp:18)
==7238== LEAK SUMMARY:
==7238==    definitely lost: 0 bytes in 0 blocks.
==7238==      possibly lost: 14 bytes in 1 blocks.
==7238==    still reachable: 0 bytes in 0 blocks.
==7238==         suppressed: 0 bytes in 0 blocks.

The problem is in the fail() function. As it exits(), the memory is leaked.

If I comment out exit(1); then there is no possible leak.

Also, if I change the signature from int fail(const std::string str) to int fail(const char* str)

then there is no possible leak as well. I don't like this solution, as I am using fail(string + (LINE)) type of things, but regardless, what is going on here?

I will be happy if someone can explain.

Thanks!

(upps. same question asked before I guess, sorry! http://stackoverflow.com/questions/1901322/valgrind-report-memory-leak-when-assign-a-value-to-a-string)

+9  A: 

When you call exit(), the destructors of automatic objects (local variables) do not get called.

In your specific example, the std::string destructor is not called, so the memory owned by the std::string is never deallocated.

The reason there is no leak if you have fail() take a const char* is that there is no destructor for const char*; nothing is deallocated when a pointer is destroyed. If the pointer points to dynamically allocated memory, then that memory must be deallocated (by you) before the program exits, otherwise you have a memory leak. If it points to a string literal, then there is no memory leak because string literals have static storage duration (that is, they exist for the entire lifetime of your program).

James McNellis
This document says you're wrong : http://www.cplusplus.com/reference/clibrary/cstdlib/exit/
Klaim
@Klaim: I don't see where that document says that I am wrong, but if it does then it is wrong. To quote the C++ standard (§18.3/8): "Automatic objects are not destroyed as a result of calling `exit()`."
James McNellis
Then that sentence "Terminates the process normally, performing the regular cleanup for terminating processes." is misleading? Anyway the standard says you're right, so you're right. (cant vote up until you edit though)
Klaim
By the way, I found a more complex duplicate of the question with the same answer : http://stackoverflow.com/questions/1901322/valgrind-report-memory-leak-when-assign-a-value-to-a-string
Klaim
@Klaim: The "regular cleanup" means destruction of static objects, calling all the functions registered using `atexit()`, and closing any streams.
James McNellis
@Klaim: here is a good explanation: http://www.stevejay.net/coding_article_quitting_time.html#exit. In short: exit() will clean up static storage, but things in automatic storage (stack) will not be touched by exit.
frunsi
+2  A: 

James McNellis already wrote a correct answer. But I'd like to add some things:

  1. It is always a good thing to write software in a way that it does not have to call exit() - this helps you improve the overall design, specify and understand object lifetimes (except in very special - rather low level - cases..).

  2. As you see here, this is important when using tools like valgrind! A "clean" shutdown procedure makes you feel safe, then everything worked fine, as you expected ;) A clean shutdown procedure in exceptional cases should be a requirement of every software.

You should consider to throw an exception instead of calling some fail() function. When an exception is thrown, the stack will be unwound, so the std::string destructor in your case would be called.

frunsi