Here is one way this can be done :
void * Allocator( size_t amount )
{
size_t desired = amount + sizeof( AllocationHeader );
size_t allocAmount = std::max( desired, MinimumAllocationAmount );
unsigned char * p = (unsigned char *)GetSomeMemory( allocAmount );
p += sizeof( AllocationHeader );
return p;
}
This is just pointer arithmetic, as you mentioned. The key is to cast it to a pointer of a type that is one byte long. Otherwise the arithmetic will be skewed - adding 1 will not offset the pointer by one byte.
In the example, the AllocationHeader is the header you mentioned, MinimumAllocationAmount is some minimum amount if you want to avoid allocating 1 or 2 bytes at a time, and GetSomeMemory is whatever you want to use to get memory - calloc, new, custom, etc...
Microsoft's allocator, in debug mode, puts a header and trailer around each allocation with special bytes in them and they test for corruption by checking those special bytes. If they differ then memory was overrun or underrun.