指向C中无效指针的指针 - 我可以使用void**进行基本多态性吗?

Gav*_*vin 4 c void-pointers

我可以理解void**在记忆中看起来如何,但我想知道我是否正确使用它.我在下面描述的是否有任何根本缺陷?例如,虽然我可以说"它对我有用",但我是否以某种方式创建了错误/不可移植的代码?

所以我有一个小行星克隆.有三个实体可以发射子弹,即玩家(SHIP *player_1,SHIP *player_2)和UFO(UFO *ufo).当子弹被射击时,重要的是要知道谁射了子弹; 如果它是一名球员,当它击中某些东西时,他们的分数需要增加.因此,子弹将存储它所属的实体类型(owner_type)以及直接指向所有者(owner)的指针:

enum ShipType
{
    SHIP_PLAYER,
    SHIP_UFO
};

typedef struct Bullet
{ 
    // ...other properties
    enum ShipType owner_type;
    void **owner;
} BULLET;
Run Code Online (Sandbox Code Playgroud)

然后,当玩家点击按钮或UFO看到目标时,将调用以下功能之一:

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
}
Run Code Online (Sandbox Code Playgroud)

......他们可能被称为,例如,像这样:

ship_fire(&player_1);
Run Code Online (Sandbox Code Playgroud)

最后,当子弹击中目标(例如小行星)时,我们取消引用所有者.如果它是一艘船,我们可以在那里增加分数.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这似乎是一种合理的方法吗?就像我说的,它对我有用,但我只有几个月的C经验.

最后一点:为什么我不使用void*而不是void**?因为我想避免悬挂指针.换句话说,说player_1死了并且是免费的,但他们的子弹继续前进并撞击小行星.如果我只有a void*,则该hit_asteroid函数无法知道bullet->owner指向取消分配的内存.但有了a void**,我可以有效地检查它是否为NULL; 如果player_1为NULL,那么*bullet->owner也将为NULL.

编辑:到目前为止,所有受访者都认为使用void**可能不是必需的,因为我可以避免悬空指针问题(例如,通过静态分配基础对象).他们是正确的,我会重构.但是我仍然有兴趣知道我是否以某种方式使用了void**,例如在内存分配/转换方面.但我想如果没有人把手伸向空中并宣称它有问题,它至少就像技术上有用的东西一样.

谢谢!

caf*_*caf 6

即使你想按照自己的方式继续这样做,你也不需要使用void **(而且不应该).

虽然void *是通用指针类型,但void **不是通用的指针指针类型 - 它应始终指向真正的void *对象.你的代码通过一个类型的左值取消引用SHIP **UFO **指针void **- 这在技术上不能保证工作.(当你这样做时会发生这种情况(SHIP*)*bullet->owner).

然而,好消息是你可以继续使用双指针方法,使用plain void *来完成这项工作. void *可以愉快地存储一个指向指针的指针(因为它毕竟只是另一种指针).如果你改成ownervoid *,那么ship_fire你会这样做:

bullet->owner = shipp;
Run Code Online (Sandbox Code Playgroud)

hit_asteroid你这样做:

ship_owner = *(SHIP **)bullet->owner;
Run Code Online (Sandbox Code Playgroud)

通常,使用指针强制转换的规则是:首先将指针强制转换回您确实知道它的指针类型,然后取消引用.