如何在 C 中实现将名称与 void 函数相关联的映射?

How to implement map that associates a name with void function in C?

在阅读了计算机程序的结构和解释 (SICP) 之后,我决定找到一种使用 C 来实现这些函数式编程技术的方法。我尝试编写一个程序来生成一对,其第一个参数是名称函数和第二个 arg 是任何接受一个 arg 和 returns 一个 arg 的函数。使用下面的实现我期待看到 输出如:

fact(7) = 5040
fib(7) = 13

但我得到的是

fact(7) = 5040
fib(7) = 0

连同警告

$ cc map.c
map.c: In function ‘main’:
map.c:41:17: warning: assignment from incompatible pointer type [enabled by default]
   maps[0].f_ptr = &fact;
                 ^
map.c:43:17: warning: assignment from incompatible pointer type [enabled by default]
   maps[1].f_ptr = &fib;
                 ^
map.c:47:7: warning: passing argument 1 of ‘maps[i].f_ptr’ makes pointer from integer without a cast [enabled by default]
       ans = (int) maps[i].f_ptr((int) num);
       ^
map.c:47:7: note: expected ‘void *’ but argument is of type ‘int’
map.c:47:13: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
       ans = (int) maps[i].f_ptr((int) num);
             ^
map.c:52:7: warning: passing argument 1 of ‘maps[i].f_ptr’ makes pointer from integer without a cast [enabled by default]
       ans2 = (int) maps[i].f_ptr((int) num);
       ^
map.c:52:7: note: expected ‘void *’ but argument is of type ‘int’
map.c:52:14: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
       ans2 = (int) maps[i].f_ptr((int) num);

编译中。查看代码,我没有发现问题,但我又很长一段时间没有使用 C 语言了。有没有更好的方法来实现这样的构造,为什么 fib(7) 打印 0 而不是 13?

这是我的代码:

struct Map
{
  char* name;
  void* (*f_ptr)(void*);
};

int fact(int a) {
    if (a == 0)
      return 0;
    if (a == 1)
      return 1;
    return a * fact (a-1);
}

int fib(int a) {
  if (a == 0)
    return 0;
  if (a == 1)
    return 1;
  return fib(a-1) + fib(a-2);
}

int findFunc (char* str, struct Map map)
{
  if (map.name == str)
    return 1;
  return 0;
}

int main()
{
  int i = 0;
  int ans = 0;
  int ans2 = 0;
  int num = 7;

  struct Map maps[2];
  maps[0].name = "fact";
  maps[0].f_ptr = &fact;
  maps[1].name = "fib";
  maps[1].f_ptr = &fib;

  for (i; i < (sizeof(maps)/sizeof(maps[0])); i++) {
    if (findFunc("fact", maps[i]))
      ans = (int) maps[i].f_ptr((int) num);
  }

  for (i; i < (sizeof(maps)/sizeof(maps[0])); i++) {
    if (findFunc("fib", maps[i]))
      ans2 = (int) maps[i].f_ptr((int) num);
  }

  printf("fact(%d) = %d\n", num, ans);
  printf("fib(%d) = %d", num, ans2);
  return 0;
}

字符串比较

这不是您在 C 中进行字符串比较的方式。

if (map.name == str)

这就是您在 C 中进行字符串比较的方式。

if (0 == strcmp(map.name, str))

因为 C 中的字符串只是指向字符的指针,map.name == str 检查 map.namestr 是否是相同的指针(指向相同的内存块),而不是它们是否指向是一样的。

for 循环

您的代码可能正在报告 fib(7) = 0,因为它未能找到 fib。一个可能的罪魁祸首是我提到的字符串比较问题。但是,您的 for 循环语法也很奇怪:

for (i; i < (sizeof(maps)/sizeof(maps[0])); i++) {

您没有将 i 设置为任何内容,所以这意味着,“从我碰巧所在的任何地方开始,执行以下操作...”

要遍历所有地图,请使用:

for (i = 0; i < (sizeof(maps)/sizeof(maps[0])); i++) {

键入警告

正如@alk 在评论中所说,您收到所有这些警告的原因是因为您声明了 void* (*f_ptr)(void*); 的函数类型,即使您的函数是 int (*)(int)。如果你想继续使用 void* 来允许不同的类型,并且你对你的类型足够小心以使其工作,那么你可以添加强制转换来消除警告。

maps[0].f_ptr = (void *(*)(void*)) &fact;
ans2 = (int) maps[i].f_ptr((void*) num);

等等

更好的实施?

将函数映射到名称的“真实”实现将使用散列 table,而不是线性搜索匹配的名称。在 C 中实现散列 table 会增加复杂性,对于本练习可能不值得。

but instead I am getting

[...]

fib(7) = 0

代码没有为第二个 for 循环将 i 初始化为 0