I'd use something higher level, e.g. stretchy buffers. See this video for a live coding session that implements those - props to Per Vognsen for making this code, and for placing into public domain (i.e. completely free to use for any purpose, but I'm not a lawyer, so take anything I say with caution :).
You'd want to include bitwise/ion/common.c in your source file, and then the array allocation becomes simple. Stretchy buffers are perhaps the closest you get to the convenience of C++'s std::vector in C. They offer an API that doesn't feel like a C++ API transcribed in C - it is at the correct level, and lets you use plain pointers in a very sensible way (e.g. a buf_len of a NULL pointer is zero, not a crash, buf_push(mybuf, element) appends an element to the array and extends it if necessary, etc.
#include <assert.h>
#include <string.h>
#include <stdlib.h>
// note that common.c includes nothing, so you have to set it up
#include "common.c"
#define buf_resize(b, n) ((n) <= buf_len(b) ? (b) : (((b) = buf__grow((b), (n), sizeof(*(b)), 0)), ((b) ? buf__hdr((b))->len = (n) : 0), (b)))
typedef struct {
int * pickup_Ind;
double *pickup_Val;
int * lInd;
double *lVal;
int * simul_Ind;
double *simul_Val;
} Data;
enum {
size1 = ...,
size2 = ...,
size3 = ...
}
Data make_Data(void) {
Data d;
memset(&d, 0, sizeof(d));
assert(buf_len(d->pickup_Ind) == 0);
buf_resize(d.pickup_Ind, size1);
buf_resize(d.pickup_Val, size1);
buf_resize(d.lInd, size2);
buf_resize(d.lVal, size2);
buf_resize(d.simul_Ind, size3);
buf_resize(d.simul_Val, size3);
}
int main(int argc, char **argv) {
Data d = make_Data();
assert(buf_len(d.pickup_Ind) == size1);
d.pickup_Ind[0] = 10;
assert(buf_len(d.pickup_Ind) == size1);
buf_push(d.pickup_Ind, 11);
assert(buf_len(d.pickup_Ind) == size1 + 1);
}
If you're building up the arrays by adding elements to them one-by-one, it'll make sense to reserve the capacity for the expected size of the array via buf_fit (it only reserves the memory but the buffer retains its length (e.g. zero)). The capacity reservation is entirely optional, though. It's there to prevent reallocation of the arrays while you add elements to them.
Thus:
Data make_Data(void) {
Data d;
memset(&d, 0, sizeof(d));
assert(buf_len(d->pickup_Ind) == 0);
buf_fit(d.pickup_Ind, size1);
buf_fit(d.pickup_Val, size1);
buf_fit(d.lInd, size2);
buf_fit(d.lVal, size2);
buf_fit(d.simul_Ind, size3);
buf_fit(d.simul_Val, size3);
}
int main(int argc, char **argv) {
Data d = make_Data();
assert(buf_len(d.pickup_Ind) == 0); // zero length: no data in the array (yet!)
assert(buf_cap(d.pickup_Ind) >= size1); // but it has the capacity we need
buf_push(d.pickup_Ind, 10);
buf_push(d.pickup_Ind, 11);
assert(buf_len(d.pickup_ind) == 2);
}
If you'll want to use stretchy buffers in multiple source files, you'll run afoul of the one declaration rule (ODR). Thus, you'll need to factor out macro definitions and function declarations out of common.c and into common.h.
If the Data is only allocated once, there's no need to free it prior to exiting the program: the operating system already does it for you. Otherwise, you may wish to add a function to do this job:
void free_Data(Data *d) {
buf_free(d.pickup_Ind);
buf_free(d.pickup_Val);
buf_free(d.lInd);
buf_free(d.lVal);
buf_free(d.simul_Ind);
buf_free(d.simul_Val);
assert(buf_len(d.pickup_Ind) == 0);
}