【问题标题】:How to implement map that associates a name with void function in C?如何在 C 中实现将名称与 void 函数相关联的映射?
【发布时间】:2015-09-02 17:32:14
【问题描述】:

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

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;
}

【问题讨论】:

  • 而不是void* (*f_ptr)(void*); 定义int (*f_ptr)(int);
  • @alk 正如我上面所指出的,我想使用 void* 的原因是我可以允许不同类型的函数。
  • 使用void*(*)(void*) 只允许您使用获取一个指针值并返回一个指针值的函数。

标签: c functional-programming sicp


【解决方案1】:

字符串比较

这不是你在 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);

等等

更好的实现?

将函数映射到名称的“真正”实现将使用哈希表,而不是线性搜索匹配的名称。在 C 中实现哈希表会增加复杂性,可能不值得做这个练习。

【讨论】:

  • 比较字符串文字的指针可能有效,但不能保证它们只有一个相同字符串的实例。
  • 谢谢,我会记住这一点,但这不会改变输出或错误消息。
【解决方案2】:

但是我得到了

[...]

fib(7) = 0

代码未将第二个for-loop 的i 初始化为0

【讨论】:

  • 是的,不要懒惰使用for 循环。如果你想继续上一个循环,你可以写(for i=i; ...),编译器会做正确的事情。
  • 谢谢,就是这样。至于 void* 如果我想允许不同类型的函数,有没有办法避免所有这些警告?
  • @TarBall: "避免所有这些警告" 只能通过(脏)强制转换,可能会调用未定义的行为。
  • 此类方法最好使用 C++。
  • @alk 很抱歉,如果这是一个愚蠢的问题,但我不熟悉脏铸造这个词,你愿意详细说明吗?
猜你喜欢
  • 2022-07-15
  • 1970-01-01
  • 2012-01-04
  • 2012-01-21
  • 1970-01-01
  • 2021-09-15
  • 1970-01-01
  • 1970-01-01
  • 2015-11-14
相关资源
最近更新 更多