If you don't need strict array indexing, you could make a pseudo-linked-list (I know there's a name for this data type but I can't remember it right now):
struct listish {
int *arr
size_t size;
struct listish *next;
};
The "indexing" function would look like this:
int *index(struct listish *list, size_t i)
{
if(list == NULL) return NULL; // index out of bounds
if(i < list->size) return list->arr + i; // return a pointer to the element
else return index(list->next, i - list->size); // not in this array - go to next node
}
The idea is to combine the in-place reordering of a linked list with the contiguous space of an array. In this case, index(list, 4) would return &a[4], and index(list, 5) would return &b[0], simulating continuous indexing without reallocating and moving your entire array - all you need to do is allocate a few small struct listish objects and set them up properly, a task I leave to you.
malloc()allocates a new block of memory. A 2ndmalloc()allocates another one. If you want them to be contiguous, allocate them both at the same time. There is no other answer.