Существует множество способов реализации полиморфной коллекции на С и у каждой реализации есть свои недостатки и достоинства. Самый популярный и простой из них, наверно, через void*. Хотя он и подходит для большинства случаев он всё же имеет недостаток в виде непоследовательного размещения данных в памяти и типобезопасности. Так что вот ещё парочка возможных способов реализации, которые могут подойти вам лучше:
Запихнув всю реализацию массива в макрос можно объявлять необходимые, типобезопасные массивы при необходимости.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ARRAY_DECLARE(type) \
struct array_##type { \
size_t size; \
size_t capacity; \
type* data; \
}; \
\
struct array_##type* array_##type##_create(size_t init_cap) { \
struct array_##type* self = malloc(sizeof(struct array_##type));\
self->capacity = init_cap; \
self->data = calloc(init_cap, sizeof(type)); \
} \
\
void array_##type##_push_back(struct array_##type* self, type value) { \
if (self->size == self->capacity) { \
type* temp = calloc(self->size + 1, sizeof(type)); \
memmove(temp, self->data, self->size * sizeof(type)); \
free(self->data); \
self->data = temp; \
} \
self->data[self->size++] = value; \
}
ARRAY_DECLARE(int);
ARRAY_DECLARE(float);
int main(void) {
struct array_int* iarr = array_int_create(1);
array_int_push_back(iarr, 42);
array_int_push_back(iarr, 1337);
for (int i = 0; i < 2; ++i) {
printf("%d\n", iarr->data[i]);
}
struct array_float* farr = array_float_create(1);
...
return 0;
}
Через "наследование".
/* Где-то в array.h */
struct array {
size_t size;
size_t capacity;
void* data;
void (*push_back)(struct array* self, void* value);
void* (*get)(struct array* sefl, size_t index);
};
/* Где-то в arra_int.h */
void array_int_push_back(struct array* self, void* value) {
if (self->size == self->capacity) {
int* temp = calloc(self->size + 1, sizeof(int));
memmove(temp, self->data, self->size * sizeof(int));
free(self->data);
self->data = temp;
}
int* int_data = (int*)self->data;
int_data[self->size++] = *(int*)value;
}
void* array_int_get(struct array* self, size_t index) {
return ((int*)self->data) + index;
}
struct array* array_int_create(size_t init_cap) {
struct array* self = malloc(sizeof(struct array));
self->push_back = array_int_push_back;
self->get = array_int_get;
self->capacity = init_cap;
self->data = calloc(init_cap, sizeof(int));
return self;
}
int main(void) {
struct array* arr = array_int_create(1);
int value = 42;
arr->push_back(arr, &value);
value = 1337;
arr->push_back(arr, &value);
for (int i = 0; i < 2; ++i) {
printf("%d\n", *((int*)arr->get(arr, i)));
}
return 0;
}
Ещё можно обратить внимение на новый
_Generic keyword.
Весь код выше приведён только в целях демонстрации и, безусловно, содежрит баги :)
Также советую посмотреть как реализованы такие структуры в крупных проектах:
1.
Glib
2.
Linux
3.
Postgres
4. Другие ссылки в интернете коих полно.
Надеюсь это вам поможет выбрать подходящую реалзизацию для вашей задачи.