正在获取成员函数内部成员函数的地址

Getting address to a member function inside the member function

本文关键字:函数 成员 地址 内部 获取      更新时间:2023-10-16

我想获取同一成员函数内的成员函数的地址。this指针已经获取,所以我知道从哪个对象调用我。当然,编译器抱怨我试图在不调用该函数的情况下使用指向该函数的指针。我明白了在对象之外使用该规则的意义。难道没有办法获得"我自己"(调用时的函数地址)地址吗?

这仅用于调试目的,应在宏中使用。如果能有办法做到这一点,那就太好了。

澄清代码:

class A
{
    void do_stuff()
    {
        printf("0x%Xn", (void*)(this->do_stuff));
    }
};

这是在一个嵌入式系统上,我不明白为什么绝对没有办法获得do_stuff保存在flash中的地址。

编辑我试着得到这样的"指向成员函数的指针":

printf("CAPP::TASK 0x%08Xn", &CApp::Start);

它给了我0x08013E85,我在链接器映射中查找它,它正是我要查找的函数。这是使用KeilµVision 5作为编译器的mdk arm。这是正确的吗?

编辑2:所有类型转换都会导致类型转换无效。尽管printf的输出是正确的:printf使用可变宏。让我们重建一下:

uint32_t getAdr(uint32_t n, ...)
{
    uint32_t adr=0;
    va_list vl;
    va_start(vl,n);
    adr=va_arg(vl,uint32_t);
    va_end(vl);
    return adr;
}
#define GetAdr(x) getAdr(0,&x);
void CApp::Start()
{  
    uint32_t a = GetAdr(CApp::Start);
    printf("Adr: 0x%08Xn", a);
    ...
}

讨厌的黑客,但它奏效了。但为什么这样做呢?遗憾的是,我没有__va_arg(…)的实现。__va_aorg如何将值类型转换为给定的类型?有没有办法以某种方式使用该功能?

指向成员函数的指针通常是像void*这样的普通指针的两倍大,因此在标准C++中不可能进行直接转换。

https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html记录了一个GCC扩展,它允许您通过组合this&CApp::Start来获得一个普通(非成员)函数指针(该页面还解释了为什么指向成员函数的指针除了函数地址之外还有其他信息)。

这个扩展可以这样使用:

typedef void (*pf_type)(CApp*);
pf_type pf = (pf_type)&CApp::Start;

我想你对va_arg的破解之所以有效,是因为你的编译器将指向成员函数&CApp::Start的指针表示为一个双字对象,函数地址在其中一个字中,你通过varargs破解读取,因为两个字被推到堆栈上,然后你只读取其中一个。使用联合,您可能会得到类似的结果

union dirty_hack
{
  void (CApp::*pmf)();
  uint32_t words[2];
};
dirty_hack x;
x.pmf = &CApp::Start;
printf("Adr: 0x%08Xn", x.words[0]);   // might need to be x.words[1] instead

你可以使用一个模板来概括这一点:

template<typename F>
uint32_t
getaddr(F f)
{
  union dirty_hack
  {
    F pmf;
    uint32_t words[2];
  };
  dirty_hack x;
  x.pmf = f;
  return x.words[0];   // might need to be x.words[1] instead
}
printf("Adr: 0x%08Xn", getaddr(&CApp::Start));

注意:对于指针不是32位的系统,最好使用uintptr_t而不是int32_t来实现可移植性,尽管这种技巧本质上是不可移植的(上面的联合技巧依赖于类型双关,这是一种未定义的行为,但一些编译器支持)。

没有"这个对象的成员函数"这回事。成员函数被同一类的所有对象有效地共享-这只是代码,而不是数据。this通常作为一个额外的隐藏参数传递给这些函数。

实际上,编译器会转换以下内容:

struct Obj
{
  int a;
  int foo(int x) const { return x * a; }
};
int bar(const Obj &o)
{ return o.foo(42); }

变成这样:

struct Obj
{
  int a;
  static int foo(const Obj *this, int x) { return this->x * a; }
};
int bar(const Obj &o)
{ return Obj::foo(&o, 42); }

正如你所看到的,根本不存在"&(o.foo)"这样的东西。obj的所有实例都具有相同的foo

此外,C++不允许您访问这个"静态foo",正是因为它是一个实现细节,如果编译器愿意(或者平台要求),他们可以用不同的方式来实现。您能得到的最好的结果是指向函数&Obj::foo的成员的指针。当然,这不是一个真实的地址,它更像是类Obj中的一个偏移。您需要明确地拼写出它的名称——如果不使用它的名称,就无法引用"封闭函数"。

在不命名函数的情况下,最好的方法是预定义的变量__func__(在C++11中引入),它包含一些编译器特定形式的函数名称。