我想用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将其打印出来来观察函数指针的内容。 但它对成员函数指针不起作用,编译器抱怨非法转换。我知道成员函数指针是一个比简单指针更复杂的结构,但是我们如何观察成员函数指针的内容呢?
答案 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 ++编译器收集一组候选函数,并根据需要使用隐式转换从这些候选函数中选择“最可行”函数。皱纹是匹配规则形成的一个部分顺序,因此多个最佳可行的匹配会导致模糊错误。
按照偏好顺序,标准转换(当然还有用户定义和省略号转换,未详细说明)
int
至float
)最后一个类别包括布尔转换,任何指针类型都可以转换为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用作输入参数))。