0
101 Jan 29, 2012 at 01:24 c++ memory

I have a buffer variable along this line:

union
{
byte* data;
char* ch;
char16_t* ch16;
char32_t* ch32;
}
m_Buffer;


I keep track of which format of data I’m storing in the buffer and use the different name depending on what kind of unit (char, char16_t, etc.) I’m storing in the buffer.

My question is, is it safe to delete[] the array of whatever unit type on any member regardless of how I initialized one of the pointers? For example:

m_Buffer.ch16 = new char16_t[ charCount ];
delete[] m_Buffer.data;  // Is this valid... does it free the above allocation?

m_Buffer.data = new byte[ charCount * sizeof(char16_t) ];
delete[] m_Buffer.ch16;  // Is this valid... does it free the above allocation?


Also, is the pointer I assign to a given union member kept properly regardless of which one I access for indexing into the array? For example:

m_Buffer.data = new byte[ charCount * sizeof(char16_t) ];

// Fill the string buffer with spaces.
for( uint i = 0; i < charCount - 1; i++ )
{
m_Buffer.ch16[ i ] = (char16_t)' ';  // Is this valid?
}

// Terminate the string.
m_Buffer.ch16[ charCount - 1 ] = (char16_t)'\0';


and

m_Buffer.ch16 = new char16_t[ charCount ];
m_UnitCount = charCount;

// Then somewhere else:

byte* someOtherBuffer = new byte[ charCount * sizeof(char16_t) ];

memcpy( someOtherBuffer, m_Buffer.data, m_UnitCount * sizeof(char16_t) );
delete[] m_Buffer.data;

m_Buffer.data = someOtherBuffer;
m_UnitCount = charCount;


My knowledge of unions and delete is very rusty. Does all of the above code work as expected?

#### 4 Replies

0
101 Jan 29, 2012 at 02:16

I think doing this mostly safe (dunno about standard though, udefined behavouir maybe?) as new/delete resolves to malloc/free, but I prefer delete pointer of the same type as allocated:

SomeType *ptr = reinterpret_cast<SomeType *>(new char[size]);
...
delete [] reinterpret_cast<char *>(ptr);


Also you can use malloc/free directly (with a bonus of realloc, which doesn’t have C++ counterpart).

0
101 Jan 29, 2012 at 02:37

I prefer delete pointer of the same type as allocated:

Aye. At the time I have it use switch() to determine which (.data, .ch, .ch16, .ch32) to perform the new[] and delete[] for, but I figured that was rather inefficient.

I guess I could just always initialize the m_Buffer.data based on the size of the array (unit count * unit size) then delete m_Buffer.data, to both get rid of the switches and avoid the undefined behavior. From what I understand, malloc is based only on the byte size and not an array size, so it should be safe to allocate with say, m_Buffer.ch16 = (char16_t*)malloc( unitCount * unitSize ) then delete with free( m_Buffer.data ) ?

0
101 Jan 29, 2012 at 22:00

@dag239

My question is, is it safe to delete[] the array of whatever unit type on any member regardless of how I initialized one of the pointers?

No.

Also, is the pointer I assign to a given union member kept properly regardless of which one I access for indexing into the array?

Theoretically, no. In practice, on practically every modern platform I can think of this wouldn’t pose a problem. But there are some platforms out there of which pointers to various types are not bitwize compatible.

As far as the standard goes, different types are not layout compatible unless they are structs/classes which are only layout compatible if they are plain old datatypes and composed of the same member types. When accessing members in a union that are not layout compatible, it’s only defined behaviour if you read from a member that has lastly been written to.

0
151 Jan 30, 2012 at 10:32

Also be aware of alignment issues, on some platforms a union of pointers is a nightmare.

Take this structure

struct blah
{
char a_char;
union
{
char * cstar_p;
int *    istar_p;
}u;
};


On some platforms an int * has to be on a 32 bit boundary, but the char * does not. So the compiler either ignores the char * and pads the structure out making the structure bigger than you expect (but allowing safe execution, GOOD COMPILER, GOOD BOY, HAVE A BISCUIT) or it takes the first type in the union (char *) and does not align the union (crashes at run time the first time you access the int * , BAD COMPILER, BAD DOG, GO TO YOUR BED!).