Setting the nth member of a void* in a struct (and struct name usage vs. typedef)

Viewed 144

For a Risk-like game I have a struct for each region, each of which need to have a list of associated regions that troops can move into from the mother region.

In theory, it would look like

typedef struct {
    char* name;
    region_t* connections;
} region_t;

However, this is of course impossible due to the fact that region_t doesn't exist before the parser reaches the type region_t*. Therefore I have opted to use void* in place of region_t*.

typedef struct {
    char* name;
    void* connections;
} region_t;

MiddleEast.connections = malloc(6 * sizeof(void*));
MiddleEast.connections = &SouthernEurope;
MiddleEast.(connections + 1) = &Egypt;

Using the region_ts MiddleEast, SouthernEurope, and Egypt, I can set the first connection to Southern Europe successfully, but when I try to set .(connections + 1) to &Egypt I get the compiler error

error: expected identifier before ‘(’ token

How do I properly access the next memory address?

2 Answers

The solution is not void *, it's properly using structs.

structs with a name can be referenced inside themselves. The type doesn't exist, but the named struct does, so you can write:

typedef struct region{
    char* name;
    struct region* connections;
} region_t;

You want to create an array of void pointers, not an array of voids. You want to iterate over void pointers.

typedef struct {
    char* name;
    void **connections;
} region_t;

MiddleEast.connections = malloc(6 * sizeof(void*));
MiddleEast.connections[0] = &SouthernEurope;
MiddleEast.connections[1] = &Egypt;
free(MiddleEast.connections);

is of course impossible due to the fact that region_t doesn't exist before the parser reaches the type region_t*.

You can't forward declare a typedef, but you can do that with structure.

typedef struct region_s {
   char *name;
   struct region_s *connections;
} region_t;

Forward declaration is common in C code in constructing linked lists and similar. The sizeof(struct region_s *) is known, the struct region_s pointer size is known before the struct region_s is defined, so you can:

// forward declaration
struct region_s;
// pointer is ok
struct region_s *regionpointer;
// we can define the structure after using a pointer
struct region_s {
    // you can even point to yourself
    struct region_s *regionpointer;
};
// typedef separated for readbility
typedef struct region_s region_t;

error: expected identifier before ‘(’ token

MiddleEast.(connections + 1) = &Egypt;

The . is member operator; it gets the member of a structure. The string after . is taken as a symbol. As you can see, there is no member inside MiddleEast structure named (connections (exactly, using space as separator), as such name is disallowed. You want:

MiddleEast.connections + 1 = &Egypt;

Which will still not work, as the left side of = is the value of the pointer inside MiddleEast.connections array. You can't assign this value; it is a result of an addition. (the same you can't int a; a + 5 = 43;). What you want is to assign the value of the second element inside MiddleEast.connections array to the value of &Egypt. So you need to deference the pointer value.

*(MiddleEast.connections + 1) = &Egypt;

Or shorter and way more readable, as a[b] is equal to *(a + b):

MiddleEast.connections[1] = &Egypt;

Jokingly: Please never:

1[MiddleEast.connections] = &Egypt;

The MiddleEast.connections is an "integral"/whole/standalone part, that you can't separate.

Note that doing:

MiddleEast.connections = malloc(6 * sizeof(void*));
MiddleEast.connections = &SouthernEurope;

just leaks memory, as you can't no longer free(MiddleEast.connections) free the memory.

Related