如何用cout打印函数指针?

时间:2010-01-14 14:22:26

标签: c++

我想用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&lt;&lt; pf是1
  cout&lt;&lt; (void *)pf是0x100000b0c
  printf(“%p”,pf)为0x100000b0c

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

提前致谢。

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

7 个答案:

答案 0 :(得分:41)

实际上&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;运算符看起来像:

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

符合您的预期 - 以十六进制输出。函数指针不能有这样的标准库重载,因为它们是无数种类型的。所以指针转换为另一种类型,在这种情况下似乎是一个布尔 - 我不能随便记住这个规则。

编辑: C ++标准指定:

  

4.12布尔转换

     

1算术的右值,   枚举,指针或指针   成员类型可以转换为   bool类型的右值。

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

答案 1 :(得分:11)

关于您的编辑,您可以通过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;
}

答案 2 :(得分:6)

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

没有演员,故事有点复杂。为了匹配重载函数(“重载决策”),C ++编译器收集一组候选函数,并根据需要使用隐式转换从这些候选函数中选择“最可行”函数。皱纹是匹配规则形成的一个部分顺序,因此多个最佳可行的匹配会导致模糊错误。

按照偏好顺序,标准转换(当然还有用户定义和省略号转换,未详细说明)

  • 完全匹配(,无需转换)
  • 促销(例如intfloat
  • 其他转化

最后一个类别包括布尔转换,任何指针类型都可以转换为bool:0(或NULL)是false,其他所有内容都是true。后者在传递给流插入运算符时显示为1

要获得0,请将初始化更改为

pf = 0;

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

答案 3 :(得分:2)

如果你想看到它们的价值,那么在{C}中投射指向(void*)以将它们打印到cout的指针是正确的(TM)。

答案 4 :(得分:1)

关于您的具体问题,

  

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

答案是,除了将它们转换为bool以表示它指向某个东西或它没有,你不能'观察'成员函数指针。至少不是以合规的方式。原因是因为标准明确禁止这样做:

4.12脚注57:

  

57)转换规则   指向成员的指针(从指针指向   指向成员的指针的成员   派生的)与倒置相比   指向对象的指针(来自   指向派生到指针的指针)   (4.10,第10条)。这种反转是   必须确保类型安全。注意   指向成员的指针不是   指向对象的指针或指向的指针   功能和转换规则   这些指针不适用于   指向成员的指针。 特别是,a   指向成员的指针无法转换   无效*。

例如,这是示例代码:

#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;
}

我注意到我的调试器(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;
}

答案 5 :(得分:1)

在C ++ 11中,可以通过定义operator<<的可变参数模板重载来修改此行为(无论是否可推荐是另一个主题):

#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"
}

答案 6 :(得分:0)

也许(有一次我在函数的地址上保持相交) 决定之一)))

#include <iostream>     
#include <stdlib.h>

void alexf();

int main()
{ 
    int int_output;

    printf("printf(\"%%p\", pf) is %p\n", alexf);

    asm( "movl %[input], %%eax\n"                
         "movl %%eax, %[output]\n" 
         : [output] "+m" (int_output)
         : [input] "r" (&alexf)
         : "eax", "ebx"
    );

        std::cout<<"" <<std::hex<<int_output <<""<<std::endl;
    return 0;
}

void  alexf() {  }

使用约束(&alexf)将指针传递给函数&或使用r传递其他指针。让gcc将register用作输入参数))。