C create array of struct using constructor function -
i have c struct:
typedef struct { dataset *datasets; int ndatasets; char *group_name; enum grouptype type; } datasetgroup;
it has constructor function this:
datasetgroup * new_datasetgroup(char *group_name, enum grouptype type, enum returncode *ret) { datasetgroup *dg; dg = (datasetgroup *) malloc(sizeof(datasetgroup)); if (dg == null) { *ret = ememory_error; } // allocate space few datasets dg->datasets = malloc(sizeof(dataset) * increment); if (dg->datasets == null) { *ret = ememory_error; } dg->group_name= malloc(sizeof(char) * strlen(group_name)); strcpy(dg->group_name, group_name); dg->type = type; groupcount++; return dg; }
i want dynamically create array of these structs. whats best way this?
so far have like:
datasetgroup * make_array(){ datasetgroup *dg_array; // allocate space few groups dg_array = (datasetgroup *) malloc(sizeof(datasetgroup) * increment); return dg_array; } void add_group_to_array(datasetgroup *dg_array, ...){ // add datasetgroup datasetgroup *dg = new_datasetgroup(...); // groupcount - 1 count incremented when group created, 1 ahead of array index want assign dg_array[groupcount - 1] = dg; if (groupcount % increment == 0) { //grow array dg_array = realloc(dg_array, sizeof(datasetgroup) * (groupcount + increment)); } }
but doesnt seem right.... ideas?
a few suggestions:
- you have groupcount being incremented constructor function of struct. means can have 1 array of struct uses array function. recommend having array responsible managing count.
- to affect if want have managed array create struct , have keep both pointer array,the number of objects , size of array (e.g. maximum number of structs can hold)
- if keep proper track of how many elements have , size of array can replace
groupcount % increment == 0
groupcount == arraysize
lot more intuitive in opinion. - you can avoid second malloc in constructor having array array of elements instead of array of pointers. constructor initialize struct members instead of allocating memory. if doing lot avoiding lot of memory fragmentation.
- finally, while depends on application, recommend when realloc not increase constant instead of multiple of current array size. if double array size have
log_2 n
number of reallocsn
being final array size , waste @ half of memory (memory cheap, said depends on application). if wasting memory can 1.5. if want more detailed explanation of recommend this joel on software article, part realloc 2/3 down.
update:
a few others things:
dg = (datasetgroup *) malloc(sizeof(datasetgroup)); if (dg == null) { ret = ememory_error; } // allocate space few datasets dg->datasets = malloc(sizeof(dataset) * increment);
as pointed out bad dg if null. want exit right after detecting error.
furthermore setting ret ret passed value not changed caller if callee changes it. instead want pass pointer , dereference it.
update 2: can give example, sure, quick not ;-d.
consider following code (i apologize if there mistakes, still half asleep):
#include <stdio.h> #include <stdlib.h> #define less_mallocs #define max_count 100000000 typedef struct _foo_t { int bar1; int bar2; } foo_t; void foo_init(foo_t *foo, int bar1, int bar2) { foo->bar1 = bar1; foo->bar2 = bar2; } foo_t* new_foo(int bar1, int bar2) { foo_t *foo = malloc(sizeof(foo_t)); if(foo == null) { return null; } foo->bar1 = bar1; foo->bar2 = bar2; return foo; } typedef struct _foo_array_t { #ifdef less_mallocs foo_t *array; #else foo_t **array; #endif int count; int length; } foo_array_t; void foo_array_init(foo_array_t* foo_array, int size) { foo_array->count = 0; #ifdef less_mallocs foo_array->array = malloc(sizeof(foo_t) * size); #else foo_array->array = malloc(sizeof(foo_t*) * size); #endif foo_array->length = size; } int foo_array_add(foo_array_t* foo_array, int bar1, int bar2) { if(foo_array->count == foo_array->length) { #ifdef less_mallocs size_t new_size = sizeof(foo_t) * foo_array->length * 2; #else size_t new_size = sizeof(foo_t*) * foo_array->length * 2; #endif void* tmp = realloc(foo_array->array, new_size); if(tmp == null) { return -1; } foo_array->array = tmp; foo_array->length *= 2; } #ifdef less_mallocs foo_init(&(foo_array->array[foo_array->count++]), bar1, bar2); #else foo_array->array[foo_array->count] = new_foo(bar1, bar2); if(foo_array->array[foo_array->count] == null) { return -1; } foo_array->count++; #endif return foo_array->count; } int main() { int i; foo_array_t foo_array; foo_array_init(&foo_array, 20); for(i = 0; < max_count; i++) { if(foo_array_add(&foo_array, i, i+1) != (i+1)) { fprintf(stderr, "failed add element %d\n", i); return exit_failure; } } printf("added elements\n"); return exit_success; }
there struct (foo_t
) 2 members (bar1
, bar2
) , struct array wrapper (foo_array_t
). foo_array_t
keeps track of current size of array , number of elements in array. has add element function (foo_array_add
). note there foo_init
, new_foo
, foo_init
takes pointer foo_t
, new_foo
not , instead returns pointer. foo_init
assumes memory has been allocated in way, heap, stack or whatever doesn't matter, while new_foo
allocate memory heap. there preprocess macro called less_mallocs
. changes definition of array member of foo_array_t
, size of initial array allocation, size during reallocation , whether foo_init
or new_foo
used. array , size have change reflect whether pointer or element in array. less_macro
defined code following suggestion number 4, when not, more similar code. finally, main
contains simple micro-benchmark. results following:
[missimer@asus-laptop tmp]$ gcc temp.c # compile less_macros defined [missimer@asus-laptop tmp]$ time ./a.out added elements real 0m1.747s user 0m1.384s sys 0m0.357s [missimer@asus-laptop tmp]$ gcc temp.c #compile less_macros not defined [missimer@asus-laptop tmp]$ time ./a.out added elements real 0m9.360s user 0m4.804s sys 0m1.968s
not time
best way measure benchmark in case think results speak themselves. also, when allocate array of elements instead of array of pointers , allocate elements separately reduce number of places have check errors. of course has trade-offs, if example struct large , wanted move elements around in array doing lot of memcpy
-ing opposed moving pointer around in approach.
also, recommend against this:
dg_array = realloc(dg_array, sizeof(datasetgroup) * (groupcount + increment));
as lose value of original pointer if realloc fails , returns null
. previous ret, should pass pointer instead of value not changing value caller, callee exits has no real affect. finally, noticed changed function definition have pointer ret need dereference pointer when use it, should getting compiler warnings (perhaps errors) when try have.
Comments
Post a Comment