Reputation: 129
How would I go about returning a string built from a buffer within a function without dynamically allocating memory?
Currently I have this function to consider:
// Reads null-terminated string from buffer in instance of buffer class.
// uint16 :: unsigned short
// ubyte :: unsigned char
ubyte* Readstr( void ) {
ubyte* Result = new ubyte[]();
for( uint16 i = 0; i < ByteSize; i ++ ) {
Result[ i ] = Buffer[ ByteIndex ];
ByteIndex ++;
if ( Buffer[ ByteIndex - 1 ] == ubyte( 0 ) ) {
ByteIndex ++;
break;
};
};
return Result;
};
While I can return the built string, I can't do this without dynamic allocation. This becomes a problem if you consider the following usage:
// Instance of buffer class "Buffer" calling Readstr():
cout << Buffer.Readstr() << endl;
// or...
ubyte String[] = Buffer.String();
Usages similar to this call result in the same memory leak as the data is not being deleted via delete
. I don't think there is a way around this, but I am not entirely sure if it's possible.
Upvotes: 0
Views: 575
Reputation: 4421
There are at least three ways you could reimplement the method to avoid a direct allocation with new
.
Use a std::vector
(This will allocate heap memory):
std::vector<ubyte> Readstr()
{
std::vector<ubyte> Result;
for (uint16 i = 0; i < ByteSize; i++)
{
Result.push_back(Buffer[ByteIndex]);
ByteIndex++;
if (Buffer[ByteIndex - 1] == ubyte(0))
{
ByteIndex++;
break;
}
}
return Result;
}
Force the caller to provide an output buffer and possibly a size do avoid overflows (Does not directly allocate memory):
ubyte* Readstr(ubyte* outputBuffer, size_t maxCount)
{
for (uint16 i = 0; i < ByteSize; i++)
{
if (i == maxCount)
break;
outputBuffer[i] = Buffer[ByteIndex];
ByteIndex++;
if (Buffer[ByteIndex - 1] == ubyte(0))
{
ByteIndex++;
break;
}
}
return outputBuffer;
}
Use an internal static array and return a reference to it:
ubyte* Readstr()
{
enum { MAX_SIZE = 2048 }; // Up to you to decide the max size...
static ubyte outputBuffer[MAX_SIZE];
for (uint16 i = 0; i < ByteSize; i++)
{
if (i == MAX_SIZE)
break;
outputBuffer[i] = Buffer[ByteIndex];
ByteIndex++;
if (Buffer[ByteIndex - 1] == ubyte(0))
{
ByteIndex++;
break;
}
}
return outputBuffer;
}
Be aware that this last option has several limitations, including possibility of data races in multithreaded application and inability to call it inside a recursive function, among other subtle issues. But otherwise, is probably the closest to what you are looking for and can be used safely if you take some precautions and make some assumptions about the calling code.
Upvotes: 1
Reputation: 153955
Personally, I'd recommend just return std::string
or std::vector<T>
: this neatly avoids memory leaks and the string won't allocate memory for small strings (well, most implementations are going that way but not all are quite there).
The alternative is to create a class which can hold a big enough array and return an object that type:
struct buffer {
enum { maxsize = 16 };
ubyte buffer[maxsize];
};
If you want get more fancy and support bigger strings which would then just allocate memory you'll need to deal a bit more with constructors, destructors, etc. (or just use std::vector<ubyte>
and get over it).
Upvotes: 5