My research focuses on




For large-scale simulations, it is hard to beat the speed and efficiency of C. But its standard library is minimal, lacking dynamic arrays or file parsing, which are common in Python. C++ improves container support, yet still falls short of Python-level convenience. The functions below fill some gaps, streamlining routine tasks and speeding up everyday workflows.
#define init_Array_1D(type, rows) ((type*)array_1d((rows), sizeof(type)))
void* array_1d(size_t rows, size_t element_size) {
return calloc(rows, element_size);
}
#define init_Array_2D(type, rows, cols) ((type**)array_2d((rows), (cols), sizeof(type)))
void** array_2d(size_t rows, size_t cols, size_t element_size) {
void **array = calloc(rows, sizeof(void*));
if (array == NULL) return NULL;
for (size_t i = 0; i < rows; i++) {
array[i] = calloc(cols, element_size);
if (array[i] == NULL) {
for (size_t j = 0; j < i; j++) free(array[j]);
free(array);
return NULL;
}
}
return array;
}
The functions above can declare, allocate, and initialize a memory block in a single statement, with all elements set to zero. They are called as double *h = init_Array_1D(double,n); or double **h = init_Array_2D(double,m,n); for a 1D or 2D array of type double, respectively. You can replace double with int or any other type, including custom data types created with typedef struct. The functions return NULL if allocation fails, so the caller should check the return value before use. As with any calloc allocation, remember to free the memory when it is no longer needed.
__attribute__((format(printf, 1, 2)))
char* make_Name(const char *pattern, ...) {
va_list args;
va_start(args, pattern);
int cLen = vsnprintf(NULL, 0, pattern, args) + 1;
va_end(args);
if (cLen < 2) return NULL;
char *fname = malloc(cLen);
if (!fname) return NULL;
va_start(args, pattern);
vsnprintf(fname, cLen, pattern, args);
va_end(args);
return fname;
}
This function creates a formatted string from variable values in a single statement. It uses C's standard formatting syntax as one of its parameters and accepts a variable number of arguments as needed. For example, calling const char *file_name = makeName("%s/S-%.2f_C-%.2e.dat",DIR,S,C); will set file_name to Data/S-0.50_C-1.20e-04.dat if DIR, S and C hold the values Data, 0.5 and 0.00012, respectively.
char **getFList(char *pat, int *count) {
glob_t gBuf;
int gR = glob(pat, 0, NULL, &gBuf);
if (gR != 0) {
if (count) *count = 0;
return NULL;
}
if (count) *count = gBuf.gl_pathc;
return gBuf.gl_pathv;
}
This function returns a list of file paths matching a given pattern, along with the number of matches. It is useful when reading data where the number of sample files varies with the parameters. Calling getFList("data/S-0.50_N-???.dat", &n) will return an array of n file paths matching the pattern, such as data/S-0.50_N-000.dat, data/S-0.50_N-009.dat, and so on. This function is intended for cases where only a few calls are necessary, as the list is allocated internally by glob and is not freed. The memory is released when the program exits. For a large number of calls, memory would accumulate, and a globfree call should be implemented.