Usage of typedef and define

刚接触vc6.0这个IDE的时候,发现MFC里面定义了很多新的变量类型,其实应该说是定义了很多已有类型的别名,刚开始学习的时候确实有点儿不习惯,后来想想为什么要重新定义一些新的类型名称呢?我自认为的好处是:(1)简化代码,使用方便;(2)方便移植。当我们要查找某个新定义的类型具体是什么的时候,一般在调试程序的时候,直接将鼠标放在我们所要查找的新类型的名称上,然后按F12即可以跳转到新类型定义的地方。

所以,我们在写程序的时候,也可以使用这一机制,即,用typedef来定义新的类型名称。下面我总结了一些关于typedef的用法和注意事项(也参考了一些网上的资料):


Part1:关于typedef的用法

我们根据用途来分别介绍一下typedef的相关用法。

 

(1)typedef用于定义一种类型的别名,而不只是简单的宏替换。可以使用这个别名同时声明某一类型的多个对象。

可以看出,在需要大量声明某些类型对象的时候(比如char*),使用typedef的方式更省事。

 

(2)用typedef帮助我们简化代码。
我们知道,在C语言中,一般我们定义一个struct结构体,以及定义一个其对象的方法如下:

而在C++中,我们定义一个结构体对象一般是直接写:结构名 对象名; 即,POINT p1; 为什么可以这样呢?是因为:

简化代码的好处,不只是上面这一点,typedef还可以将一个复杂的声明另起(定义)一个简单的别名。见第(3)点。

 

(3)typedef为复杂的声明定义一个新的简单的别名。
方法是:在原来的声明里逐步用别名替换一部分复杂的声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。
例1:
原声明:int*(*a[5])(int,char*);
//a是一个变量,后面接[],说明是一个数组([]的优先级高于*),前面有一个*,说明是一个指针,即,指针类型的数组。再看,(*a[5])后面有一对()括号,说明这是个函数,即,数组里面存放的是函数指针。这个函数有两个参数,分别是int类型和char*类型,且返回值为int*类型。
按照上面的方法,变量名为a,直接用一个新别名pFun替换a就可以了:
typedef int*(*pFun)(int,char*);
此时我们再想定义一个可以存放5个函数指针的数组,就可以简单地写为:
pFun a[5];   //原声明的最简化版本

例2:
原声明:void(*b[10])(void(*)());
//b是一个变量,后面接[],说明是一个数组([]的优先级高于*),前面有一个*,说明是一个指针,即,指针类型的数组。再看,(*b[10])后面有一对括号,说明这是一个函数,即,数组里面存放的是函数指针。然后我们发现函数的参数列表中,仅有一个参数,且这个参数是一个函数指针,此函数的参数为空,且返回值为void类型。而前面的那个函数的返回值为void类型。
按照上面的方法,变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];

例3:
原声明:doubl(*)()(*e)[9];
//e是一个变量,前面有一个*,说明它是一个指针。再看(*e)的后面是一个[],说明它指向一个数组,这个数组是什么类型呢?前面是一个double(*)(),是一个函数指针,参数为空,返回值为double类型。即,声明了指针e,它指向一个数组,这个数组存放的是double(*)()类型的函数指针。
按照上面的方法,变量名为e,先替换左边部分,pFuny为别名一:
typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef pFuny (*pFunParamy)[9];
原声明的最简化版:
pFunParamy e;


理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到第一个右圆括号(')')就调转阅读的方向;此括号内分析完就跳出此括号然后分析此括号后面的内容,还是按先右后左的顺序,如此循环,直到整个声明分析完。
举例:
int (*func)(int *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
int (*func[5])(int *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。


自己感觉分析复杂声明的方法,有点儿像我们在英语中分析一个so long的定语从句,有很多嵌套,一个定语从句中嵌套若干个定语从句,所以我们一般是,先找到主句,然后再分析从句,一步一步解剖,直至分析完毕。

我们也可以记住2个模式:
type (*)(...)  //函数指针(指向一个函数的指针)
type (*)[]     //数组指针(指向一个数组的指针)


(4)用typedef来定义与平台无关的类型。(方便移植)
比如我们定义一个叫REAL的浮点类型,在目标平台一上,让它表示最高精度的类型为: 
 
在不支持long double的平台二上,改为:

在连double都不支持的平台三上,改为:


也就是说,当跨平台时,只要改下typedef本身就行,不用对其他源码做任何修改。
 
标准库就广泛使用了这个技巧,比如size_t类型。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。


Some Pitfalls of Using typedef


[陷阱一]
以前我发过一篇关于const限定符用法的文章,当时特别要求注意,当const和typedef一起使用的时候,很多人会出错!
记住,typedef是为一种类型定义了一个新名字(别名),不同于宏,它不是简单的字符串替换。

typedef定义的别名不能做简单的字符替换,我们应该把它看成是一个很好的封装,即,const修饰的是PSTR,也就是说,修饰的是char*这个整体。而const char* cstr;指明的是,const限定符修饰的是char,而不是char*!所以,const PSTR cstr;  定义的是一个指向char类型的const指针(即,常量指针,必须初始化,且以后指针的方向不能再被改变)。

注意:
const char * cstr;//等价于char const * cstr; (即,const限定符可以放在所修饰类型的前面或者后面,建议,放在后面好理解。)


[陷阱二]
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性。
typedef static int SINT;   //不可行
编译将失败,会提示“指定了一个以上的存储类”。因为不能在声明中有多个存储类关键字,typedef已经占据了存储类关键字的位置,在

typedef声明中不能用register(或任何其它存储类关键字)。


Part2:关于#define的用法

#define为一宏定义语句,通常用它来定义常量(包括无参量与带参量),以及用来实现那些“表面似和善、背后一长串”的宏,它本身并不在编译过程中进行,而是在这之前(预处理过程)就已经完成了,但也因此难以发现潜在的错误及其它代码维护问题。

在C语言中,#define主要用于定义一些常量,而在C++中,则建议用const定义常量,因为可以定义自己的类型。(更详细的原因可以查阅《Essential C++》)

下面给出一个用#define写的一个有趣的程序例子,帮助我们理解它的用法:


最后,欢迎对本篇文章内容有不同见解的朋友留言讨论!:)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值