rainInSunny 2024-09-08 10:38 采纳率: 0%
浏览 3

有关于C++函数地址疑问

#include <iostream>
#include <stdio.h>

int func_0(int a, int b)
{
    return a + b;
}

class A
{
public:
    A() {}
    ~A() {}

    void func_1()
    {
        std::cout << "func_1 in A!" << std::endl;
    }

    virtual void func_2()
    {
        std::cout << "func_2 in A!" << std::endl;
    }

    static void func_3()
    {
        std::cout << "func_3 in A!" << std::endl;
    }
};

class B : public A
{
public:
    B() {}
    ~B() {}

    void func_1()
    {
        std::cout << "func_1 in B!" << std::endl;
    }

    virtual void func_2()
    {
        std::cout << "func_2 in B!" << std::endl;
    }

    static void func_3()
    {
        std::cout << "func_3 in B!" << std::endl;
    }
};

template<typename addressType, typename funcType>
addressType union_cast(funcType func_ptr)        // 获取类内成员函数的函数地址
{
    union
    {
        funcType f;
        addressType d;
    }u;
    u.f = func_ptr;
    return u.d;
}

#define asm_cast(var, addr)        \
{                                \
    __asm                        \
    {                            \
        mov var, offset addr    \
    }                            \
}

int main()
{
    A *pA = new B;
    pA->func_2();
    typedef int(*func_0_pointer)(int a, int b);
    func_0_pointer pFunc_0 = func_0;

    std::cout << "func_0: " << pFunc_0 << std::endl;

    typedef void(A::*func_1_pointer)();
    typedef void(A::*func_2_pointer)();
    typedef void(*func_3_pointer)();
    func_1_pointer pFunc_1 = &A::func_1;
    func_2_pointer pFunc_2 = &A::func_2;
    func_3_pointer pFunc_3 = &A::func_3;

    std::cout << "A::func_1: " << pFunc_1 << std::endl;
    std::cout << "A::func_2: " << pFunc_2 << std::endl;
    std::cout << "A::func_3: " << pFunc_3 << std::endl;
    
    void *ptr_1 = union_cast<void *>(&A::func_1);
    void *ptr_2 = union_cast<void *>(&A::func_2);
    void *ptr_3 = nullptr;
    void *ptr_4 = nullptr;
    asm_cast(ptr_3, A::func_1);
    asm_cast(ptr_4, A::func_2);

    std::cout << "A::func_1: " << ptr_1 << std::endl;
    std::cout << "A::func_2: " << ptr_2 << std::endl;
    std::cout << "A::func_1: " << ptr_3 << std::endl;
    std::cout << "A::func_2: " << ptr_4 << std::endl;
    printf("A::func_2: %p", &A::func_2);

}

环境为VS2017,输出结果如下:

img

有如下问题:

  1. 为什么A::func_2的地址通过union和汇编方式输出有差异。

  2. 为什么A::func_2是虚函数,但是输出的不是偏移量。

望解答~

  • 写回答

1条回答 默认 最新

  • 一轮明月照丘壑 2024-09-08 10:39
    关注

    以下回复参考:皆我百晓生券券喵儿等免费微信小程序作答:

    首先,我们需要了解的是函数地址与虚函数在C++中的概念。在C++中,函数的地址通常是一个固定的值,它指向该函数在内存中的位置。而虚函数则是面向对象编程中的一个重要概念,主要用于实现多态。虚函数在基类和派生类中可以有不同的实现,但是它们指向的都是同一个地址,这个地址就是虚函数表(vtable)中的一个条目。这意味着当我们访问一个对象的虚函数时,实际上是调用的是vtable中对应函数指针所指向的函数。下面我会一一回答你的问题。

    为什么A::func_2的地址通过union和汇编方式输出有差异?

    这是因为union和汇编语言获取函数地址的方式不同。在union中,我们通过将函数指针赋值给数据指针来获取函数的地址。这种方式获取的地址是函数在内存中的绝对地址。而汇编语言获取的函数地址则是相对于某个基址的偏移量。这就是为什么通过这两种方式获取的函数地址会有差异的原因。此外,由于编译器优化、链接器处理等因素也可能导致这两种方式获取的地址不同。

    为什么A::func_2是虚函数,但是输出的不是偏移量?

    在C++中,虚函数的地址实际上是存储在vtable中的条目。当你调用一个对象的虚函数时,实际上是通过vtable查找相应的函数指针来调用函数的。因此,输出的函数地址并不是简单的偏移量,而是一个具体的vtable中的条目地址。在你的例子中,即使func_2是一个虚函数,但它的地址依然是确定的,因为虚函数的实现(在派生类中)在内存中有一个固定的位置。这个地址指向的是vtable中的条目,而不是一个偏移量。因此,无论你是通过union还是汇编语言获取的地址,都应该是一个具体的内存地址。至于为什么输出的地址看起来不像偏移量,那是因为编译器和链接器在处理虚函数时会对这些地址进行特定的处理和管理,我们看到的地址实际上是编译器生成的用于管理vtable的结构中的一部分。在实际应用中,我们通常不会直接操作这些地址,而是通过调用虚函数来调用对应的实现。

    总的来说,理解C++中的函数地址和虚函数的工作原理对于理解这些问题很有帮助。希望这个答案能帮助你理解这些问题。

    评论

报告相同问题?

问题事件

  • 创建了问题 9月8日