Search code examples
cmemorystructstructurevoid-pointers

C Access structure contents from a void pointer


I have an assignment that is making me lose valuable hours without success. I have to read the contents from a struct that is passed to a function as void *c. I can read its contents without problems except the field that is a pointer to another struct. Example code:

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct Car Car;
struct Car {
        int     year;
        int     type;
        int     free;
        struct {
                char    *name;
                char    *city;
        } *facility;
};

int main()
{

    Car *c = malloc(sizeof(Car)*2);

    c->year = 2020;
    c->type = 2;
    c->free = 1;
    c->facility = malloc(200);
    c->facility->name = malloc(10);
    c->facility->city = malloc(10);
    snprintf(c->facility->name, 5, "Test");
    snprintf(c->facility->city, 5, "Test");
    test(c);
}

int test(void *c)
{
    int year = *(void **) c;
    int type = *(void **) (c+4);
    int free = *(void **) (c+4+4);
    printf("Year %d\n",year);
    printf("Type %d\n",type);
    printf("Free %d\n",free);
    void *facility;
    facility = *(void **) (c+4+4+4);
    printf("Facility name %s", *(void **) facility);
    printf("Facility city %s", *(void **) facility+8);
}

The part I am unable to read is facility name and facility city. I know I can access easily using -> but the assignment asks precisely understand how structure is defined in memory and extract its contents directly using the void*. Thanks.


Solution

  • the assignment asks precisely understand how structure is defined in memory

    The memory layout (assuming no padding) would look something like the following.

                    -------------------------|
                    |          ^             |
        c  ------>  |  year    | sizeof(int) |
                    |          v             |
                    |------------------------|
                    |  type                  |
                    |------------------------|
                    |  free                  |
                    |------------------------|           |--------|            |---|---|---
                    |  facility              |  ------>  |  name  |  ------->  | a | b | ..
                    |------------------------|           |--------|            |---|---|---
                                                         |  city  |  ---\      
                                                         |--------|     |      |---|---|---
                                                                        \--->  | x | y | ..
                                                                               |---|---|---
    

    To access what would be c->facility->city, for example:

        void *facility = *(void **)( (char *)c + 3 * sizeof(int) );  // skip past year, type, free
        void *city = *(void **)((char *)facility + sizeof(char *));  // skip past name
    

    [ EDIT ]   Without the "no padding" assumption, the code could use the offsetof macro, instead.

        void *facility = *(void **)( (char *)c + offsetof(struct Car, facility) );