您的位置:首页 > 编程语言 > C语言/C++

C/C++ 与函数有关的typedef用法

2015-05-05 22:12 435 查看
今天搞了一天的仓储管理,身心憔悴啊。

晚上师弟过来问linux c编程关于typedef的用法,typedef怎么说呢,仔细想觉得水很深,但是常见的
typedef
也就几种类型,现在总结下。为了更好的体验,我用
vc6.0
写了一个程序,如下:

#include <stdio.h>
typedef int (*func)(int ,int);   //变量类型 func,为函数指针类型
typedef int (another)(int,int);  //变量类型? another,函数名变量?,比较疑惑
/*int another(int,int);
刚开始搞不懂第二个和函数指针的区别,最后发现把typedef去掉,不就是个类似函数声明的东西嘛。。。

从我现在的阅历来看,找不到`typedef int (another)(int,int);`的用处,因为你后期调用的时候还

不得`another * param`嘛,这不就是函数指针嘛。。
*/
int sum(int a,int b)
{
return a+b;
}

int main()
{
int result;
func p;
another *test;     //其实就是func test.
//  another test ;
//不能通过编译,那定义了有毛线作用啊,就为了引用指针为毛不直接 func test呢。
//所以上文我才说这种typedef是毫无意义的,希望有大牛看到这里打脸。
result=sum(1,2);
printf ("sum=%d\n",result);

p =(func)sum;
result=p(1,2);
printf ("sum=%d\n",result);

p =(func)sum;
result=(*p)(1,2);
printf ("sum=%d\n",result);

p =(func)∑
result=p(1,2);
printf ("sum=%d\n",result);

p =∑
result=p(1,2);
printf ("sum=%d\n",result);

p =sum;
result=p(1,2);
printf ("sum=%d\n",result);

*test =sum;
result=test(1,2);
printf ("sum=%d\n",result);
return 0;
}

output: sum=3 (repeate-7times)


首先来复习一下基础知识

1. 函数指针

int max(int a,int b){return max_number of a or b} //伪代码
int(*p)(int,int);     //声明一个函数指针
p =max
usage: p(1,2) (*p)(1,2)


结合反汇编来看一下吧:

1:    #include <stdio.h>
2:    typedef int (*func)(int ,int);
3:    typedef int (another)(int,int);
4:    //int another(int,int);
5:
6:    int sum(int a,int b)
7:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,40h  //default stack space,no local parameter exist.
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-40h]
0040102C   mov         ecx,10h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
8:        return a+b;
00401038   mov         eax,dword ptr [ebp+8]         //a
0040103B   add         eax,dword ptr [ebp+0Ch]       //b
9:    }
0040103E   pop         edi
0040103F   pop         esi
00401040   pop         ebx
00401041   mov         esp,ebp
00401043   pop         ebp
00401044   ret

11:   int main()
12:   {
0040D740   push        ebp
0040D741   mov         ebp,esp
0040D743   sub         esp,4Ch
0040D746   push        ebx
0040D747   push        esi
0040D748   push        edi
0040D749   lea         edi,[ebp-4Ch]          //cH=4bytes*3
0040D74C   mov         ecx,13h
0040D751   mov         eax,0CCCCCCCCh
0040D756   rep stos    dword ptr [edi]
13:       int result;
14:       func p;
15:       another *test;
16:       result=sum(1,2);
0040D758   push        2
0040D75A   push        1
0040D75C   call        @ILT+0(_sum) (00401005)
0040D761   add         esp,8
0040D764   mov         dword ptr [ebp-4],eax
17:       printf ("sum=%d\n",result);
0040D767   mov         eax,dword ptr [ebp-4]
0040D76A   push        eax
0040D76B   push        offset string "sum=%d\n" (0042201c)
0040D770   call        printf (004010b0)
0040D775   add         esp,8
18:
19:       p =(func)sum;
0040D778   mov         dword ptr [ebp-8],offset @ILT+0(_sum) (00401005)
20:       result=p(1,2);
0040D77F   mov         esi,esp
0040D781   push        2
0040D783   push        1
0040D785   call        dword ptr [ebp-8]
0040D788   add         esp,8
0040D78B   cmp         esi,esp
0040D78D   call        __chkesp (00401130)
0040D792   mov         dword ptr [ebp-4],eax
21:       printf ("sum=%d\n",result);
0040D795   mov         ecx,dword ptr [ebp-4]
0040D798   push        ecx
0040D799   push        offset string "sum=%d\n" (0042201c)
0040D79E   call        printf (004010b0)
0040D7A3   add         esp,8
22:
23:       p =(func)sum;
0040D7A6   mov         dword ptr [ebp-8],offset @ILT+0(_sum) (00401005)
24:       result=(*p)(1,2);
0040D7AD   mov         esi,esp
0040D7AF   push        2
0040D7B1   push        1
0040D7B3   call        dword ptr [ebp-8]   //可以看到,p(1,2) =(*p)(1,2),都是call [ebp-8]
0040D7B6   add         esp,8
0040D7B9   cmp         esi,esp
0040D7BB   call        __chkesp (00401130)
0040D7C0   mov         dword ptr [ebp-4],eax
25:       printf ("sum=%d\n",result);
0040D7C3   mov         edx,dword ptr [ebp-4]
0040D7C6   push        edx
0040D7C7   push        offset string "sum=%d\n" (0042201c)
0040D7CC   call        printf (004010b0)
0040D7D1   add         esp,8
26:
27:       p =(func)∑
0040D7D4   mov         dword ptr [ebp-8],offset @ILT+0(_sum) (00401005) //可以看到编译过程中sum=&sum
28:       result=p(1,2);
0040D7DB   mov         esi,esp
0040D7DD   push        2
0040D7DF   push        1
0040D7E1   call        dword ptr [ebp-8]
0040D7E4   add         esp,8
0040D7E7   cmp         esi,esp
0040D7E9   call        __chkesp (00401130)
0040D7EE   mov         dword ptr [ebp-4],eax
29:       printf ("sum=%d\n",result);
0040D7F1   mov         eax,dword ptr [ebp-4]
0040D7F4   push        eax
0040D7F5   push        offset string "sum=%d\n" (0042201c)
0040D7FA   call        printf (004010b0)
0040D7FF   add         esp,8
30:
31:       p =∑         //可以看到,没有对&sum强转,也变成了(func)∑
0040D802   mov         dword ptr [ebp-8],offset @ILT+0(_sum) (00401005)
32:       result=p(1,2);
0040D809   mov         esi,esp
0040D80B   push        2
0040D80D   push        1
0040D80F   call        dword ptr [ebp-8]
0040D812   add         esp,8
0040D815   cmp         esi,esp
0040D817   call        __chkesp (00401130)
0040D81C   mov         dword ptr [ebp-4],eax
33:       printf ("sum=%d\n",result);
0040D81F   mov         ecx,dword ptr [ebp-4]
0040D822   push        ecx
0040D823   push        offset string "sum=%d\n" (0042201c)
0040D828   call        printf (004010b0)
0040D82D   add         esp,8
34:
35:       p =sum;
0040D830   mov         dword ptr [ebp-8],offset @ILT+0(_sum) (00401005)
36:       result=p(1,2);
0040D837   mov         esi,esp
0040D839   push        2
0040D83B   push        1
0040D83D   call        dword ptr [ebp-8]
0040D840   add         esp,8
0040D843   cmp         esi,esp
0040D845   call        __chkesp (00401130)
0040D84A   mov         dword ptr [ebp-4],eax
37:       printf ("sum=%d\n",result);
0040D84D   mov         edx,dword ptr [ebp-4]
0040D850   push        edx
0040D851   push        offset string "sum=%d\n" (0042201c)
0040D856   call        printf (004010b0)
0040D85B   add         esp,8
38:
39:       *test =sum;          //test=sum is wrong
0040D85E   mov         dword ptr [ebp-0Ch],offset @ILT+0(_sum) (00401005)
40:       result=test(1,2);      //此处(*test)(1,2) 也一致,可通过。
0040D865   mov         esi,esp
0040D867   push        2
0040D869   push        1
0040D86B   call        dword ptr [ebp-0Ch]
0040D86E   add         esp,8
0040D871   cmp         esi,esp
0040D873   call        __chkesp (00401130)
0040D878   mov         dword ptr [ebp-4],eax
41:           printf ("sum=%d\n",result);
0040D87B   mov         eax,dword ptr [ebp-4]
0040D87E   push        eax
0040D87F   push        offset string "sum=%d\n" (0042201c)
0040D884   call        printf (004010b0)
0040D889   add         esp,8
42:       return 0;
0040D88C   xor         eax,eax
43:   }
0040D88E   pop         edi
0040D88F   pop         esi
0040D890   pop         ebx
0040D891   add         esp,4Ch
0040D894   cmp         ebp,esp
0040D896   call        __chkesp (00401130)
0040D89B   mov         esp,ebp
0040D89D   pop         ebp
0040D89E   ret


对函数指针比较了解,但是对于下面这个another有些郁闷了,
another
到底能不能算一个变量类型,这不是一个函数名嘛,也即常量啊,有没有朋友知道怎么去解决这个问题。

typedef int (another)(int,int);  //变量类型? another,函数名也能算变量类型?比较疑惑
//int another(int,int);


可以看到,在编译器编译过程中
&sum ,sum
都是同一个数。我感觉这个严重伤害老百姓的感情,该怎么定义就怎么定义,否则很容易让人搞混。

强转是个好习惯,但是人有时候偷懒就不想自己强转,在涉及到指针偏移的时候可能会出错,因为编译器的默认转化可能不是我们想要的结果。一定要注意。

我们来想下一个问题,为什么在typedef的时候,需要带上返回值类型和参数列表。准确的说,这是一个关于函数指针定义的问题。

来看一下普通函数的调用方式:

int result = sum(1,2);
0040D758   push        2
0040D75A   push        1
0040D75C   call        @ILT+0(_sum) (00401005)
0040D761   add         esp,8
0040D764   mov         dword ptr [ebp-4],eax


所有的高级语言都这是一个接口,看看汇编层次:

首先,你需要压入参数吧,就必须定义push的多少,编译器就需要知道参数数目,所以定义的时候要(int,int ,,,,).

接着吧,你还要知道返回的值取的长度,
int sum(int,int)
就代表了要
dword ptr 还是 word ptr,或者 byte ptr
.

在我们眼里,函数地址就是一个地址,但是只有一个地址是不能够完成函数调用的,不是说你call 401005 就能完成sum函数。因为函数调用需要上下文条件,所以定义的时候才必须提供参数,和返回值类型。

函数指针变量存放的就只是
sum
的地址,但靠此地址不能完成调用,sum函数还没有完成,所以一个函数指针必须要提供返回值类型和参数类型,这时候编译器才能将完成对整个函数的汇编级编译。

关于指针插个题外话:

*在前表示取指向的值,*在后表示声明类型,常用于强制转化。比如*p 表示取p指向的值,p* 表示p类型的指针。关于这个知识点,常常和char* a, *a 做类比,就知道了。


总结:typedef在看不懂的时候将typedef这个词给删除了,后面应该就是一个变量的普通定义,记住这一点。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c-c++