I can understand how a void**
might look in memory, but I'm wondering if I'm using it quite right. Are there any fundamental flaws in what I describe below? For example, although I can say "it works for me", am I creating bad / unportable code in some way?
So I have an Asteroids clone. There are three entities that can fire bullets, the players (SHIP *player_1
, SHIP *player_2
) and the UFO (UFO *ufo
). When a bullet is fired, it's important to know who fired the bullet; if it was a player, when it hits something their score needs to be incremented. So, the bullet will store what kind of entity it belongs to (owner_type
) and also a pointer directly to the owner (owner
):
enum ShipType
{
SHIP_PLAYER,
SHIP_UFO
};
typedef struct Bullet
{
// ...other properties
enum ShipType owner_type;
void **owner;
} BULLET;
Then, when the player hits the button or the UFO sees a target, one of these functions will be called:
void ship_fire(SHIP **shipp)
{
BULLET *bullet = calloc(1, sizeof(BULLET));
bullet->owner_type = SHIP_PLAYER;
bullet->owner = (void**)shipp;
// do other things
}
void ufo_fire(UFO **ufop)
{
BULLET *bullet = calloc(1, sizeof(BULLET));
bullet->owner_type = SHIP_UFO;
bullet->owner = (void**)ufop;
// do other things
}
... they may be called, for example, like this:
ship_fire(&player_1);
Finally, when the bullet hits a target (such as an asteroid), we dereference the owner. If it's a ship, we can increment the score there and then.
void hit_asteroid(ASTEROID *ast, BULLET *bullet)
{
SHIP *ship_owner;
if (bullet->owner_type == SHIP_PLAYER && *bullet->owner != NULL)
{
ship_owner = (SHIP*)*bullet->owner;
ship_owner->score += 1000;
}
}
Does that seem a reasonable approach? Like I say, it works for me, but I only have a couple of months of C experience.
A final note: why do I not use a void*
instead of a void**
? Because I want to avoid dangling pointers. In other words, say that player_1
dies and is free'd, but their bullet keeps going and hits an asteroid. If I only have a void*
, the hit_asteroid
function has no way of knowing that bullet->owner
points to de-allocated memory. But with a void**
, I can validly check to see if it's NULL; if player_1
is NULL, then *bullet->owner
will be NULL too.
EDIT: All respondents so far concur that using a void** probably isn't necessary here because I can avoid the dangling pointers issue (by just statically allocating the base object, for instance). They're correct and I will refactor. But I'm still kinda interested to know if I've used void** in a way that might break something e.g. in terms of memory allocation / casting. But I guess if no-one has thrown their hands in the air and declared it faulty, it at least resembles something that would technically work.
Thanks!