I am implementing a dynamic array of arbitrary type in C. The implementation works by allocating a header prior to the data of the array:
typedef struct
{
unsigned char* begin; // Pointer to data
unsigned char* end; // Pointer to last element
size_t typeSize; // Size of type
size_t capacity; // Capacity of array (not number of elements in array)
} dynamicArr;
#define dynamicArr_ConstructBase(dest, src, newCapacity) \
dest = src; \
dest->capacity = newCapacity; \
dest->begin = (unsigned char*)dest + sizeof *dest
#define dynamicArr_Construct(dest, src, newCapacity, currSize, typeSize) \
dynamicArr_ConstructBase(dest, src, newCapacity); \
dest->end = dest->begin + typeSize * (currSize)
#define dynamicArr_Header(arr) ((dynamicArr*)((unsigned char*)(arr) - sizeof(dynamicArr)))
static inline size_t dynamicArr_Size(dynamicArr* arr)
{
return (arr->end - arr->begin) / arr->typeSize;
}
#define dynamicArr_Create(typename, arr) typename* arr = (typename*)dynamicArr_Create_(sizeof(typename))
static inline unsigned char* dynamicArr_Create_(size_t typeSize)
{
dynamicArr* dynArr;
void* tmp = malloc(sizeof * dynArr + typeSize * 10);
if (!tmp) return NULL;
dynArr = tmp;
dynArr->begin = (unsigned char*)dynArr + sizeof * dynArr;
dynArr->end = dynArr->begin;
dynArr->capacity = 10;
dynArr->typeSize = typeSize;
return dynArr->begin;
}
#define dynamicArr_Free(arr) free(dynamicArr_Header(arr))
#define dynamicArr_Push(arr, item) \
do \
{ \
if (arr) \
{ \
dynamicArr* header = dynamicArr_Header(arr); \
if (header->typeSize && header->typeSize == sizeof item) \
{ \
size_t arrSize = dynamicArr_Size(header); \
if (arrSize == header->capacity) \
{ \
size_t newCapacity = (size_t)(header->capacity * 1.5f); \
if (newCapacity == header->capacity) ++newCapacity; \
void* tmp = realloc(header, sizeof(dynamicArr) + header->typeSize * newCapacity); \
if (tmp) \
{ \
dynamicArr_Construct(header, tmp, newCapacity, arrSize, header->typeSize); \
*((void**)&(arr)) = header->begin; \
arr[arrSize] = item; \
header->end += header->typeSize; \
} \
else \
{ \
free(header); \
arr = NULL; \
} \
} \
else \
{ \
arr[arrSize] = item; \
header->end += header->typeSize; \
} \
} \
} \
} while(0)
Example use:
void Func()
{
dynamicArr_Create(int, intArr);
dynamicArr_Push(intArr, 10);
printf("%i\n", intArr[0]);
dynamicArr_Free(intArr);
}
The problem with this is that I believe this is undefined behaviour for if the compiler chooses to use aligned instructions as the items of the array the come after the header may not be aligned.
Is there a way to guarantee the alignment of the items ?
I've looked into using flexible array members, however I'm not sure how to implement this such that it would work with arbitrary types.
For example, I could declare the header like so:
#define dynamicArr_Header(type) \
struct \
{ \
size_t typeSize; \
size_t capacity; \
type data[]; \
}
However if I am to use this in code my compiler warns me '=': incompatible types - from '*' to '*'
.
Example that causes warning:
dynamicArr_Header(int)* A = malloc(sizeof(dynamicArr_Header(int)));
dynamicArr_Header(int)* B = A; //Causes warning
Is this dangerous to do? Is this because the anonymous structs are really not the same type, or does this cause more problems (such as whether I can guarantee the compiler pads and packs these structs the same way)?
Another "problem" with the flexible array member approach is that I would have to pass the header pointer around unlike in the previous approach where I could provide just the pointer to the data. I know with the flexible array version I could:
int* pntrToIntArr = &header->data[0];
But I would like the header to be abstracted away, but if I provided the pointer to the data array how would I retrieve the header back? Can I just do (dynamicArr_Header(int)*)((unsigned char*)pntrToIntArr - sizeof(dynamicArr_Header(int)))
?
Is what I'm doing simply not possible in C without breaking strict-aliasing / alignment rules? I know I'm probably breaking a lot of strict-aliasing rules with the pointer semantics.