【问题标题】:Why do function pointers all have the same value?为什么函数指针都具有相同的值?
【发布时间】:2013-06-30 03:00:20
【问题描述】:

例如:

using namespace std;
#include <iostream>

void funcOne() {
}

void funcTwo( int x ) {
}

int main() {

  void (*ptrOne)() = funcOne;
  cout << ptrOne << endl;      //prints 1

  void (*ptrTwo)( int x ) = funcTwo;
  cout << ptrTwo << endl;      //prints 1

  int (*ptrMain)() = main;
  cout << ptrMain << endl;     //prints 1

}

有人知道这背后的原因吗?起初我认为这是因为这些函数不存在于内存中,因为我从不调用它们,因此它们永远不会被添加到堆栈中。但即使是指向主函数的指针的值也会打印出 1。

【问题讨论】:

  • 它不适合我。在 MSV2012 上,我得到: 011A1230、011A122B、011A12F3 每次打印。
  • 那些语句应该打印函数指针地址
  • @Borgleader 哦,它为你打印了什么?也许这只是特定于我的编译器或类似的东西。
  • @billz:为什么? cout &lt;&lt; ... 是否处理函数指针类型的参数?
  • @billz 我的打印语句正在打印出作为函数内存地址的指针的值。

标签: c++ function-pointers


【解决方案1】:

函数指针不会隐式转换为void *,这是operator &lt;&lt; 重载的原因。

这是由 C++11 §4.10/2 中的省略指定的:

“指向 cv T 的指针”类型的纯右值,其中 T 是对象类型,可以转换为“指向 cv void 的指针”类型的纯右值。将“指向 cv T 的指针”转换为“指向 cv void 的指针”的结果指向类型 T 的对象所在的存储位置的开始,就好像该对象是类型 T 的最派生对象 (1.8) (即,不是基类子对象)。将空指针值转换为目标类型的空指针值。

函数类型不是对象类型。

此外,您甚至无法使用static_cast 来完成此操作。函数和对象可能存在于完全不同的地址空间(这称为哈佛架构)中,具有不同大小的指针。将函数指针转换为void * 可以也许reinterpret_cast 完成:它是“有条件支持的”(C++11 §5.2.10/8)。这样的void * 应该只用于打印或转换回原来的函数指针类型。

【讨论】:

  • 谢谢!所以问题是函数指针不会转换为 void 指针。这是否意味着所有其他类型的指针在与
  • 实际上,在大声读了几次之后,我完全明白了。非常感谢!
【解决方案2】:

这样使用,不然会转成bool类型。

cout << reinterpret_cast<void*>(ptrOne) << endl;

【讨论】:

  • 为什么要转成bool类型?
  • 它可能会,不会因为它在使用 MSVC2012 时打印正常。
  • 好的,所以我至少知道这是特定于我正在运行的环境的东西。谢谢。
【解决方案3】:

C++ 中的运算符重载增加了各种令人讨厌的复杂性。 (它也可以让你做一些很棒的事情——但有时只是让人头疼。)

正如其他答案中所解释的,C++ 正在对您的函数指针进行一些自动类型强制。如果您只是使用良好的 ol' C 风格 printf,您应该会得到您期望的结果:

#include <cstdio>

// ...

printf("func1: %p\nfunc2: %p\n", funcOne, funcTwo);

【讨论】:

  • 从技术上讲,这可能是一个类型错误,因为%p 需要void *,所以你必须强制转换。在大多数实现中,它不会产生影响,但这不会改变它是类型错误的事实。
  • @DietrichEpp - 这是一个有趣的观点。合同确实声明它需要一个void *,但由于它是一个可变参数函数,所以无论如何都应该丢弃演员表。我认为许多现代编译器实际上会解析格式字符串并为您做一些额外的检查——但这超出了 C 标准可以实际指定的任何内容。如果你提出了一个有趣的观点,我会给你一个 +1,但我不会添加额外的强制转换,如果它遵守 C 标准,编译器应该忽略它。
  • @DietrichEpp - 实际上,根据an answer to another question about function pointers,C 标准甚至不允许您将函数指针转换为void * 类型。 Potatoswatter 的回答似乎是说 C++ 标准中也是如此。
  • 嘿道文,我试过了,它说 funcOne 和 funcTwo 没有在范围内声明。你的意思是把 ptrOne 和 ptrTwo 放在函数参数中吗?
  • @KacyRaye - 我从高中一直学习日语,我想说它实际上比 POSIX 格式字符串更晦涩难懂的选项更容易阅读!
【解决方案4】:

试试这个:

void (*ptrOne)() = funcOne;
cout << reinterpret_cast<void*>(ptrOne) << endl;

void (*ptrTwo)( int x ) = funcTwo;
cout << reinterpret_cast<void*>(ptrTwo) << endl;

int (*ptrMain)() = main;
cout << reinterpret_cast<void*>(ptrMain) << endl;

我认为通常情况下,函数重载规则意味着&lt;&lt;被调用的版本是operator&lt;&lt;(bool),这意味着:

cout << ptrOne << endl;

转化为:

cout << (ptrOne != NULL) << endl;

等同于:

cout << true << endl;

【讨论】:

  • 介意解释一下这是做什么的吗?
  • 你要我解释什么?
  • reinterpret_cast 我以前从未见过/听说过。
  • 它应该包含在任何 C++ 参考中。我讨厌这样说,但这是一个非常基本的问题。 duckduckgo.com/?q=reinterpret_cast
猜你喜欢
  • 1970-01-01
  • 2021-04-17
  • 1970-01-01
  • 2013-05-15
  • 1970-01-01
  • 2020-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多