views:

59

answers:

3

Hello:

I'm getting some odd behavior in one of my class members and it is truly throwing me for a loop but I'm certainly not seeing the issue (long week!)

void MyFakeStringClass::readStream( iostream& nInputStream )
{
    // Hold the string size
    UINT32 size = 0;

    // Read the size from the stream
    nInputStream.read( reinterpret_cast< char* >( &size ), sizeof( UINT32 ) );

    // Create a new buffer
    char* buffer = new char[ size ];

    // Read the stream
    nInputStream.read( buffer, size );

    // Save to class member
    value = string( buffer );

    // Clean up
    delete[] buffer;
    buffer = NULL;
}

This issue arises when I use two or more MyFakeStringClass's. buffer is somehow still containing data from previous calls to MyFakeStringClass::readStream.

Say for example I read in two strings 'HelloWorld', 'Test' the resulting MyFakeStringClass objects contain 'HelloWorld', and 'TestoWorld' ( should be 'HelloWorld', 'Test' ).

The second time the function is accessed buffer is still containing 'old' memory. How is this possible as it is locally scoped and deleted? I've confirmed that buffer is being somehow shared with a debugger.

+1  A: 

You need to create the string using:

value = string( buffer, size );

If you do not specify the size, it will assume buffer is a null-terminated string. Since there is no null terminator, it reads past the end of the data, and gives you previous contents of the memory.

interjay
I've tried that previously and that works but doesnt' explain why new char[ size ] does not return the properly requested size.
Matthew
It returns the requested size, but you are reading past the end of the allocated memory. In this case you happen to be getting contents of the previous read, but in other cases you will get random garbage or a segmentation fault.
interjay
Yes it does. Memory isn't really "deleted", it's only returned for recycling. When you read `"Test"`, `new char[size]` returns a recycled piece of memory which still contains the characters from `"HelloWorld"`. That's pure coincidence. In standardese, the moment you access the character behind `"Test"`'s final `'t'`, you invoke undefined behavior. Concerning the standard, your program is allowed to do anything after that. Giving you the string `"TestoWorld"` is just one way for this to manifest.
sbi
In the case of 'HelloWorld' and 'Test' example, the first string is larger ( size of 10 vs size of 4 ). I understand the constructor of string is reading the entire length of buffer ( 10 ) but how is buffer even being carried over between different calls to this class member as it is locally scoped and deleted. The second time the call is made buffer should be properly allocated to 4 but it still remains at 10.
Matthew
The second buffer happened to be placed at the same memory address as the first one (since it had been freed). This is not something you can count on to happen every time! Due to this, the contents of memory still had the old string in them. You only saw this because your code read past the end of the allocated buffer, which is an illegal operation. It is like calling `int *arr = new int[5]; cout<<arr[8];`
interjay
Yep, and I realize now by using the debugger to inspect <code>buffer</code> was adding to the confusion as it was treating the memory as a null-terminated string and showing beyond where it should have been. Needless to say, I've seen the light. Thanks interjay.
Matthew
A: 

It does have the right size, but if you call new and delete then new again you will get the same value because the memory manager doesn't initialize anything for you. If you are using it as a null terminated string you need to initialize it to zero yourself.

Charles Eli Cheese
+2  A: 

new char does not initialize the memory. It will be filled with whatever random data was left in that memory from the previous use.

If there were no other memory allocations between one two calls to readStream, it's very likely you'll get a buffer starting at the same address.

That said, you have an error that buffer is not 0 terminated and the constructor you are using assumes it is. You are luck that you haven't had a lot of other problems.

You can use the length specified constructor:

value = string( buffer, size );

Or if you needed to use buffer for something that absolutely required a 0 terminated buffer, you could also change your code to this:

// Create a new buffer
char* buffer = new char[ size + 1];

// Read the stream
nInputStream.read( buffer, size );

// Add the 0 termination to the end of the string
buffer[size] = '\0';
R Samuel Klatchko
Great explaination, wish I could pick two correct answers.
Matthew