在C语言中初始化静态数组结构体

34

我正在使用C语言实现一个纸牌游戏。有许多类型的纸牌,每种纸牌都有很多信息,包括需要与之相关联的一些单独脚本化的操作。

假设有以下这样一个结构体(我不确定函数指针的语法是否正确)

struct CARD {
    int value;
    int cost;
    // This is a pointer to a function that carries out actions unique
    // to this card
    int (*do_actions) (struct GAME_STATE *state, int choice1, int choice2);
};

我希望能够初始化一个静态数组,其中每个元素都是这个对象的一个实例。我猜应该是这样的:

int do_card0(struct GAME_STATE *state, int choice1, int choice2)
{
    // Operate on state here
}

int do_card1(struct GAME_STATE *state, int choice1, int choice2)
{
    // Operate on state here
}

extern static struct cardDefinitions[] = {
    {0, 1, do_card0},
    {1, 3, do_card1}
};
  1. 这种做法可行吗?我尝试避免使用大量的switch语句。

  2. 在结构体初始化时,我需要预先定义'do_cardN'函数吗?还是有一种内联定义它们的方法(类似于Python中的lambda函数)?

  3. 我需要从另一个文件以只读方式访问cardDefinitions - 对于这个问题,'extern static'是否正确?

我知道这是一个涉及多个问题的问题,但我确实对如何处理此事有些模糊。

谢谢。

编辑:

明确一下,我的目标是能够像这样进行操作:

int cost = cardDefinitions[cardNumber].cost;
或者
int result = cardDefinitions[cardNumber].do_action(state, choice1, choice2);

可以避免到处使用庞大的switch语句。

3个回答

42

你的方法完全正确。

  1. 这个方法可以运行,而且是避免使用大型switch语句的好方法。
  2. 在C中无法内联定义函数,每个函数都必须具有唯一的名称。
  3. extern才是你想要的,不是static。将你的函数改成下面这样:

    struct CARD cardDefinitions[] = { 
        {0, 1, do_card0}, 
        {1, 3, do_card1} 
    }; 
    

    然后在适当的头文件中:

    extern struct CARD cardDefinitions[];
    

4
在C语言中,实际上可以有“内联”函数。你要找的词是“匿名函数”。此外,我建议在数组的最后添加{0, 0, NULL},这样你就不需要单独存储它的大小了。 - Philip
@Philip 嗯,那些“内联”与他所说的完全无关,但是没错,你是对的。我更喜欢在这种情况下使用“sizeof(cardDefinitions)/sizeof(cardDefinitions[0])”技巧。 - Michael Mrozek
@Michael:如果你声明了"extern struct CARD cardDefinitions[]",这个技巧就不起作用了。 - Philip

3

您的方法是正确且可行的。您的函数指针语法是正确的,除了您没有使用参数名称——仅使用类型:

int (*do_actions)(struct GAME_STATE *, int, int);

1
  1. 那应该可以正常工作。如果你为每张卡片都做一个函数,似乎会有很多函数,但也许这个特定的游戏需要那种程度的控制。

  2. 你不能在内联中定义它们,但你可以进行前向声明。在结构初始化中需要使用 &func_name

  3. 不行;extern 表示变量在另一个文件中声明,因此在该位置声明外部变量是没有意义的。此外,static 表示该变量只能从当前文件访问,这与你想要的相反。使其只读需要一个 getter 函数,但如果你只想从另一个文件访问它,请在此处正常声明 (struct cardDefinitions[] = {...}),并在另一个文件中使用 extern (extern struct cardDefinitions[];)。


3
你不需要做 &func_namefunc_name&func_name 都可以(裸函数名会被转换为指向函数的指针,类似于裸数组名被转换为指向数组第一个元素的指针的方式)。 - caf
2
  1. 是的,这个游戏(“Dominion”)就是这样荒谬。
  2. 感谢您澄清了 extern 部分,现在更有意义了。我的方法只是把关键词扔进去直到它起作用...
- russell_h
哇,我之前实际上开始了那个游戏的实现,但后来觉得和现实生活相比可能没那么有趣。你可能不需要为每张卡片编写单独的函数,只需定义卡片赋予你多少钱、让你抽多少张牌、获得多少购买和行动机会以及价值多少胜利点数的属性即可,这将涵盖大部分情况。 - Michael Mrozek
@caf 有趣。有些情况下需要使用 &,因为我以前遇到过这种情况,并养成了总是包含它的习惯,但你说得对,它显然并不总是必需的。 - Michael Mrozek
2
唯一需要使用 & 的情况是当您将其用作 sizeof 运算符的操作数时(在这种情况下,函数指示符不会衰减为函数指针,并且在函数指示符上使用 sizeof 是不允许的)。 - caf
@Michael 这实际上是一个课堂作业(实现三个Dominion游戏的特定子集),所以很可能你比我更了解它。就像你说的,每张牌都有某些共同的参数(抽取x张牌等)。但几乎每一张“王国卡”似乎都描述了一些动作,比如“弃掉这张牌。获得一张成本高达5的牌”-在查看卡牌列表时,我找不到一个好的方法来概括这些内容,至少不能根据我需要实现的API。 - russell_h

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接