【问题标题】:C - should I use a variable array of function pointersC - 我应该使用函数指针的可变数组吗
【发布时间】:2017-07-09 01:25:20
【问题描述】:

这个问题是关于如何在我设计程序的层面上解决我的问题。对于一个学校项目,我正在构建一个外壳,它具有几个内置功能。这些函数的目的之一 (cmd_type) 是检查提供的参数是否在该函数列表中。这是它的部分实现:

int cmd_type(int argc, char *argv[]) {
    if (argc == 2) {
        for (int i = 0; i < BUILTIN_FUNC_COUNT; i++) {
            if (strcmp(cmds_name[i], argv[1]) == 0) {
                printf("%s is a shell builtin\n", argv[1]);
                return 0; // found it
            }
        }

        // still need to search path, call stat(path/cmd)
        errmsg("not implemented! type", 1);
    } else {
        err_msg("type", 1);
    }
}

为我的 shell 支持的每个函数定义手动 if 语句听起来是个糟糕的选择,因为列表可能会随着时间的推移而扩展,而且我无论如何都需要存储函数名称列表。所以最初,我打算定义一个函数名数组和一个它们的指针数组,如下所示:

char cmds_name[BUILTIN_FUNC_COUNT-1][16];
char (*cmds_ptr)(int,*char[])[BUILTIN_FUNC_COUNT-1];
// make list of built-in funcs
strcpy(cmds_name[0], "exit");
strcpy(cmds_name[1], "cd");
// make list of func pointers
cmds_ptr[0] = &cmd_exit;
cmds_ptr[1] = &cmd_cd;

它们的访问方式如下:

// try builtin cmds
for (int i = 0; i < BUILTIN_FUNC_COUNT; i++) {
    if (strcmp(cmds_name[i], argv[0]) == 0) {
        last_cmd_err = (*cmds_ptr[i])(argc, argv);
        continue; // we found it, so next loop
    }
}

然后他们每个人都会很高兴地将 (int argc, char *argv[]) 作为参数。但是除了这些参数之外, cmd_path() 还需要访问列表,所以我必须将它定义为全局,或者定义一个指向它的全局指针......在研究这个的过程中,我找到了这个答案,说类似的方法真的很糟糕:https://stackoverflow.com/a/41425477/5537652

所以我的问题是:这是解决这个问题的好方法,还是我应该只做 if/else 语句/有更好的方法吗?你会推荐一个指向函数名数组的全局指针吗?

【问题讨论】:

  • 解决它的一种方法是定义一个结构数组(而不是两个独立的名称和指针数组),并使用int builtin_cmd(int argc, char **argv, void *extra); 的原型定义函数。额外的指针指向函数需要的任何额外信息。如果你能设计一个类型——可能是某种结构指针——而不是空泛的void *,那会更好,但这是最通用的类​​型。不需要额外信息的函数可以传递一个空指针,也可以忽略它们传递的指针。
  • 在全局化或隐藏之前,将具有名称和函数指针的结构设为对结构,并通过按名称在数组中对它们进行排序来使用二进制搜索。
  • OT:此char (*cmds_ptr)(int,*char[])[BUILTIN_FUNC_COUNT-1]; 与此for (int i = 0; i &lt; BUILTIN_FUNC_COUNT; i++) { if (strcmp(cmds_name[i], ... 不太对应。

标签: c


【解决方案1】:

我打算像这样提议structurecmd_namefunction pointer

typedef struct{
  char cmds_name[16];
  char (*cmds_ptr)(int,*char[]);
} cmd_type;

现在为您的所有cmds 定义一个这种类型的static 表:

static const cmd_type cmd_table[] = {
  {"exit", &cmd_exit},
  {"cd", &cmd_cd},
  .......
  .......
};

最后像这样访问它:

for (int i = 0; i < BUILTIN_FUNC_COUNT; i++) {
  if (strcmp(cmd_table[i].cmds_name, argv[0]) == 0) {
    last_cmd_err = (*cmd_table[i].cmds_ptr)(argc, argv);
    continue; // we found it, so next loop
  }
}

在 if-else 与全局表之间进行选择的决定取决于个人品味和编码风格。我更喜欢上述解决方案,因为它提高了**代码可读性**并减少了混乱。您的环境中可能存在其他可能会影响您的决定的约束条件 - 例如,如果表条目的数量很大并且全局内存空间存在限制 - if-else 路由将是更好的选择..

HTH!

【讨论】:

    【解决方案2】:

    我不会使用if-else 声明。 https://stackoverflow.com/a/41425477/5537652提出的解决方案(2)没有错。

    您可以有一个带有字符串的表和一个为条目提供服务的函数:

    typedef struct cmd_desc
    {
      char cmd[80];
      int builtin_cmd(int argc, char **argv, void *extra);
    } CMD_DESC;
    
    static CMD_DESC descTable[] =
    {
      { "exit",                 cmd_exit      },      
      { "cd",                   cmd_cd        },   
      { "$ON_OPEN_CMD",         OnOpenCmd     },
      { "$OPEN_EXTRA_CMD",      OpenExtraCmd  },
      { "$AC",                  ActionCmd     },
      { "$AD",                  ActionDataCmd },
      { "$EC",                  ExtraCmd      },
      { "$TC",                  TextCmd       },
      { "",                     NULL          }
    };
    
    int cmd_exit (int argc, char **argv, void *extra)
    {
      //...
    }
    

    访问/执行:

    for (int tokenIndex=0; strcmp(descTable[tokenIndex].cmd,""); tokenIndex++) //search table 
    {
        if ( strcmp( (descTable[tokenIndex]).cmd, argv[0] ) == 0 )
        { 
            int ret = (*(descTable[tokenIndex]).builtin_cmd( argc, argv, extra);
        }
    }
    

    我在我的应用程序中使用了上述方法,它对我很有效。

    表格可以轻松扩展,表格的可读性优于if/else链。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-20
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 2014-11-09
      • 2021-01-24
      • 2013-08-24
      • 2011-11-08
      相关资源
      最近更新 更多