typedef使用详解1.再论typedef1.1、如何轻松理解和应用typedef?typedef用最简单的话去诠释他,那么就是给类型取别名。
但是牠并没有你想象的那么简单!举例:typedef int size;//那么int就有一个别名叫size了,以后size就和int这个类型是一样的用法了。
看到这里,如果你仅仅认为typedef不过如此,不就是把第一个参数(类型名),等同于第二个参数,这么简单。
那你可能就会陷入困境。
比如看下面这个例子: typedef char Line[81]; 你能说Line[81]就是char的别名吗?这显然不对。
它真正的含义是:Line类型即代表了具有81个元素的字符数组。
那么Line t;就等同于char t[81];看到这里读者是否晕了?我们尝试把它放在一起看看。
typedef int size; //typedef行int i; //原型行size i; //应用行同理:typedef char Line[81]; //typedef行char t[81]; //原型行Line t; //应用行再举一个函数指针的例子:typedef int (*fun_ptr)(int,int); //typedef行int (*fp)(int,int); //原型行fun_ptr fp; //应用行以上3个例子都有以下几个共同点:1、“typedef行”和“原型行”相比,“typedef行”仅仅多个typedef而已。
就函数指针的例子来说,其次,“typedef行”和“原型行”的根本区别在于:fun_ptr是类的别名,fp是该类的变量。
2、“原型行”和“应用行”,它们的编译结果是一样的。
就函数指针的例子来说,他们都是创建了一个类型为int (*)(int,int)的函数指针fp。
只是fun_ptr fp(应用行)比int (*fp)(int,int)(原型行)这种形式更加简洁,便与书写和理解。
形式越复杂typedef 的优势就越明显。
Typedef的定义应用和理解应该是一步到位的。
定义过程:只要我们能写出原型行,就能直接写出typedef行。
应为形式上只差一个typedef。
如:我们写出原型:char t[81],那么加上一个typedef就能得到我们想要的定义,当然可以修改下类名,如:typedef char T[81]。
应用过程:T t;其中T是之前定义的类型,t是通过该类型定义的变量。
理解过程:如何理解“T t;”要想理解“T t;”就找到与之对应的“原型行”,但是“原型行”一般在程序中是不会出现,所以只能先找到“typedef行”,在通过“typedef行”推出“原型行”(推的过程就是去掉typedef而已)。
“T t;”的“typedef行”为typedef char T[81],所以“T t”所表示的“原型行”就是char x[81],而“原型行”和“应用行”(这里“T t;”就是应用行),的含义完全相同。
那么对“T t;”的理解就是对char x[81]的理解。
在“T t”的定义中,T的typedef定义形式为:typedef char T[81];因此T t就等价于:char t[81];所以:typedef char T[81];T t;与直接char t[81];是完全等价的。
小结:当我们看到一个typedef定义时,如:typedef int (*fun_ptr)(int,int)。
我们的大脑里需要做两个条件反射:1、typedef是给类取别名,所以只要是typedef定义的东西都是类型。
所以,看到以上表达式就要意识到fun_ptr是个类型。
2、要理解typedef到底定义了什么,首先去掉typedef,再将typedef 定义的“类型”看成“变量”。
如:将以上表达式就看成:int (*x)(int,int),就能明白该表达是的目的是想定义一个函数指针类型。
注意事项:typedef在语法上是一个存储类的关键字(如auto、extern、static、register),而变量只能被一种储类的关键字修饰。
如果变量被两种及以上储类的关键字修饰则编译报错:typedef static int a;//错误示范错误信息为:multiple storage classes in declaration specifiers。
1.2、typedef与#define宏的区别讲到typedef就不得不提一提#define,以便大家对比学习,将知识点编织成网。
与typedef不同,#define是单纯的替换,替换发生在预编译过程,此时可以把#define的每个参数看成一堆字母,#define 只是将一堆字母用另一堆字符母换。
至于字母的含义分析,在预编译过程之后。
也就是说#define要做的只是傻傻的替换,至于词义的分析不在它的能力范围之内。
替换规则是,#define会将它右边的第一个参数作为替换的结果,将之后的参数(直到换行符)作为替换的目标。
接下来,就对比下typedef与#define:#define dpChar char*typedef char* tpChar;dpChar p1, p2;tpChar p3, p4;这里貌似#define和typedef想干的是同一件事——用一个新的名字替换掉char*。
先不管结果是否一致,先看看形式上有什么不同:#define dpChar char*typedef char* tpChar;首先,#define是没有分号的(当然如果有分号,分号也将成为替换的内容,但这明显不是我们想要的结果)。
而typedef作为语句,必须是有分号的。
其次,他们的参数看上去是反过来的,如char*在#define里是作为第二个参数,而在typedef是作为第一个参数。
有的时候,还真的容易弄反了。
记得有一次,读者在头文件里定义了一个#defin就把顺序弄反了,结果编译报错,愣是看了好久没发现问题。
那么如何解决这个易混淆的地方呢?其实如果读者能回想一下上一节内容这个问题就可以迎刃而解。
当我们用typedef定义类型时,如果去掉typedef,形式上其实是一个再正常不过的定义变量的语句。
如typedef char* tpChar;去掉typedef之后,就是char* tpChar;所以此时char*当然在前面。
所有只要记住顺序上#define和typedef相反就行了,#define是将第一个参数作为替换的结果。
说完了形式的区别,再来看看结果是否一致:dpChar p1, p2;tpChar p3, p4;dpChar是#define定义的,按照替换原则,替换的结果为:char* p1, p2;再进行语法分析可知结果为:char* p1;和char p2。
此时p1是char*类型p2是char类型。
而tpChar是typedef给char*取的别名,此时定义出的p3和p4的类型都是char*。
所以想一次性定义多个指针变量,记得用typedef。
1.3、typedef与struct结构体在使用时都是先定义结构体类型,再用结构体类型去定义变量。
如struct node {}这样就定义了一个node的结构体类型。
在申请node的变量时,必须带上struct:struct node n;如果配合typedef,有如下几种用法:1、在利用结构体类型申请变量时就可以省略掉struct这个关键字:typedef struct node{} Node;//给struct node{}类型取别名Node n;//利用结构体类型申请变量2、使用typedef一次定义2个类型,分别是结构体变量类型,和结构体变量指针类型。
typedef struct node{} Node, *pNode;这句话可以拆分成两句理解: typedef 类型Node;和typedef 类型* pNode;其中Node为结构体类型,pNode是结构体指针类型。
1.4、 typedef与const(1)typedef int *PINT; const PINT p2; 相当于是int *const p2;(2)typedef int *PINT; PINT const p2; 相当于是int *const p2;(3)如果确实想得到const int *p;这种效果,只能typedef const int *CPINT;CPINT p1;1.5、使用typedef的重要意义使用typedef的原因主要有两个:1、简化类型,让程序更易理解和书写。
2、创造平台无关类型,便与移植。
至于简化模型这点,从“如何轻松理解和应用typedef?”这节中的第三例子应该可以体会到了。
这里再来举个例子来体会下:不采用typedef的情况:void (*a[10]) (void (*)());采用typedef的情况:typedef void (*pFun)(pFunParam);pFun a[10];这里重点说说typedef的第二个重要意义——创造平台无关类型。
比如利用typedef 定义一个浮点类型,名字就叫myMax,myMax必须始终代表该平台的最高精度的浮点类型。
如果程序移植到支持long double的平台上就给long double取别名为myMax,如果程序移植到最多支持float的精度的平台上就给float取别名为myMax。
如果仅仅采用系统提供的原生类型,而不采用typedef取别名的方式,当程序移植时,可能面临大量的程序修改工作。
比如,之前用到了long double这个类型,当移植到另一个平台时,这个平台只支持float类型而不支持long double这个类型。
那么你将面临将全部的long double换成float。
而如果,之前将long double取别名为myMax,程序中用的类型都是myMax。
那么我们只需要把typedef long double myMax;更换为typedef float myMax;即可。