是否可以调用C函数,并将其名称作为字符串?

时间:2010-02-02 14:16:40

标签: c function

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

如何将函数命名为字符串,如何调用函数? 是否可以使用scanf读取的字符串直接用于调用 一个功能?

我已经想过if(strcmp(str,“string”))然后调用函数。

但还有其他方法吗?

7 个答案:

答案 0 :(得分:11)

既然没有提到什么是函数,也没有提及它的参数,我会想象它是这样的:

typedef void (*foo)();
struct puzzleFoo{
   char *function_name;
   foo *fn;
};

使用字符串参数

基于结构创建查找表
struct puzzleFoo *Lookup(const char *function_name);

然后遍历查找puzzleFoo的{​​{1}} e的数组/列表,并执行名为function_nam的函数指针。

答案 1 :(得分:10)

使用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);
}

答案 2 :(得分:3)

你可能偷偷摸摸:

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

或者您可以使用dlopendlsym(或等效的),具体取决于您的执行环境,但这些不是标准C,因此可能不是一个选项。

除了可执行文件或设计可怕的宏(这很可能归结为上面显示的if语句的集合或函数)之外,这就是我所拥有的非常可靠的东西。某种指针数组。)

答案 3 :(得分:3)

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

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

答案 4 :(得分:3)

是的,有时候。

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

找到您的功能

在Windows上,您通常会分别拨打LoadLibrary()GetProcAddress()

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

另外,不要忘记,如果你的库是用C ++编写的,那么你可能不得不应对名称修改。您需要了解编译器使用的修改方法。

答案 5 :(得分:2)

取决于你想要打电话的功能。
如果这是来自DLL或其他类型的导出二进制文件的函数 - 当然你可以 这有一个普通的API 如果此函数未导出并且是以可执行文件编译的 - 当然不是,因为func名称等信息会被截断。

答案 6 :(得分:1)

我把它写在了我的头顶 - 不能保证它甚至会编译,但它可能非常接近。足以接受采访。

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