有关函数,函数指针的问题。30分!还可以加分。
#include <stdio.h>
#include <stdlib.h>
typedef void (*FUN)();
void Myfunction()
{
static int num(0);
printf("we have come to this function!%d\n",num++);
}
int main()
{
//////////////////////////////////////////////////////////////////////////
//为什么上面两种都可以呢?
FUN fPtr1 = Myfunction;//Myfunction的值写入。
fPtr1();
(*fPtr1)();
FUN fPtr2 = &Myfunction;//Myfunction的地址写入。
fPtr2();
(*fPtr2)();
//////////////////////////////////////////////////////////////////////////
//为什么第二种就不行了呢?
FUN fPtr4;
int *p = (int*)&Myfunction;
int *pMf = (int*)&p;
int *f = (int*)&fPtr4;
*f = *pMf;//将Myfunction的地址写入到fPtr4中。
fPtr4();//调用成功
(*fPtr4)();//调用成功
FUN fPtr3;
int* nPtr = (int*)&Myfunction;
int* nPtrFptr3 = (int*)&fPtr3;
*nPtrFptr3 = *nPtr;//将Myfunction的值写入到fPtr3中
(*fPtr3)();//调用失败
fPtr3();//调用失败
//////////////////////////////////////////////////////////////////////////
//last question:函数的调用过程到底是什么样的呢?函数地址扮演着什么角色呢?
//Myfunction这个值是什么呢?有类型吗?
//希望能够得到逐一的解答。
return EXIT_SUCCESS;
}
编译器vs2005.
可以另外加分。
[解决办法]
FUN fPtr3;
int* nPtr = (int*)&Myfunction;
int* nPtrFptr3 = (int*)&fPtr3;
*nPtrFptr3 = *nPtr;//将Myfunction的值写入到fPtr3中
(*fPtr3)();//调用失败
fPtr3();//调用失败
=======================================
这两句提出来:
int* nPtr = (int*)&Myfunction;//假设函数的地址是:0x00401005
*nPtrFptr3 = *nPtr;//这句的意思是到0x00401005的地方取个整数到*nPtrFptr3中,这已经不是函数的地址了.
*nPtrFptr3 = (int)nPtr;//改为这样就对了.
函数的地址就是一个值,当然,在编译时是有类型的值.无论你怎么变,不能把它本身的值变了.
[解决办法]
首先,对于:
//为什么上面两种都可以呢?
FUN fPtr1 = Myfunction;//Myfunction的值写入。
fPtr1();
(*fPtr1)();
FUN fPtr2 = &Myfunction;//Myfunction的地址写入。
fPtr2();
(*fPtr2)();
是因为,通常情况下,一个对象名作为初始值时代表值传递,而对象名加&解析符代表地址传递,但对于函数名而言是一个例外,当以函数名为相对应类型的函数指针赋值(或作为参数传递)的时候,编译器把"Myfunction"与"&Myfunction"视为等同语句 -都是获得函数地址!(见《C++ Common Knowledge》条款14-函数指针page37》)
其次对于你的下述代码,请允许我为你注释:
FUN fPtr4 = NULL;
int *p = (int*)&Myfunction;//函数地址赋值与整型指针p,可以认为p就是函数指针(当然已被强制转型!);
int *pMf = (int*)&p;//令pMf指针指向p;(这是一个所谓的"指针的指针"!)
int *f = (int*)&fPtr4;//令f指向函数指针变量fPtr4; (也是一个所谓的"指针的指针"!)
*f = *pMf;//pMf对f解引用赋值;
//如上pMf与f解引用赋值的结果,使其等同于语句: fPtr4 = p;//fPtr4获取到正确的函数指针!
fPtr4();//调用成功 合法的调用......
(*fPtr4)();//调用成功
然而后面的情况不佳,请允许我继续为你注释你的程序:
FUN fPtr3 = NULL;
int* nPtr = (int*)&Myfunction;//取函数地址在nPtr,等同于nPtr是函数指针!
int* nPtrFptr3 = (int*)&fPtr3; //令nPtrFptr3指向fPtr3(也即指针的指针);
*nPtrFptr3 = *nPtr;//!!!!
/*
如上的双解引用,等同于fPtr3 = *nPtr;
要注意的是:fPtr3得到的不是函数指针nPtr,而是该指针nPtr的非法解引用-指向执行指令代码的地址!
*/
(*fPtr3)();//调用失败,非法的调用!
fPtr3();//调用失败
关于最后一个问题:函数的调用过程到底是什么样的呢?函数地址扮演着什么角色呢?
答:基本上就编译器底层而言,所有的函数调用(包括类成员函数),除内联函数外,都是以函数指针调用到函数代码实体!
例如一个非成员函数void show(void)是这样调用:
(*_VoidShowVoid)();//调用!
一个名为_fish对象(类实例)的成员函数调用_fish.memberfun()是这样:
(*_voidFishMemberfuncvoid)(&_fish);//其中_fish即是该对象的this指针!
上述函数指针名视不同的编译器的mangling策略不同而大同小异!可见函数指针就是调用函数的handle!
对于"Myfunction"这个函数名,我可以认为它就是函数指针,而函数指针肯定是有类型的,因为一个没有类型的东西,编译器将无所适,不确定其sizeof,这根本就无法寻址。而函数指针的类型就是函数的原型!例如上述Fun,就是一个所有类似:
void Myfunction(void);//无返回值、无参数的函数;
函数原型的typedef!
还有一个问题是,上述程序用了大量的C风格的强制类型转换为int型指针,但在最后又都隐式的转换成了Fun型函数指针,这在本程序而言不是一个问题,但这样的代码不够健壮,而且平台移植性也非常的糟!
[解决办法]
学习
[解决办法]
在debug模式下,你可以看到 两个函数指针的值不是一样的.
FUN fPtr1 = Myfunction;//Myfunction的值写入。
fPtr1();
(*fPtr1)();
FUN fPtr2 = &Myfunction;//Myfunction的地址写入。
fPtr2();
(*fPtr2)();
你可以使用relase试试,不过既然强制转换的代码在debug下无法运行,那就说明你的强制转换不对.
不应该这样使用.
[解决办法]
对于函数名语言是有特殊规定的,&MyFunction和MyFunction都表示他首址
注意:这个特殊规定只对函数名有效,当你使用int*做中转时,&算子会使得指针增加一级,上面的这个规则就无效了
所以没有啥为什么,就是乌龟的屁股--规(龟)定(腚)
[解决办法]
致huangyimin朋友:
你真的认为判断一个标识符或其它什么东西是不是类型,是以能不能进行sizeof运算作为依据的吗?
你说:"函数原型的sizeof是多少呢?好像sizeof(Myfunction)不行哈,编译不过。",下列代码可以告诉你:
typedef void (*FUN)();
int main()
{
cout << "这个函数指针类型的sizeof运算是: " << sizeof(FUN) << endl;
_PAUSE;
return 0;
}
执行上述程序,其输出算是对你的回答!(它说明在我的32位windows平台上函数指针是4byte)
其次请再看如下程序:
typedef void (*FUN)();
typedef int (*FUNC)(const double& _d);//声明了一个新类型的别名!
void Myfunction()
{
static int num(0);
printf("we have come to this function!%d\n",num++);
}
int main()
{
//////////////////////////////////////////////////////////////////////////
FUN fPtr1 = Myfunction;//Myfunction的值写入。
fPtr1();
/*
如下语句生成错误!
类似BC6.0:"E2034 Cannot convert 'void (*)()' to 'int (*)(const double &)'"
类似VC编译器:
Error C2440:
“conversion”: 无法从“type1”转换为“type2”编译器无法从“type1”转换为“type2”。
*/
FUNC fPtr2 = Myfunction;
_PAUSE;
return 0;
}
上述示例说明,编译器是依靠类型糸统,在识别从Myfunction到fPtr2的赋值转换许可!关于函数指针及函数指针的类型,众多的C++书籍均有祥解(比如《C++ Primer》第7.9.1章节),在此不再论述。