【问题标题】:Determining to which function a pointer is pointing in C?确定指针指向 C 中的哪个函数?
【发布时间】:2016-01-17 20:33:35
【问题描述】:

我有一个函数指针,假设任何签名。 而且我有 5 个具有相同签名的不同功能。

在运行时,其中一个被分配给指针,然后调用该函数。

在这些函数中不插入任何打印语句,如何知道指针当前指向的函数的名称?

【问题讨论】:

  • 函数名只是程序员的帮手。在运行时,程序没有函数名的概念(除非它被编译为添加调试符号)。
  • 你不能,而且除了调试之外你不应该这样做。
  • @cad 除非您将标准标识符 __func__ 添加到代码中,在这种情况下,编译器会将与函数名称对应的字符串文字链接到程序中。
  • 是的,有这个新奇的 C99 标准,让我们像 1999 年一样狂欢吧:)
  • @Sumit:你到底为什么要问?请编辑您的问题以解释原因并改进它...

标签: c function-pointers


【解决方案1】:

您必须检查您的指针指向的 5 个函数中的哪一个:

if (func_ptr == my_function1) {
    puts("func_ptr points to my_function1");
} else if (func_ptr == my_function2) {
    puts("func_ptr points to my_function2");
} else if (func_ptr == my_function3) {
    puts("func_ptr points to my_function3");
} ... 

如果这是您需要的常见模式,请使用结构表而不是函数指针:

typedef void (*my_func)(int);

struct Function {
    my_func func;
    const char *func_name;
};

#define FUNC_ENTRY(function) {function, #function}

const Function func_table[] = {
    FUNC_ENTRY(function1),
    FUNC_ENTRY(function2),
    FUNC_ENTRY(function3),
    FUNC_ENTRY(function4),
    FUNC_ENTRY(function5)
}

struct Function *func = &func_table[3]; //instead of func_ptr = function4;

printf("Calling function %s\n", func->func_name);
func ->func(44); //instead of func_ptr(44);

【讨论】:

  • func_table->func 应该是 func-> 以使用此处选择的条目 [3] 并不总是条目 [0]
【解决方案2】:

通常,在 C 中,程序员无法使用这些东西。

可能有通过使用调试符号等特定于系统的方式到达那里,但您可能不希望依赖这些方式来使程序正常运行。

但是,您当然可以将指针的值与另一个值进行比较,例如

if (ptr_to_function == some_function)
    printf("Function pointer now points to some_function!\n");

【讨论】:

  • 在 C 中,程序员可以通过__func__ 标识符实际使用这些东西。
  • @Lundin 但这不是仅在函数的 body 中可用吗? OP 想单独从ptr_to_function 中查找名称。
  • @Lundin,这是一个预处理器指令,它获取当前具有范围的函数的当前名称,它甚至不适用于该问题。
  • @Sam __func__ 仅在函数内可用,因此在这里不是一个好的答案,但它是一个普通的(准确地说,TP7)标识符,而不是像 __FILE__ 和 @ 这样的预处理器宏987654326@,也不是像#if这样的指令。
【解决方案3】:

函数名称在运行时将不可用。

C 不是一种反思性语言。

要么维护一个以函数名称为键的函数指针表,要么提供一种调用每个返回名称的函数的模式。

【讨论】:

  • reflective 是我缺少的词。赞一个!
  • 这样,反思的答案就写出来了。
  • 除非您在代码中添加标准标识符__func__,在这种情况下编译器会将与函数名称对应的字符串文字链接到程序中。
【解决方案4】:

调试器可以告诉你(即函数的名称,给定它的地址)。

未剥离的ELF 可执行文件的符号表也有帮助。见nm(1)objdump(1)readelf(1)

另一种 Linux GNU/libc 特定方法是在运行时使用 dladdr(3) 函数。假设您的程序很好地动态链接(例如使用-rdynamic),它可以在给定某个地址(全局命名函数)的情况下找到符号名称和共享对象路径。

当然,如果您只有五个给定签名的函数,您可以比较您的地址(与它们的五个地址)。

请注意,某些函数没有任何((全局可见)名称,例如static 函数。

有些函数可能是dlopen-ed 和dlsym-ed(例如在插件内部)。或者他们的代码在运行时由一些 JIT 框架(libjitgccjitLLVMasmjit)合成。而optimizing compiler 可以(并且确实!)内联函数、克隆它们、tail-call 它们等等......所以你的问题一般来说可能没有任何意义......

另请参阅 backtrace(3) 和 Ian Taylor 在 GCC 内的 libbacktrace

但总的来说,你的任务是不可能的。如果您确实需要这种可靠的反射信息,请自行管理(以Pitrat's CAIA 系统为例,或者以某种方式查看我的MELT 系统),也许通过在构建期间生成一些代码。

【讨论】:

    【解决方案5】:

    要知道函数指针指向的位置是您必须在程序中跟踪的内容。最常见的是声明一个函数指针数组,并使用一个 int 变量作为该数组的索引。

    话虽如此,现在也可以通过使用__func__ 标识符在运行时判断当前正在执行的函数:

    #include <stdio.h>
    
    typedef const char* func_t (void);
    
    const char* foo (void)
    {
      // do foo stuff
      return __func__;
    }
    
    const char* bar (void)
    {
      // do bar stuff
      return __func__;
    }
    
    int main (void)
    {
      func_t* fptr;
    
      fptr = foo;
      printf("%s executed\n", fptr());
    
      fptr = bar;
      printf("%s executed\n", fptr());
    
      return 0;
    }
    

    输出:

    foo executed
    bar executed
    

    【讨论】:

    • 这类似于在函数中放置一个打印语句。我认为这个限制意味着你不能修改函数内部的代码。
    • 这还要求函数返回const char *(或原型上的一些等效限制)。这也需要您调用函数...
    • @ratchetfreak 您需要知道调用了哪个函数,这解决了哪个问题。或者你需要知道你正在调用哪个函数......这没有多大意义。你怎么会不知道你在调用哪个函数?基本程序设计解决了这个问题:如答案中所述,制作一个数组和一个索引变量,就这么简单。
    【解决方案6】:

    完全没有 - 函数的符号名称在编译后消失。与反射语言不同,C 不知道程序员如何命名其语法元素。特别是编译后没有按名称进行“函数查找”。

    您当然可以拥有一个函数指针的“数据库”(例如一个数组),您可以将当前指针与之进行比较。

    【讨论】:

    • 除非您在代码中添加标准标识符__func__,在这种情况下编译器会将与函数名称对应的字符串文字链接到程序中。
    • @Lundin 仅在函数内,因此您必须向每个函数添加代码以“调用”它并让它返回名称,或者您可以进行某种骇人听闻的内存扫描查找函数名称。无论哪种方式,都不会简单。
    • @DrewMcGowen 按照设计,它只能从函数内部获得,因为这是唯一可能感兴趣的地方。要知道函数指针指向的位置,实际上只需简单地跟踪您自己的代码所做的事情。但是,这个答案有点不准确,因为在某种程度上确实存在函数查找功能。
    • 嗯,该函数可以复制它,返回它或加载到传递的字符中*
    • @MartinJames:但如果您只是开始修改参数或返回函数类型,您将不再处理相同的函数。
    【解决方案7】:

    这非常糟糕且不可移植,但假设:

    1. 您使用的是 Linux 或类似的基于 ELF 的系统。
    2. 您正在使用动态链接。
    3. 该函数在共享库中,或者您在链接时使用了-rdynamic
    4. 可能还有很多其他你不应该做的假设......

    您可以通过将函数的地址传递给非标准的dladdr 函数来获取函数的名称。

    【讨论】:

      【解决方案8】:
      1. 将链接器设置为输出 MAP 文件。
      2. 暂停程序
      3. 检查指针中包含的地址。
      4. 在 MAP 文件中查找地址以找出所指向的函数。

      【讨论】:

      • 确实如此。或者在调试构建二进制文件上运行 nm
      【解决方案9】:

      指向 C 函数的指针是地址,就像任何指针一样。您可以从调试器中获取值。您可以将指针转换为具有足够位以完全表达它的任何整数类型,然后打印它。任何可以使用指针的编译单元,即在作用域内具有函数名称,都可以打印指针值或将它们与运行时变量进行比较,而无需触及函数内部的任何内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-09
        • 1970-01-01
        • 1970-01-01
        • 2018-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多