类本身的函数如何与其对象函数的地址具有不同的地址?

How the function of the class itself has a different address from its objects' functions' addresses?

提问人:Mohamed Mostafa 提问时间:7/20/2023 最后编辑:Mohamed Mostafa 更新时间:7/20/2023 访问量:123

问:

我知道在类中,每个对象的变量在内存中都会有不同的地址。但是,成员函数的地址在对象之间是通用的,因此在内存中只加载一个具有通用地址的函数。

问题是我试图打印类本身的某个成员函数的地址,我希望它与所有对象的成员函数的地址相同。但是我发现它和他们不一样!!这对我来说很奇怪。

#include<iostream>
using namespace std;
    
class LuckyNum {
    public:
        void PrintAddress() {
            printf("\tFunction address :%p\n", &LuckyNum::PrintAddress);
        }
};
    
int main() {
    LuckyNum r1;
    cout << "r1:\n";
    r1.PrintAddress();

    LuckyNum r2;
    cout << "r2:\n";
    r2.PrintAddress();

    cout << "LuckyNum Class:\n";
    printf("\tFunction address :%p\n", &LuckyNum::PrintAddress);

    return 0;
}

/*Output!! :
r1:
    Function address :0000007acdbffda0
r2:
    Function address :0000007acdbffda0
LuckyNum Class:
    Function address :0000007acdbffde0 => HOW!! it should equal the above two addresses, shouldn't it?
*/
C++ 成员函数

评论

5赞 Peter 7/20/2023
打印类的(非静态)成员的地址 using 给出未定义的行为。粗略地说,成员的地址类似于相对于类实例的偏移量,而不是内存中的地址。%p
2赞 user207421 7/20/2023
我不理解“类本身的功能”、“其对象的函数地址”或“所有对象的成员函数地址”。请解释一下这个毫无意义的术语。
2赞 JaMiT 7/20/2023
我不确定我是否理解这里要问的问题,但我认为您可以从输出中删除“数据地址”,而不会对您的问题产生负面影响。消除干扰通常有助于读者理解问题。
1赞 Davis Herring 7/20/2023
看看这种行为是如何从 ABI 中产生的,这似乎是一个有趣的练习(尽管它当然在语言级别上是未定义的)。不过,这是什么平台?对于 Itanium 或 MS x64 ABI 来说,这似乎不太合适......
0赞 n. m. could be an AI 7/20/2023
@DavisHerring 对我来说,看起来像 Windows 上的 gcc 或 clang。发生的情况是,指向成员的指针大小为 16 个字节,ABI 认为该字节太大,无法按值传递。因此,它的地址被传递给 printf,并愉快地用 %p 打印。

答:

3赞 chrysante 7/20/2023 #1

函数调用

printf("\tFunction address :%p\n", &LuckyNum::PrintAddress);

调用未定义的行为。 不是类型安全的,也不是指针。它是指向成员的指针,这是非常不同的东西,它的表示形式是实现定义的。 请参阅以下程序:printf&LuckyNum::PrintAddress

#include <iostream>
#include <vector>
#include <cstring>

class LuckyNum {
public:
    std::vector<char> getAddress() {
        auto const address = &LuckyNum::getAddress;
        std::vector<char> result(sizeof(address));
        std::memcpy(result.data(), &address, sizeof(address));
        return result;
    }
};
 
int main() {
    LuckyNum r;
    auto address = r.getAddress();
 
    auto const address2 = &LuckyNum::getAddress;
    if (address.size() != sizeof(address2)) {
        std::cout << "Fail\n";
        return -1;
    }
    int result = std::memcmp(address.data(), &address2, address.size());
    if (result != 0) {
        std::cout << "Fail\n";
        return -1;
    }
    std::cout << "Success\n";
    return 0;
}

该函数将指向成员的指针存储在字节向量中并返回它。 然后通过函数获取一个指向成员的指针,并通过 直接获取另一个指针。然后它逐字节比较,如果你运行程序(至少使用 gcc),你会发现指向 member 的两个指针确实是相等的。getAddress()&LuckyNum::getAddressmaingetAddress()&LuckyNum::getAddress