1

At this function i need to return a char* variable, before i return it, I print it and it prints well. what happened to the variable that makes the return variable wrong and why?

The function:

const char* NameErrorException::what() const throw()
{
     std::string str = "NameErrorException: name \'";
     str += _name;
     str += "\' is not defimed";
     std::cout << str.c_str()<< std::endl; //Prints good
     return str.c_str(); 
}

The print code:

catch (std::exception& ex)
{
   //Prints something like "▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌♀·┘v♦"
    std::cout << ex.what() << std::endl;
}

(NameErrorException inheritor from exception)

Thank you!

swis11
  • 53
  • 1
  • 6
  • 7
    Possible [duplicate](http://stackoverflow.com/questions/6456359/what-is-stdstringc-str-lifetime). – Mohamad Elghawi Feb 12 '16 at 10:22
  • 1
    str is a local variable and like all local variables, it will be collected at the end of the method. If you return a valid pointer in the method, it will be invalid outside the method if it came from a local variable unless you allocated the space for it. This holds true also for returned pointers like c_str() unless you have reason to think that you're being returned a pointer that won't get destroyed later which is most certainly not your case. – Neil Feb 12 '16 at 10:26
  • Maybe not a necessarily a duplicate, but an answer to this problem is there for sure. – Leśny Rumcajs Feb 12 '16 at 10:27

2 Answers2

3

Your string str will be destroyed once you leave the function. So the pointer you return is invalid, as it points to memory that is handled by the object. If the object is destroyed it will free this memory again.

Make str a member of the Exception and initialize it in the constructor. Than your what() will return a valid pointer (as long as the exception object is valid).

#include <string>
#include <iostream>

class NameErrorException : public std::exception
{
    std::string m_what;
public:
    NameErrorException(std::string name)
        :m_what("NameErrorException: name \'" + name + "\' is not defined")
    {
    }

    const char* what() const throw() override
    {
        return m_what.c_str();
    }
};


int main() 
{
    try
    {
        throw NameErrorException("TEST");
    }
    catch (std::exception& ex)
    {
        std::cout << ex.what() << std::endl;
    }
}

See here: http://coliru.stacked-crooked.com/a/878d5835db998300

Simon Kraemer
  • 5,700
  • 1
  • 19
  • 49
  • Construction of `std::string` can throw an exception. It's not necessarily a good idea to have an exception type that can throw as it is being constructed. The result can be an unexpected exception being thrown. – Peter Feb 12 '16 at 10:45
  • @Peter Where would you recommend to construct the string? `what()` is `const`. I agree that this is might cause problems yet if `std::string` throws I also expect some other more serious issues. – Simon Kraemer Feb 12 '16 at 10:55
  • Not actually the right question. What you need to decide is what exceptions need to be thrown, and implement the exception types accordingly. If it doesn't matter to the caller if (for example) `std::bad_alloc` is thrown, then you can get away with constructing a string. If the caller should only ever catch a `NameErrorException`, then make sure that type does not require constructing any `std::string` - or any other type that dynamically allocates memory (e.g. contain an array of char). – Peter Feb 12 '16 at 12:45
  • That seems a bit involved, but it's one way. – Peter Feb 12 '16 at 21:14
0

string::c_str() returns a pointer to the internal memory used.

your string object is a local variable, it gets destroyed at the end of your function call, leading to an invalid memory access afterwards.

you should either use strcpy to a newly generated memory chunk or directly return a std::string object instead which would be the much safer option.

Jack White
  • 409
  • 4
  • 11