c++ - 如何查看方法指针的内容?

标签 c++ casting function-pointers pointer-to-member reinterpret-cast

typedef int (D::*fptr)(void);
fptr bfunc;
bfunc=&D::Bfunc;
cout<<(reinterpret_cast<unsigned long long>(bfunc)&0xffffffff00000000)<<endl;

完整代码可在:https://ideone.com/wRVyTu

我尝试使用reinterpret_cast,但编译器抛出错误

prog.cpp: In function 'int main()': prog.cpp:49:51: error: invalid cast from type 'fptr {aka int (D::*)()}' to type 'long long unsigned int'   cout<<(reinterpret_cast<unsigned long long>(bfunc)&0xffffffff00000000)<<endl;

我的问题是:

  1. 为什么reinterpret_cast不适合这种场合?

  2. 还有其他方法可以看到方法指针的内容吗?

最佳答案

使用 clang++ 编译代码的稍微修改版本(删除所有 cout 以避免获得数千行...),我们得到 main 的内容:

define i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  %bfunc = alloca { i64, i64 }, align 8
  %dfunc = alloca { i64, i64 }, align 8
  store i32 0, i32* %retval, align 4
  store { i64, i64 } { i64 1, i64 16 }, { i64, i64 }* %bfunc, align 8
  store { i64, i64 } { i64 9, i64 0 }, { i64, i64 }* %dfunc, align 8
  ret i32 0
}

请注意,bfuncdfunc 是两个 64 位整数值。如果我针对 32 位 x86 进行编译,则它是两个 i32(因此是 32 位整数值)。

所以,如果我们让 main 看起来像这样:

int main() {
    // your code goes here

    typedef int (D::*fptr)(void);

    fptr bfunc;
    fptr dfunc;

    bfunc=&D::Bfunc;
    dfunc=&D::Dfunc;

    D d;
    (d.*bfunc)();

    return 0;
}

生成的代码如下所示:

; Function Attrs: norecurse uwtable
define i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  %bfunc = alloca { i64, i64 }, align 8
  %dfunc = alloca { i64, i64 }, align 8
  %d = alloca %class.D, align 8
  store i32 0, i32* %retval, align 4
  store { i64, i64 } { i64 1, i64 16 }, { i64, i64 }* %bfunc, align 8
  store { i64, i64 } { i64 9, i64 0 }, { i64, i64 }* %dfunc, align 8
  call void @_ZN1DC2Ev(%class.D* %d) #3
  %0 = load { i64, i64 }, { i64, i64 }* %bfunc, align 8
  %memptr.adj = extractvalue { i64, i64 } %0, 1
  %1 = bitcast %class.D* %d to i8*
  %2 = getelementptr inbounds i8, i8* %1, i64 %memptr.adj
  %this.adjusted = bitcast i8* %2 to %class.D*
  %memptr.ptr = extractvalue { i64, i64 } %0, 0
  %3 = and i64 %memptr.ptr, 1
  %memptr.isvirtual = icmp ne i64 %3, 0
  br i1 %memptr.isvirtual, label %memptr.virtual, label %memptr.nonvirtual

memptr.virtual:                                   ; preds = %entry
  %4 = bitcast %class.D* %this.adjusted to i8**
  %vtable = load i8*, i8** %4, align 8
  %5 = sub i64 %memptr.ptr, 1
  %6 = getelementptr i8, i8* %vtable, i64 %5
  %7 = bitcast i8* %6 to i32 (%class.D*)**
  %memptr.virtualfn = load i32 (%class.D*)*, i32 (%class.D*)** %7, align 8
  br label %memptr.end

memptr.nonvirtual:                                ; preds = %entry
  %memptr.nonvirtualfn = inttoptr i64 %memptr.ptr to i32 (%class.D*)*
  br label %memptr.end

memptr.end:                                       ; preds = %memptr.nonvirtual, %memptr.virtual
  %8 = phi i32 (%class.D*)* [ %memptr.virtualfn, %memptr.virtual ], [ %memptr.nonvirtualfn, %memptr.nonvirtual ]
  %call = call i32 %8(%class.D* %this.adjusted)
  ret i32 0
}

这并不完全是微不足道的,但本质上是:

  %memptr.adj = Read adjustment from bfunc[1]
  %2 = %d[%memptr.adj]
  cast %2 to D*
  %memptr.ptr = bfunc[0]
  if (%memptr.ptr & 1) goto is_virtual else goto is_non_virtual

is_virtual:
  %memptr.virtual=vtable[%memptr.ptr-1]
  goto common

is_non_virtual:
  %memptr.non_virtual = %memptr.ptr

common:
   if we came from 
      is_non_virtual: %8 = %memptr.non_virtual
      is_virtual: %8 = %memptr.virutal
   call %8

我跳过了一些类型转换和其他内容以使其更简单。

注意这并不是说“这就是它始终实现的方式。这是编译器可以执行的操作的一个示例。不同的编译器执行此操作的方式略有不同。但是如果该函数可能或可能不是虚拟的,编译器首先必须弄清楚哪个。[在上面的例子中,我相当确定我们可以打开优化并获得更好的代码,但它可能只是弄清楚到底发生了什么并删除所有代码的一部分,这对于理解它是如何工作的毫无意义]

关于c++ - 如何查看方法指针的内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34495432/

相关文章:

c# - 使用延迟加载的 NHibernate ObjectProxy 转换

c# - 将一个对象转换为另一种类型

c++ - QObject::connect 中 Func2 类型的参数?

c++ - 范围解析运算符和常量

c++ - 使用 for_each 标准或 boost 工具优化循环

c++ - Armadillo C++矩阵线程安全吗

c++ - 'unsigned long int' 和 'unsigned long long int' 赋值问题

.net - Entity Framework : ObjectSet and its (generics) variance

c++ - 如何将函数指针作为参数传递给重载的成员函数

C - 可变参数宏,它扩展为对每个参数的一组宏调用