在《C语言深度解剖》中的4.7.2一节——函数指针的使用中有一个例子~~~
有代码,有真相~~~
#include <stdio.h>
#include <string.h>
char * fun(char *p1,char *p2)
{
int i=0;
i=strcmp(p1,p2);
if(i==0)
{
return p1;
}
else
return p2;
}
int main()
{
char *(*pf)(char * ,char *);
pf=fun;
printf("%s\n", (*pf)("aa","bb"));
return 0;
}
输出结果:bb
然后书中说道“在pf=fun;这条语句里面,fun和&fun是一样的,因为函数名被编译之后本身就是一个地址(VC6)”。
也就是说如果fun可以看成一个变量,我是说如果,那么就是“&fun”这个内存地址中存放这自己的地址值~~~
以下是我在gcc下编译通过并显示的结果:
#include <stdio.h>
#include <string.h>
char * fun(char *p1,char *p2)
{
int i=0;
i=strcmp(p1,p2);
if(i==0)
{
return p1;
}
else
{
return p2;
}
}
int main()
{
char *(*pf)(char * ,char *);
pf=fun;
printf("%s\n", (*pf)("aa","bb"));
printf("pf:%x ,&pf: %x ,*pf:%x fun:%x &fun:%x *fun:%x \n", pf,&pf,*pf,fun,&fun,*fun);
return 0;
}
结果:
bb
pf:400584 ,&pf: e4b8edb8 ,*pf:400584 fun:400584 &fun:400584 *fun:400584
它(gcc)竟然让*fun通过编译并运行了,它到底做了什么,可以让一个函数变成变量~~~
当然以上结果显示我之前的分析是一致的,但是真相真的是这样么~~~
可能没那么简单~~~
因为我在vs2008里面不是这么个情况的~~~
上vs下面的代码主函数的反汇编代码:
int main()
{
004135A0 push ebp
004135A1 mov ebp,esp
004135A3 sub esp,0CCh
004135A9 push ebx
004135AA push esi
004135AB push edi
004135AC lea edi,[ebp-0CCh]
004135B2 mov ecx,33h
004135B7 mov eax,0CCCCCCCCh
004135BC rep stos dword ptr es:[edi]
char *(*pf)(char *p1,char *p2);
pf=&fun;
004135BE mov dword ptr [pf],offset @ILT+295(_fun) (41112Ch)
printf("%s\n",(*pf)("aa","bb"));
004135C5 mov esi,esp
004135C7 push offset string "bb" (415744h)
004135CC push offset string "aa" (41573Ch)
004135D1 call dword ptr [pf]
004135D4 add esp,8
004135D7 cmp esi,esp
004135D9 call @ILT+320(__RTC_CheckEsp) (411145h)
004135DE mov esi,esp
004135E0 push eax
004135E1 push offset string "%s\n" (415740h)
004135E6 call dword ptr [__imp__printf (4182C4h)]
004135EC add esp,8
004135EF cmp esi,esp
004135F1 call @ILT+320(__RTC_CheckEsp) (411145h)
printf("pf:%x,&pf:%x,*pf:%x,fun:%x,&fun:%x,*fun%x,\n",pf,&pf,*pf,fun,&fun,*fun);
004135F6 mov esi,esp
004135F8 push offset @ILT+295(_fun) (41112Ch)
004135FD push offset @ILT+295(_fun) (41112Ch)
00413602 push offset @ILT+295(_fun) (41112Ch)
00413607 mov eax,dword ptr [pf]
0041360A push eax
0041360B lea ecx,[pf]
0041360E push ecx
0041360F mov edx,dword ptr [pf]
00413612 push edx
00413613 push offset string "pf:%x,&pf:%x,*pf:%x,fun:%x,&fun:"... (415A00h)
00413618 call dword ptr [__imp__printf (4182C4h)]
0041361E add esp,1Ch
00413621 cmp esi,esp
00413623 call @ILT+320(__RTC_CheckEsp) (411145h)
getchar();
00413628 mov esi,esp
0041362A call dword ptr [MSVCR90D_NULL_THUNK_DATA (4182C0h)]
00413630 cmp esi,esp
00413632 call @ILT+320(__RTC_CheckEsp) (411145h)
return 0;
00413637 xor eax,eax
}
00413639 push edx
0041363A mov ecx,ebp
0041363C push eax
0041363D lea edx,[ (413660h)]
00413643 call @ILT+130(@_RTC_CheckStackVars@8) (411087h)
00413648 pop eax
00413649 pop edx
0041364A pop edi
0041364B pop esi
0041364C pop ebx
0041364D add esp,0CCh
00413653 cmp ebp,esp
00413655 call @ILT+320(__RTC_CheckEsp) (411145h)
0041365A mov esp,ebp
0041365C pop ebp
0041365D ret
0041365E mov edi,edi
00413660 db 01h
00413661 db 00h
00413662 db 00h
00413663 db 00h
00413664 db 68h
00413665 db 36h
00413666 db 41h
00413667 db 00h
00413668 db f8h
00413669 db ffh
0041366A db ffh
0041366B db ffh
0041366C db 04h
0041366D db 00h
0041366E db 00h
0041366F db 00h
00413670 db 74h
00413671 db 36h
00413672 db 41h
00413673 db 00h
00413674 db 70h
00413675 db 66h
00413676 db 00h
ps:还没有进如到汇编的level~~~做个标记先~~~
结果如下:
说明:vs监视器和print输出的地址(vs的print和gcc下的print一样)值不一样,估计是个bug吧,print输出时对的,而且vs监视器不认为*fun是一个变量~~~但也还是能够输出~~~
那么按照vs监视器来说fun还真是个变量~~~是char*(char *,char *),也就是函数指针的类型~~~
后记:觉得有些C语言的特性不能单从C语言的语法角度来解释,因为C语言是相对比较低级的语言,和硬件(CPU及体系结构)由很大的相关性,所以如果要深入理解,还是得从更低级的汇编的角度来理解~~~好吧,给自己找个了解汇编找个动力先~~~