首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

高分求运行结果和原因(函数指针)解决办法

2012-03-12 
高分求运行结果和原因(函数指针)#includeiostreamusingnamespacestdintFunA(int,int)voidFunB(int*pA)

高分求运行结果和原因(函数指针)
#include   <iostream>
using   namespace   std;

int   FunA(int   ,   int);
void   FunB(int*   pA);


int   main(int   argc,   char*   argv[])
{
int(*p)(int,int);
p   =   FunA;
int   nA   =   (*p)(1,2);
cout   < <   "nA= "   < <   nA   < <   endl;

typedef   int(*pA)(int,int);
p   =   (pA)FunB;


int   nB   =   (*p)(7,4);
cout   < <   "nB= "   < <   nB   < <   endl;
return   0;
}

int   FunA(int   nA,   int   nB)
{
return   nA   +   nB;
}

void   FunB(int*   pA)
{
*pA++;
}

结果如何,并说出为什么?第一个答对给分!!!!

[解决办法]
看大家争论得这么激烈,我也说上两句:
你的代码,出现问题的根源在于使用了C风格的强制类型转换:
typedef int(*pA)(int,int);
p = (pA)FunB;
我们看一下,这两行代码,很显然函数指针pA就是p的类型。
对于C风格的强制类型转换,编译器不会进行类型检查,按照指定类型给你转换,这时候

编译器很相信你(例如long到int的强制转换,那么你只得到long类型变量的低sizeof

(int)个字节),
那么在这里呢,p现在指向的地址的确是FunB的首地址,也就是说当你通过函数指针p调用

时,执行的是FunB的函数体,这点相信大家都清楚。
那么参数呢,编译器会如何处理,既然你告诉了编译器,参数是两个整型,所以编译器会

根据这个做参数类型检查,然后传递参数。
那么如何传递参数呢?对于这个传递过程是通过实参压栈(函数调用前,编译器给你安排

的代码)、出栈(函数体前面,编译器给你安排插入的代码)来实现的,但是这里有一点

还要提到的就是,不同的实现对于压栈的顺序是不同的,这也就是为什么我们不能依靠参

数的实行顺序类进行计算(例如(i++,i),结果未定义)。所以
int nB = (*p)(7,4);执行时。这就牵涉到7和4作为参数压栈(我的Dev-C++4.9.9.2上,

是4先,7后),然后执行FunB函数体FunB的参数值(即int *pA),恰好它也是占用int大

小的空间,所以在我的实现上得到的是pA==7,
这个地址大家知道肯定是非法的(因为这种小地址内存肯定被系统占用了,你如果用,那

就是访问非法,异常终止)。
然而你会说在我这里能运行,而且能得到结果。
哈哈,我的也是,但是如果你将FunB改为如下
void FunB(int* pA)
{
cout < < "pA = " < < pA < < endl;
cout < < "*pA = " < < *pA < < endl; //这里有问题了!
int b = *pA++;
cout < < "b = " < < b < < endl; //这里导致上一行有问题了!
}
你在运行看看,那就是访问违例,异常终止!
为什么呢:
首先看一下原先的代码,实际上编译器都会对函数做一些优化,最典型的就是RV,即返回

值优化,所以你的调用实际上:
int nB; //注意未初始化,为什么?嗬嗬局部变量,所以。。
int _temp_nB_002; //返回值优化通过它保存返回值
{//加上或括号是进入一个局部作用域
//压栈,出栈
int *_temp_pA_001 = pA, pA+=1, *_temp_pA_001;
}
nB = _temp_nB_002; //注意!
当然我这里是表示一个执行过程,实际上复杂得多。
再看看,实际上nB没有在内部被赋值(因为没有return语句,所以返回值优化无法给

_temp_nB_002正确的值)
所以你得到的nB值实际上是随即的(没有初始化)。
那为什么没有访问违例,异常终止?
问得很好,那是因为,编译器发现int *_temp_pA_001 = pA, pA+=1, *_temp_pA_001;根

本没有意义,因为变量定义了后面没有使用,大多的编译器会对这种情况进行优化,所以

经过优化的代码实际上就没有了这些操作。
但是当我们加上
cout < < "*pA = " < < *pA < < endl;
这时候,的确是要对pA指向的内存进行访问了,所以这时候问题就出现了!
同样的道理:

cout < < "b = " < < b < < endl; //这里导致上一行有问题了!
这一行如果没有,那么
int b = *pA++;
也是没问题的,因为编译器给它优化掉了!

热点排行