天天看点

C/C++ 获取函数地址

C

  • C语言中没有类的概念,只有普通的函数。通过函数名就可以得到函数地址
#include <stdio.h>
#include <stdlib.h>

void fun() {
  
}
int main() {
  printf("%p\n", &fun);
}      
  • 对于fun 和 &fun 应该这样理解:
  • fun是函数的首地址,它的类型是void ()
  • &fun表示一个指向函数fun这个对象的地址, 它的类型是void (*)()
  • 因此fun 和 &fun 所代表的地址值是一样的,但类型不一样。
  • fun是一个函数
  • &fun表达式的值是一个指针!

C++

  • 普通函数

    C++的普通函数和C中是一样的,利用函数名就可以获得函数地址。

  • 类静态函数

    本类所有对象公用一个静态函数,所以是同一个地址【其实类的成员函数都只有一个,解释见后文】。和普通函数一样,有了函数名就可以获得地址。

    可以用类名::函数名,也可以用对象.函数名 / 对象指针->函数名。

  • 类成员函数(除了静态函数外的所有类中的函数)

    有这样一个类:

class Base {
public:
  Base() {
    cout << "Base构造" << endl;
}
virtual ~Base() {
  cout << "Base虚析构" << endl;
  }
  virtual void f1() {
    cout << "Base::f1()" << endl;
  }
  void f2() {
    cout << "Base::f2()" << endl;
  }
  virtual void f3() {
    cout << "Base::f3()" << endl;
    //cout << data << endl; 
  }
  int data = 5;
  static void fn() {
  }
};      

如果这样输出:

cout << &Base::f1 << endl;  // 普通类成员函数
cout << &Base::f2 << endl;  // 虚函数

// cout << &p->f1 << endl;
// cout << &p->f2 << endl;
// 这两个都会报错,对象绑定的函数只能用于调用      
  • 静态函数,是独立于对象的,是类拥有的,所以我们调用静态函数,既可以通过类调用也可以通过对象调用。无论是通过类调用还是对象调用,对应的都是同一个函数。
  • 动态函数,只能通过对象来调用。因为在动态成员函数中,往往都需要访问对象的成员变量。我们知道同一类型的不同对象,它们拥有类中成员变量的不同副本,所以假如动态成员函数由类来调用,我们无法知道在函数中访问的是哪一个对象的成员变量。

    要输出动态函数的地址,必须通过对象来获取。

    C++调用非静态的成员函数时,采用的是一种__thiscall 的函数调用方式。采用这种调用方式,编译器在编译的时候,会在调用的函数形参表中增加一个指向调用该成员函数的指针,也就是我们经常说的this指针。调用的形式类似于Base::f1(Base* this, otherparam…),在函数体中,涉及到对象的成员变量或者其他成员函数,都会通过这个this指针来调用,从而达到在成员函数中处理调用对象所对应的数据,而不会错误处理其他对象的数据。可见,虽然我们必须通过对象来调用动态函数,但是其实我们访问的都是同一个成员函数。所以我们采用&Base::f1来获取成员函数地址是没错的,动态函数同样是跟类绑定而不是跟对象绑定的。

    出错的原因是,输出操作符<<没有对void(__thiscall A:: *)()类型重载,编译器将这种类型转换为bool类型,所以输出了1;

    对于静态函数,其调用方式并非__thiscall,<<有对它的重载,因此类的静态函数可以直接用cout输出函数地址。我们可以用printf输出,因为他可以接收任意类型的参数,包括__thiscall类型

cout << p->fn << endl;    // 静态成员函数可以直接获取地址
cout << Base::fn << endl;  // 静态成员函数可以直接获取地址

cout << &Base::f1 << endl;  // 编译器将void(__thiscall A::*)()类型转换为bool类型。 输出 1
printf("Base::f1()地址:%p\n", &Base::f1);
printf("Base::f2()地址:%p\n", &Base::f2);
printf("Base::fn()地址:%p\n", &Base::fn);