【问题标题】:How can we know the caller function's name?我们如何知道调用者函数的名称?
【发布时间】:2013-04-19 08:07:11
【问题描述】:

在 C 语言中,__FUNCTION__ 可用于获取 当前 函数的名称。 但是如果我定义了一个名为 a() 的函数并在 b() 中调用它,如下所示:

b()
{
    a();
}

现在,在源代码中,有很多像 b() 这样调用 a() 的函数,例如c(), d(), e()...

是否可以在 a() 中添加一些代码来检测调用 a() 的函数的名称?

进一步:

  1. 很抱歉出现误导性错字。我已经更正了。
  2. 我正在尝试找出哪个函数调用 a() 以进行调试。一世 不知道你在同样的情况下是怎么做的?
  3. 而且我的代码在vxWorks下,但不知道是C99相关还是 别的东西。

【问题讨论】:

  • 不确定。不过this可能对你有帮助
  • 为什么要这样做?是为了调试目的吗?
  • 如果这只是为了调试,您可以将 a 定义为宏,这样使用 __func__ 将根据需要返回 b。
  • 哪个编译器有FUNCTION来返回当前函数的名字?在 C 标准中预见到的标识符是 __func__ IIRC。
  • 你写的是错误的。 __FUNC__ 返回当前方法名 AFAIK。要获取调用者,您必须查看堆栈跟踪

标签: c


【解决方案1】:

没有什么可以只在a中做的。

然而,通过一个简单的标准宏技巧,你可以实现你想要的,IIUC 显示调用者的名字。

void a()
{
    /* Your code */
}

void a_special( char const * caller_name )
{
    printf( "a was called from %s", caller_name );
    a();
}

#define a() a_special(__func__)

void b()
{
    a();
}

【讨论】:

  • 我必须将 caller_name 变量声明为 const char* caller_name,但它起作用了!
  • 精度:__func__ 是 C99 标准的一部分,[...] __FUNCTION____func__ 的另一个名称,用于向后兼容旧版本的 GCC。 (来源:gcc.gnu.org/onlinedocs/gcc/Function-Names.html
  • 您能告诉我如何在函数声明中使用带有省略号的示例吗? void a(int a, char *fmt, ...)如何将其包含在宏定义中?
  • @mulg0r 看看 va_listva_arg 和同伴。但是我怀疑您是否可以将此技巧应用于使用省略号的函数:它应该改用va_list 编写,就像vprintf 函数系列一样:-(
  • @mulg0r 使用variadic macros 你可以实现你想要的(例如#define a(...) a_special (__func__, __VA_ARGS__),对于一个函数void a_special(char const * caller_name, ...){})它们自C99以来就在C中,自C++ 11以来就在C++中。
【解决方案2】:

您可以使用内置 gcc 来做到这一点。

void * __builtin_return_address(int level)

下面的方法应该打印一个函数 a() 的直接调用者。

例子:

a() {
    printf ("Caller name: %pS\n", __builtin_return_address(0));
}

【讨论】:

  • 这会打印返回地址(仍然有用),但不会打印函数名称。
【解决方案3】:

如果你使用的是Linux系统,你可以使用backtrace()函数。

有关更多详细信息和代码示例,请参阅man page

【讨论】:

  • 2020 年末它也可以在 macOS 上使用。
【解决方案4】:

试试这个:

void a(<all param declarations to a()>);

#ifdef DEBUG
#  define a(<all params to a()>) a_debug(<all params a()>, __FUNCTION__)
void a_debug(<all params to a()>, const char * calledby);
#endif

void b(void)
{
  a(<all values to a()>);
}

#ifdef DEBUG
#  undef a
#endif

void a(<all param declarations to a()>)
{
  printf("'%s' called\n", __FUNCTION__);
}

#ifdef DEBUG
void a_debug(<all param declarations to a()>, const char * calledby)
{
  printf("'%s' calledby '%s'", __FUNCTION__, calledby);
  a(<all params to a()>);
}
#endif

如果例如&lt;all param declarations to a()&gt;int i, double d, void * p 那么&lt;all params to a()&gt;i, d, p


或者(不那么邪恶 ;->> - 但更多的代码修改,因为每次调用 a() 都需要被触及):

void a((<all params of normal a()>    
#ifdef DEBUG
  , const char * calledby
#endif
  );

void a((<all params of normal a()>    
#ifdef DEBUG
  , const char * calledby
#endif
  )
{
#ifdef DEBUG
  printf("'%s' calledby '%s', __FUNCTION__, calledby);
#endif
  ...
}

...

void b(void)
{
    a(<all params of normal a()>
#ifdef DEBUG
      , __FUNC__
#endif
    );
}

__FUNCTION__ 在 GCC 上可用(至少?),如果使用不同的 C99 编译器,请将其替换为 __func__

【讨论】:

  • :D 这对我来说看起来很邪恶,但它是一种很酷的方式 IMO
【解决方案5】:

参考:https://www.gnu.org/software/libc/manual/html_node/Backtraces.html

回溯是当前活动的函数调用列表 在一个线程中。检查程序回溯的常用方法是 使用外部调试器,例如 gdb。但是,有时它很有用 从程序中以编程方式获取回溯,例如, 用于记录或诊断。

头文件 execinfo.h 声明了三个函数,它们获取和 操作当前线程的回溯。

【讨论】:

    【解决方案6】:

    如果您只是在知道自己在哪里进行日志记录/调试之后,您可以使用宏来避免 __func__ 给出您的日志记录/调试函数的名称,但给出调用它的函数的名称。

    在宏中不会导致对__func__ 的更改,但会“感觉”像使用函数。

    例如

    #define LOG(s, data...) log("%s: "s, __function__, ## data)
    

    【讨论】:

    • 对不起,但这并不能回答 OP 的问题。
    • 既然我们不知道为什么他们需要这样做,它可能会。
    • 可以确认 Joe,这正是我要寻找的东西
    【解决方案7】:

    如果你的平台是 Windows,你可以使用这个:walking the callstack

    【讨论】:

      【解决方案8】:

      您可以使用作为参数传递给 a() 的整数标识符标记每个调用 a() 的函数,然后使用 switch- a() 中的 case 构造来判断哪个函数调用了 a()printf() 会告诉哪个函数调用了a() 取决于整数标识符值,如果您将其用作 a()

      中的 switch-case 构造的参数
      #include<stdio.h>
      
      void a(int);
      void b();
      void c();
      void d();
      
      int main(void)
      {
      
      b();
      c();
      d();
      
      }
      
      void b()
      {
      
      int x=1;
      a(x);
      
      }
      
      void c()
      {
      
      int x=2;
      a(x);
      
      }
      
      void d()
      {
      
      int x=3;
      a(x);
      
      }
      
      void a(int x)
      {
      
      switch(x)
      {
      case 1:
      printf("b called me\n");
      break;
       case 2:
      printf("c called me\n");
      break;
      case 3:
      printf("d called me\n");
      }
      
      }
      

      【讨论】:

      • 这是一种“解决方法”,而不是解决方案。如果您需要提供外部 API - 您的直接方式将毫无用处。
      • @DmitrySazonov 我回答了发帖者的要求。不多也不少。这是一个解决方案。
      • 这不是一个解决方案,因为您更改了函数签名。没关系,这只是我的看法;)
      • 或者,您可以将__FUNCTION__ 传递给a()。不需要开关,它可以处理新函数、更改函数名称等。当然,正如 Dmitry Sazonov 所说,添加该参数确实会更改函数的签名。
      【解决方案9】:

      如果有问题的函数在不同的c文件中,你可以这样做

      #define name_of_function(...) \
          printf("Function %s is parent\n", __FUNCTION__); \
          name_of_function(__VA_ARGS__);
      

      它位于 c 文件的顶部

      #ifdef name_of_function
      #undef name_of_function
      #endif
      

      如果它们在同一个文件中,您可以将函数定义包装在第二个宏中,然后在最后重新定义第一个宏。 它的可扩展性不是很好,因为您无法从其他定义中生成新定义,但是如果您尝试为特定功能追踪父级,它可以毫无意义地工作。

      https://godbolt.org/z/f2jKOm

      【讨论】:

        【解决方案10】:
        #include <stdio.h>
        #include <stdlib.h>
        

        #define FUNCTION_NAME(FUNCTION) printf("FUNCTION=%s \r\n", #FUNCTION);

        int a() {
          printf("A function call");
        }
        
        int b() {
          printf("B function call");
        }
        
        int main(){
        FUNCTION_NAME(a);
        FUNCTION_NAME(b);
        return 0;
        

        }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-18
          • 2015-10-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多