【问题标题】:Is it possible to call a C function, given its name as a string?是否可以调用 C 函数,将其名称作为字符串?
【发布时间】:2010-02-02 14:16:40
【问题描述】:

我在一个 C 谜题中看到了这个问题!! 这真的可能吗?

如果函数名是字符串,我如何调用它? 是否可以使用用 scanf 读取的字符串直接用于调用 一个函数?

我已经想到了 if(strcmp(str,"string"))then 调用函数。

但是还有其他方法吗?

【问题讨论】:

  • 是的 Qt 用它来实现信号槽
  • 问题的根源在于编译器不会将函数的名称放入可执行文件中(调试版本可能除外)。 C 语言不要求编译器保留函数的名称。
  • 什么是函数?代码编译和链接后就不可能说了。编译器可以为所欲为:创建新函数、删除旧函数(内联)等。
  • IIRC,如果有可能使用它的指针,编译器可以保留函数的非内联版本。

标签: c function


【解决方案1】:

由于没有提到什么是函数,也没有提到它的参数,我想它是这样的:

typedef void (*foo)(); 结构拼图Foo{ 字符 *function_name; 富 *fn; };

根据结构创建查找表,使用字符串参数

结构拼图Foo *Lookup(const char *function_name);

然后遍历一个数组/列表寻找puzzleFoofunction_name并执行名为fn的函数指针。

【讨论】:

  • char* 中的 puzzleFoo 应该是 const char*。此外,您可以定义一个宏来为您创建一个puzzleFoo#define FN_ENTRY(funcPtr) { #funcPtr, &funcPtr } 然后您只需说puzzleFoo functionList[] = { FN_ENTRY(fooA), FN_ENTRY(fooB) }; size_t functionListLength = sizeof(functionList) / sizeof(puzzleFoo);
  • @Mike D:这是一个很好的建议! :) 感谢您的输入! :)
【解决方案2】:

对于 POSIX.1-2001,您应该使用 dlopen() and dlsym()。在 Windows 上使用 GetModuleHandleEx()GetProcAddress()

这是手册中的一个逐字示例,它从数学库中加载名为“cos”的函数并确定 2.0 的余弦:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int
main(int argc, char **argv)
{
   void *handle;
   double (*cosine)(double);
   char *error;

   handle = dlopen("libm.so", RTLD_LAZY);
   if (!handle) {
       fprintf(stderr, "%s\n", dlerror());
       exit(EXIT_FAILURE);
   }

   dlerror();    /* Clear any existing error */

   /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
      would seem more natural, but the C99 standard leaves
      casting from "void *" to a function pointer undefined.
      The assignment used below is the POSIX.1-2003 (Technical
      Corrigendum 1) workaround; see the Rationale for the
      POSIX specification of dlsym(). */

   *(void **) (&cosine) = dlsym(handle, "cos");

   if ((error = dlerror()) != NULL)  {
       fprintf(stderr, "%s\n", error);
       exit(EXIT_FAILURE);
   }

   printf("%f\n", (*cosine)(2.0));
   dlclose(handle);
   exit(EXIT_SUCCESS);
}

【讨论】:

  • 哈,我们的答案差别不大。你已经提供了一些关于名称处理的额外内容,但我知道菜鸟喜欢代码示例,只给出函数名称是远远不够的。
【解决方案3】:

你可能会偷偷摸摸:

if (strcmp (fn, "function1") == 0) function1();
if (strcmp (fn, "function2") == 0) function2();
if (strcmp (fn, "function3") == 0) function3();

或者您可以根据您的执行环境使用dlopendlsym(或等效项),但它们不是标准C,因此可能不是一个选项。

除了令人难以置信的不可移植的可执行文件或设计可怕的宏(这很可能归结为上面显示的if 语句的集合或函数指针数组之外,这就是我所拥有的一切某种)。

【讨论】:

    【解决方案4】:

    如果函数在共享库中可用,您可以在运行时加载共享库并访问符号表,该表可以转换为函数名和指针。至于确保函数调用签名是正确的,你自己。

    VxWorks 为其内置 shell 执行此操作。

    【讨论】:

      【解决方案5】:

      是的,有时。

      在 Linux 下,您可以使用 dlopen() 打开包含所需函数的共享库,甚至可以访问当前可执行文件并使用 dlsym() 定位您的函数

      在 Windows 上,您通常会分别调用 LoadLibrary()GetProcAddress()

      如果相关库的符号表已被剥离,或者在某些情况下,如果方法是静态/私有的,那么您将无法使用这种方法访问它们。

      另外,别忘了,如果你的库是用 C++ 编写的,那么你可能不得不面对名称修改问题。面对这种情况,您需要了解编译器使用的修饰方法。

      【讨论】:

        【解决方案6】:

        这取决于您要调用的函数。
        如果这是来自 DLL 或其他类型的导出二进制文件的函数——当然可以。
        对此有一个普通的 API。
        如果这个函数没有被导出并且被编译成可执行文件——当然不会,因为函数名称等信息会被截断。

        【讨论】:

          【解决方案7】:

          我在脑海中写下了这个 - 不能保证它甚至会编译,但它可能非常接近。适合面试。

          #include <stdio.h>
          #include <unistd.h>
          char *program = "#include<stdio.h>\
          \
          void Func1() { printf("hi\n"); }\
          void Func2() { printf("hello\n"); }\
          void Func3() { printf("hey\n"); }\
          main() {\
          ";
          char *program1 = "}\n";
          
          static in ContainsFunction(char *func)
          {
              char *match = strstr(func, program);
              if (!match || match - program < 5)
                  return 0;
              int len = strlen(func);
              return strcmp(match-5, program) == 0 && match[len + 1] == '(' && isalnum(func[len-1]);
          }
          
          static void Compile(char *prog)
          {
              pid_t pid = fork();
              if (pid == 0) {
                  execl("/usr/bin/gcc", "gcc", prog, (char *)0);
              }
              else if (pid < 0) { printf("fork fail\n"); }
              else {
                  pid_t ws = wait();
              }
           }
          
           static void Execute(char *prog)
           {
              pid_t pid = fork();
              if (pid == 0) {
                  execl(prog, prog, (char *)0);
              }
              else if (pid < 0) { printf("fork fail\n"); }
              else {
                  pid_t ws = wait();
              }
           }
          
          static void CallFunction(char *funcname)
          {
              FILE *fp = fopen("foo.c", "w");
              fputs(program, fp);
              fprintf(fp, "\t%s();\n", funcname);
              fputs(fp, program1);
              fclose(fp);
          
              Compile("foo.c");
              Execute("a.out");
          }
          
          int main()
          {
              char funcname[8192]; /* too small, fail */
              puts("Who ya gonna call?");
              gets(funcname);
              if (ContainsFunction(funcname)) { CallFunction(funcname)); }
              else { printf("fail - no function %s\n", funcname); }
          }    
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-05-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-08-22
            相关资源
            最近更新 更多