Fog Creek Software
Discussion Board




string memory management 101

I am writing code similar to the example below, and I realized I don't really understand some things.

In the example below, where in the process will lpwszExample live initially? Will it be on the heap? I don't really understand how string literals are stored.

If SetString is called, what happens to memory originaly used to store "Example"?

Is the behavior different if lpwszExample is an instance member, not a static member?

How can I possibly ever delete lpwszExample since it is static?


-----------------------------------------
EXAMPLE.H
-----------------------------------------
class Example
{

public:

static void SetString(LPCWSTR lpwszNew);

private:

static LPWSTR lpwszExample;

};


-----------------------------------------
EXAMPLE.CPP
-----------------------------------------
LPWSTR Example::lpwszExample = L"Example";

void Example::SetString(LPCWSTR lpwszNew)
{
    DWORD dwLen = 0;

    dwLen = wcslen( lpwszNew );

    if ( dwLen == 0 )
    {
        return;
    }

    lpwszExample = new WCHAR[ dwLen + 1 ];

    if ( !lpwszExample )
    {
        return;
    }

    wcscpy( lpwszExample, lpwszNew );
}

Modern Cowboy
Wednesday, June 30, 2004

It will be stored in the data segment.

Anon
Wednesday, June 30, 2004

You are storing a pointer not the actually memory for the string.  That is managed by the string class.

Anon
Wednesday, June 30, 2004

Err... Nevermind about the string class you aren't using it.

But you are still only storing a pointer.

Anon
Wednesday, June 30, 2004

Your initial assignment is stored in the data segment and when you call the SetString function which calls the 'new' function it then allocates memory on the heap which is where the new string will be stored.

Anon
Wednesday, June 30, 2004

Of course some compilers might put that initial assignment in the code segment.

Anon
Wednesday, June 30, 2004

To be honest, I hope you aren't actually using that code in production.

Anon
Wednesday, June 30, 2004

Static data declarations are normally stored in the code or data segments.  There is also the stack segment which wouldn't be used for that.

Anon
Wednesday, June 30, 2004

You can't really delete something from the data segment.  It remains there.  But you can delete memory you allocate with 'new.'

So 'Example' is stored in the data segment then when your program starts the lpwszExample pointer is set to point to the fixed up location of that data.

Now when you call the SetString function which allocates new memory with 'new' you are allocating other memory on the heap which is seperate from the data segment.

It is possible to overwrite data in the data segment so you could overwrite 'Example' but you would have to know the size of the data there so you don't overwrite other static data.  Also when you call the Setstring function you are modifying the pointer to 'Example' which was in the data segment but after the call your pointer points to memory on the heap if it succeeds.

Anon
Wednesday, June 30, 2004

Instance vs. Static behavior doesn't really have a correlation but I think I know what you mean.  If I know what you mean the answer is no the behavior is the same.

Even if the string was not static the compiler would still store the data 'Example' in the data segment so that it could initialize the pointer.

Anon
Wednesday, June 30, 2004

"To be honest, I hope you aren't actually using that code in production."

Why do you say that?

After SetString has been called, and lpwszExample is pointing to the heap, is there an elegant way to make sure it is deleted? Or would I have to require someone to call some other function, like "DeleteString"?

Modern Cowboy
Wednesday, June 30, 2004

You *could* release the memory in the destructor but that has it's own caveats.  Read the books by Eric Meyer.  He tells you all about C++ string classes.

You should be using the string class that came with your compiler instead of reinventing the wheel.  If you are just trying to learn well that is a different story.  Even then you may want to start off with something more basic and picking up the proper book will help you a great deal in learning how and what is going on.

Anon
Wednesday, June 30, 2004

You're declaring the _pointer_ as static with

static LPWSTR lpwszExample;

not the _memory_area_ to whch it points.
The allocated memory is on taken from the heap with

lpwszExample = new WCHAR[ dwLen + 1 ];

And then you copy the original characters to the new memory area using

wcscpy( lpwszExample, lpwszNew );

So if you don't do

delete [] lpwszExample;

before SetString() is called the next time, you will have a memory leak.

Unsygn
Thursday, July 01, 2004

First of all why aren't you using std::string?  Secondly if you really want to do something like this you should use a singleton and delete the data in the destructor of the single instance. 

Keep in mind this is different than the following

static char[] myString = "foobar";

This allocates 6 characters globally.  I guess you could exchange those 6 characters, but I personally wouldn't do that. 

christopher (baus.net)
Thursday, July 01, 2004

The example is pretty dangerous, because:

1) The memory which contains L"Example" is not on the heap (must not be deleted)

2) The memory allocated via new WCHAR is on the heap (should be deleted)

3) After a while, for example when SetString is called for the first time, you don't know whether lpwszExample is poiting at heap-allocated memory, or whether it's pointing at the original memory in the data segment: and therefore you don't know whether or not to delete it.

Other than using std::string, another way to make it safe would be to allocate the initial memory on the heap (so that you know that it always can be deleted):

static LPWSTR allocate_example_on_heap()
{
    LPWSTR p = L"Example";
    LPWSTR rc = new WCHAR(p.wcslen() + 1);
    wcscpy(rc,p);
    return rc;
}

LPWSTR Example::lpwszExample = allocate_example_on_heap();

If you do this then it's now safe to modify your Example::SetString implementation, to call "delete[] lpwszExample;" immediately before you call " lpwszExample = new WCHAR[ dwLen + 1 ];".

Christopher Wells
Thursday, July 01, 2004

> LPWSTR rc = new WCHAR(p.wcslen() + 1);

:-)

That was funny, actually.

LPWSTR rc = new WCHAR(wcslen(p) + 1);

Unsygn
Friday, July 02, 2004

*  Recent Topics

*  Fog Creek Home