今天搞了一天的仓储管理,身心憔悴啊。
晚上师弟过来问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这个词给删除了,后面应该就是一个变量的普通定义,记住这一点。