Search code examples
cstructvoid

Initializing arrays in C with void *'s


Say I have a group of struct's that I want to manage arrays of via blocks of pointers. I would like to initialize these blocks in a generic fashion, and here's what I'm thinking:

enum my_type {STRUCT_1, STRUCT_2, STRUCT_3} ;

struct my_struct_1 {...} ;
struct my_struct_2 {...} ;
struct my_struct_3 {...} ;

void * my_array_init(my_type t) {
    void * array = NULL ;
    switch(t) {
    case STRUCT_whatever :
        (my_struct_whatever **) array ; /* does this cast or is assignment needed? */
    default :
    }
    array = malloc(sizeof(*array) * BLOCK_SIZE) ;
    if(array) {
        for(i=0 ; i<BLOCK_SIZE ; ++i)
            array[i] = NULL ;
    }

    return array ;
}

Then I was thinking I could use it like:

/* Initially set all pointers to NULL */
my_struct_1 ** s1 = NULL ;
my_struct_2 ** s2 = NULL ;
my_struct_3 ** s3 = NULL ;

s1 = my_array_init(STRUCT_1) ;
s2 = my_array_init(STRUCT_2) ;
s3 = my_array_init(STRUCT_3) ;

Am I on a right track, do I have the level of indirection wrong, or should I just bag it and write individual 'init' functions? Regarding 'void *', since it can point to anything I'd think it could be a handle (pointer to pointer), and I've seen conflicting arguments about 'void **'. However, 'void **' does appear in at least one of the examples in K&R 2nd ed. - in '5.11 Pointers to Functions' on page 119. For what it's worth, all the struct's contain only pointers and basic data types.


Solution

  • In this particular example, the my_init_array function does not need to know the type of the struct. This is because it's creating an array of pointers (all NULL), and is not creating an array of the struct.

    (Warning: Technically, it's possible that your platform is one where sizeof(void*) is not the same as sizeof(struct my_struct_1 *). See this answer. The good news is that pointers to all struct types will be the same size. If you are paranoid, you should edit this code accordingly. I have assumed, in this answer, that sizeof(void*)==sizeof(struct my_struct_WHATEVER *). You might be best to simply copy and paste the entire function three times and write one function for each of the three struct types. A macro might help here.)

    #include<stdlib.h>
    
    enum my_type {STRUCT_1, STRUCT_2, STRUCT_3} ;
    
    struct my_struct_1 { int x; };
    struct my_struct_2 { double x; };
    struct my_struct_3 { char * x; };
    
    #define BLOCK_SIZE 10
    
    void * my_array_init(void) {
        void ** array = NULL ;
        array = malloc(sizeof(*array) * BLOCK_SIZE) ;
        if(array) {
            int i;
            for(i=0 ; i<BLOCK_SIZE ; ++i)
                array[i] = NULL ;
        }
        return array ;
    }
    
    int main() {
            struct my_struct_1 ** s1 = my_array_init();
            struct my_struct_2 ** s2 = my_array_init();
            struct my_struct_3 ** s3 = my_array_init();
    }
    

    I have made a lot of changes here to the code in the original question. I think the interesting line is:

    array = malloc(sizeof(*array) * BLOCK_SIZE) ;
    

    The sizeof(*array) is independent of the type of struct been using. array is a pointer-to-a-pointer and hence *array is simply a pointer. And the size of a pointer is always the same. Therefore this line doesn't need to depend in any way on the struct type. And array can be of type void**.

    The only other interesting line is

    array[i] = NULL ;
    

    Again, array[i] is just a pointer, and we are setting it to NULL. And hence this code doesn't depend on the type of struct. It is this line which requirs that array be void**, not void*