如何用cout打印函数指针?

我想用cout打印出一个函数指针,发现它不起作用。 但是,在将函数指针转换为(void *)之后,它起作用,printf与%p也是如此,例如

#include <iostream> using namespace std; int foo() {return 0;} int main() { int (*pf)(); pf = foo; cout << "cout << pf is " << pf << endl; cout << "cout << (void *)pf is " << (void *)pf << endl; printf("printf(\"%%p\", pf) is %p\n", pf); return 0; } 

我用g ++编译,得到这样的结果:

cout << pf是1
cout <<(void *)pf是0x100000b0c
printf(“%p”,pf)是0x100000b0c

那么cout用int(*)()做什么? 我被告知函数指针被视为bool,是真的吗? cout和type(void *)是做什么的?

提前致谢。

编辑:无论如何,我们可以通过将其转换为(void *)并使用cout将其打印出来,观察函数指针的内容。 但它不适用于成员函数指针,编译器抱怨非法转换。 我知道成员函数指针不是简单的指针,而是一个复杂的结构,但我们如何观察成员函数指针的内容呢?

实际上有一个<<运算符的重载,看起来像这样:

 ostream & operator <<( ostream &, const void * ); 

这是你所期望的 – 以hex输出。 函数指针不能有这样的标准库重载,因为它们的types是无限的。 所以指针被转换为另一种types,在这种情况下,这种types似乎是一个布尔 – 我不能不顾一切地记住这个规则。

编辑: C ++标准指定:

4.12布尔转换

1算术,枚举,指针或指向成员types的指针的右值可以转换为booltypes的右值。

这是为函数指针指定的唯一转换。

关于你的编辑,你可以通过unsigned char指针来访问它的内容。 指向成员函数的示例:

 #include <iostream> #include <iomanip> struct foo { virtual void bar(){} }; struct foo2 { }; struct foo3 : foo2, foo { virtual void bar(){} }; int main() { void (foo3::*p)() = &foo::bar; unsigned char const * first = reinterpret_cast<unsigned char *>(&p); unsigned char const * last = reinterpret_cast<unsigned char *>(&p + 1); for (; first != last; ++first) { std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)*first << ' '; } std::cout << std::endl; } 

您可以将函数指针视为该函数机器代码中第一条指令的地址。 任何指针可以被视为一个bool :0是假的,其他的都是真的。 正如你所观察到的,当转换为void *并作为stream插入操作符( << )的参数给出时,地址被打印出来。 (严格看来,将函数指针指向void *是未定义的。)

没有演员,故事有点复杂。 为了匹配重载函数(“重载parsing”),C ++编译器收集一系列候选函数,并从这些候选函数中select“最好可行”函数,必要时使用隐式转换。 皱纹是形成偏序的匹配规则,所以多个最可行的匹配会导致模糊性错误。

按照优先顺序,标准转换(当然还有用户定义和省略号转换,不详细)

  • 完全匹配( 不需要转换)
  • 推广( 例如intfloat
  • 其他转换

最后一个类别包括布尔转换,任何指针types都可以转换为bool :0(或NULL )为false ,其他都为true 。 后者在传递给stream插入操作符时显示为1

为了得到0 ,改变你的初始化

 pf = 0; 

请记住,使用零值常量expression式初始化指针会产生空指针。

如果你想查看它们的值,将指向(void*)指针打印到cout是正确的(TM)。

关于你的具体问题,

我们如何观察一个成员函数指针的内容?

答案是,除了将它们转换为bool来表示它指向某个或者没有,你不能“观察”成员函数指针。 至less不符合标准。 原因是因为标准明确地不允许这样做:

4.12脚注57:

57)与指向对象的指针(从指针到派生指针到基指针)的规则(4.10,第10条)相比,指向成员的指针转换规则(从指针到基本成员到指向派生成员的指针) 。 这种反转对于确保types安全是必要的。 请注意,指向成员的指针不是指向对象的指针或指向函数的指针,这些指针的转换规则不适用于指向成员的指针。 特别是,指向成员的指针不能转换为void *。

例如,这里是示例代码:

 #include <cstdlib> #include <vector> #include <algorithm> #include <string> #include <iostream> using namespace std; class Gizmo { public: void DoTheThing() { return; }; private: int foo_; }; int main() { void(Gizmo::*fn)(void) = &Gizmo::DoTheThing; Gizmo g; (g.*fn)(); // once you have the function pointer, you can call the function this way bool b = fn; // void* v = (void*)fn; // standard explicitly disallows this conversion cout << hex << fn; return 0; } 

我注意到我的debugging器(MSVC9)能够在运行时告诉我成员函数的实际物理地址,所以我知道必须有一些方法来实际得到这个地址。 但我确定它是不符合规定的,不可移植的,可能涉及机器代码。 如果我顺着这条路走下去,我会先从函数指针的地址(例如&fn )开始,然后把它放到void *处,然后从那里开始。 这也需要你知道指针的大小(在不同的平台上是不同的)。

但是我会问,只要你可以将成员函数指针转换为bool值并且评估指针的存在性,为什么在实际的代码中你需要这个地址呢?

推测最后一个问题的答案是“所以我可以确定一个函数指针是否指向与另一个函数指针相同的函数”。 很公平。 您可以比较函数指针是否相等:

 #include <cstdlib> #include <vector> #include <algorithm> #include <string> #include <iostream> using namespace std; class Gizmo { public: void DoTheThing() { return; }; **void DoTheOtherThing() { return; };** private: int foo_; }; int main() { void(Gizmo::*fn)(void) = &Gizmo::DoTheThing; Gizmo g; (g.*fn)(); // once you have the function pointer, you can call the function this way bool b = fn; // void* v = (void*)fn; // standard explicitly disallows this conversion cout << hex << fn; **void(Gizmo::*fnOther)(void) = &Gizmo::DoTheOtherThing; bool same = fnOther == fn; bool sameIsSame = fn == fn;** return 0; } 

在C ++ 11中,可以通过定义operator<<的可变参数模板重载来修改这种行为(不pipe这个build议是否是另一个主题):

 #include<iostream> namespace function_display{ template<class Ret, class... Args> std::ostream& operator <<(std::ostream& os, Ret(*p)(Args...) ){ // star * is optional return os << "funptr " << (void*)p; } } // example code: void fun_void_void(){}; void fun_void_double(double d){}; double fun_double_double(double d){return d;} int main(){ using namespace function_display; // ampersands & are optional std::cout << "1. " << &fun_void_void << std::endl; // prints "1. funptr 0x40cb58" std::cout << "2. " << &fun_void_double << std::endl; // prints "2. funptr 0x40cb5e" std::cout << "3. " << &fun_double_double << std::endl; // prints "3. funptr 0x40cb69" }