目录
一、函数指针
函数存储在内存的代码段(text segment)中,并且它们同样具有地址。如果我们有一个 int test(int a)
函数,那么 test
本身就表示该函数的地址,这一点类似于数组名代表数组的起始地址。
1.1. 函数指针的定义方式
data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);
例如:
int (*fp)(int a); // 这里就定义了一个指向函数(这个函数参数仅仅为一个 int 类型,函数返回值是 int 类型)的指针 fp。
在 C++ 中,获取函数的地址 和 打印函数的地址 需要注意指针的类型。下面我们分别介绍 普通函数指针、静态函数指针、静态成员函数指针 和 非静态成员函数指针 的情况。并用这些指针打印他们的地址。
二、非类成员函数地址的获取
1.1 普通的全局或静态函数的地址
普通的全局或静态函数的地址可以直接打印:
#include <iostream>
void myFunction() {
std::cout << "Hello, world!" << std::endl;
}
int main() {
void (*funcPtr)() = &myFunction;
// 打印函数指针中存放的值(函数的地址)
std::cout << "Function pointer address: " << (void*)funcPtr << std::endl;
return 0;
}
输出示例:
Function pointer address: 0x401550
📌 说明
funcPtr
是普通函数的指针,可以直接转换成void*
进行打印。
关键点解析
myFunction
是一个函数,它存放在代码段(text segment)中,并且具有固定的地址。funcPtr
是一个指向myFunction
的指针,即funcPtr
存储的是myFunction
的地址。(void*)funcPtr
的实际含义:funcPtr
存储的是myFunction
的地址。(void*)funcPtr
进行类型转换,使funcPtr
以void*
的形式打印出来。- 由于
funcPtr
存储的就是myFunction
的地址,所以std::cout << (void*)funcPtr;
打印的是myFunction
的地址。
最终结论
✅ 打印的结果是 myFunction
的地址,而不是 funcPtr
本身的地址。
如果想打印 funcPtr
指针本身的地址(即存放 funcPtr
的内存地址),可以使用:
std::cout << "Address of funcPtr itself: " << &funcPtr << std::endl;
这样才能看到 funcPtr 变量本身存储在内存中的地址。🚀
三、类成员函数地址的获取
1.1 静态成员函数地址获取
静态成员函数和普通函数类似,它们不依赖对象,可以直接获取地址:
#include <iostream>
class A {
public:
static void staticFunc() {
std::cout << "Static function called!" << std::endl;
}
};
int main() {
void (*funcPtr)() = &A::staticFunc;
// 打印静态成员函数的地址
std::cout << "Static function pointer address: " << (void*)funcPtr << std::endl;
return 0;
}
输出:
Static function pointer address: 0x402cf0
📌 说明
- 静态成员函数本质上和普通函数一样,可以直接
void*
转换后打印。
1.2 非静态成员函数地址获取(不好获取)
为什么非静态成员函数指针不能直接打印?
非静态成员函数指针与普通函数指针(包括静态成员函数指针)有所不同,原因主要在于 它们的内存布局和调用机制。
我们可以从以下几个方面来理解:
1. 非静态成员函数的特殊性
-
非静态成员函数 是类的一部分,它们依赖于类的实例,也就是说,它们的调用需要绑定一个对象(或者通过
this
指针)。这与普通的全局或静态函数不同,后者不依赖于对象。 -
由于非静态成员函数指针不仅包含函数的地址,还需要包含与对象相关的附加信息,比如
this
指针,它指向当前对象。因此,非静态成员函数指针包含了更多信息,并不是单纯的代码地址。
2. 内存布局
在内存中,普通函数指针(比如 void (*funcPtr)()
)和非静态成员函数指针的布局是不同的。普通函数指针直接指向代码段(函数的起始位置),而非静态成员函数指针则可能存储:
- 函数的代码地址;
- 其他信息,如与该函数关联的类的实例(通过
this
指针)。
因此,非静态成员函数指针不仅仅是一个普通的地址,它包含更多的内存结构,不能直接用普通的输出方式打印。
3. std::cout
和成员函数指针
std::cout
默认并没有定义如何输出成员函数指针。因为成员函数指针是一个复杂的类型,包含了函数地址以及其他内部信息,而 std::cout
是设计来打印普通指针和基本数据类型的,不能直接理解和处理这种复合类型。
4. 如何正确处理非静态成员函数指针
对于非静态成员函数指针,我们可以通过以下方法来访问和使用它们:
1) 使用 printf
打印函数指针
如果你想打印非静态成员函数指针的地址,可以使用 printf
:
printf("%p", *(void**)&funcPtr);
通过类型转换,你可以将非静态成员函数指针转换为 void*
类型,这样就能够获取其底层地址。
2) 使用 std::cout
打印成员函数指针
成员函数指针不能直接转换为 void*
,但是你可以通过 std::cout
来打印它:
std::cout << funcPtr << std::endl;
某些编译器和平台可能允许直接输出成员函数指针,但这种行为不符合 C++ 标准,可能会导致平台依赖性或未定义行为。
5. 总结
- 非静态成员函数指针是特殊的,包含了对函数代码地址的引用和与对象的关联,因此不能直接像普通函数指针那样直接打印。
std::cout
不支持直接输出成员函数指针,因为它包含了更多的信息,而不仅仅是地址。- 如果需要打印,使用
printf("%p", *(void**)&funcPtr)
可以正确打印函数指针的底层地址。
这些设计和限制源于 C++ 的内存管理方式和面向对象特性,尤其是与对象实例(this
)的绑定。
推荐阅读: