views:

915

answers:

5

Are the string literals we use inside functions automatic variables? Or are they allocated in heap which we have to free manually?

I've a situation like the code shown below wherein I'm assigning a string literal to a private field of the class (marked as ONE in the code) and retrieving it much later in my program and using it (marked as TWO). Am I assigning a variable in the stack to a field in ONE? Can the code be referencing to a dangling pointer which in this case worked because the program was small enough?

I've compiled and ran it, it worked fine but I'm having a strange crash in my actual program where I'm assigning string literals to fields of the class like this and I suspect the case I mentioned above.

#include <iostream>

using namespace std;

class MemoryLeak
{
private:
    char *s;
public:
    MemoryLeak() {}

    void store()
    {
        s = "Storing a string"; // ONE
    }

    char *retrieve()
    {
        return s;
    }
};

int main()
{
    MemoryLeak *obj = new MemoryLeak();
    obj->store();
    cout << obj->retrieve() << endl; // TWO
    delete obj;
    return 0;
}

Should I be declaring the variable "s" as a char array instead of a pointer? I'm planning to use std::string, but I'm just curious about this.

Any pointers or help is, as always, much appreciated :) Thanks.

+7  A: 

String literals will be placed in the initialized data or text (code) segment of your binary by the compiler, rather than residing in (runtime allocated) memory or the stack. So you should be using a pointer, since you're going to be referencing the string literal that the compiler has already produced for you. Note that modifying this (which would require changing memory protection typically) will change all uses of this literal.

Cody Brocious
Modifying it is illegal. It's really a const char*.
Martin York
+1 to const char*. MemoryLeak::s should be const and it should be const char* MemoryLeak::retrieve()
quamrana
A: 

Maybe the cause of the crash is that you did not 0-terminate the string?

Treb
Erm, string literals are always null terminated.
Cody Brocious
Depends on the compiler, does't it?
Treb
Not unless you're dealing with a compiler from 30 years ago :P
Cody Brocious
Ok, seems I learned something new ;-) Thanks!
Treb
+6  A: 

It is undefined behaviour to modify a string literal, and is most likely the cause of the crash in your program (ISO C++: 2.13.4/2). The standard allows for a conversion from a string literal to char* for backwards compatibility to C and you should only have that conversion in your code if you absolutely need it.

If you wish to treat the string literal as a constant, then you can change the type of your member to a const char *.

If your design requires that s can be modified, then I would recommend changing its type to std::string.

Richard Corden
A: 

Thank you Cody and Richard.

I found the cause of the bug. It was because I was doing a delete on an object which was already delete'd. I was doing:

if (obj != NULL) delete obj;

I changed it to:

if (obj != NULL)
{
    delete obj;
    obj = NULL;
}

Learning C++ is definitely fun :)

artknish
Yes - fun it is! Relating to the bug that you found, I really would recommend that you use smart pointers. Do a search for "smart pointer" and "RAII" in StackOverflow and you'll find useful information. Smart pointers take care of managing the object lifetime for you.
Richard Corden
At least note that the C++ standard guarantees that it is safe to delete a null pointer. Implementers of operator delete ought to do the same. So null-check before delete is redundant. If you might execute the code again, and don't/can't use smart pointers, blanking after delete might be necessary.
Steve Jessop
delete should be done in destructors. Possibly in assignment, but even there copy-to-temp-and-swap is usually safer (the destructor of the temp cleans up the swapped old value)
MSalters
@Richard: I'll have a look at smart pointers and [email protected]: Thanks for that. I'll remove the NULL checks before deleting in my code. But as Richard pointed out, smart pointers seem to be a better choice but not before I learn it :)@MSalters: I'm not sure I understand you. :(
artknish
A: 

Lets have a look at your options.
There are also a couple of things you should do:

    /*
     * Should initialize s to NULL or a valid string in constructor */
        MemoryLeak()
        {
            store();
        }

        void store()
        {
            // This does not need to be freed because it is a string literal
            // generated by the compiler.
            s = "Storing a string"; // ONE

            // Note this is allowed for backward compatibility but the string is
            // really stored as a const char* and thus unmodifiable. If somebody
            // retrieves this C-String and tries to change any of the contents the
            // code could potentially crash as this is UNDEFINED Behavior.

            // The following does need to be free'd.
            // But given the type of s is char* this is more correct.
            s = strdup("Storing a string");

            // This makes a copy of the string on the heap.
            // Because you allocated the memory it is modifiable by anybody
            // retrieving it but you also need to explicitly de-allocate it
            // with free()
        }

What you are doing is using C-Strings. These should not be confused with C++ std::string. The C++ std::string is auto-initialized to the empty string. Any memory allocated is de-allocated correctly. It can easily be returned as both a modifiable and non modifiable version. It is also easy to manipulate (i.e. grow shrink change). If you grow a C-String you need to re-allocate memory and copy the string to the new memory etc (it is very time consuming an error prone).

To cope with dynamically allocating your object I would learn about smart pointers.
See this article for more details on smart pointers.
Smart Pointers or who owns you Baby

std::auto_ptr<MemoryLeak> obj(new MemoryLeak());

obj->store();
std::cout << obj->retrieve() << std::endl; // TWO

// No need to delete When object goes out of scope it auto deletes the memory.
Martin York