概述
typedef和define在C和C++的代码中比较常见,它们之间有些类似,但更多的是不同,特别是在带有指针的操作上。
typedef
typedef是类型别名的一种操作,类型别名是一个名字,它是某种类型的同义词。1
2typedef double wages; //wages是double的同义词
typedef wages base,*p; //base是double的同义词,p是double *的同义词
首先说一下常用方式
一般typedef有两种使用方式比较常见,第一种是对基本数据类型取别名,这种方法的目的在于使程序更加易于阅读:1
2
3
4typedef apple int;
typedef orange int;
apple a1,a2;
orange o1,o2;
这就很明显a1,a2是苹果对象,o1,o2是橘子对象。
第二种是在自定义数据结构中的使用,这种方法的目的在于书写更加方便:1
2
3
4
5
6typedef struct tagPOINT
{
int x;
int y;
}POINT;
POINT p1;
这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候,虽然这种方法用途不是很大,但是确实比较常见。
接下来说下typedef中的一些坑点。
1 | typedef char *pstring; |
上述两条声明语句的基本数据类型都是const pstring,const是对给定类型的修饰。pstring实际上是指向char的指针,因此,const pstring就是指向char的常量指针,而非指向常量字符的指针。切不可将其简单的替换进行理解,比如:const char *cstr = 0; //这是对const pstring cstr的错误理解
声明语句中用到pstring时,其基本数据类型是指针。可是用char *重写了声明语句后,数据类型就变成了char,*成为了声明符的一部分。这样改写的结果是,const char成了基本数据类型。前后两种声明的含义截然不同,前者声明了一个指向char的常量指针,改写后的形式则声明了一个指向const char的指针。
1 | typedef char* PCHAR; |
pa和pb都是char*类型的对象,这与define不同,typedef本身就是类型的别名,以上可以书写为:char *pa,*pb
而define只是简单的替换,这点需要明白。
还有一种简化书写的用途,在linux系统调用signal函数中可以很好的体现这一点,下面举两个例子来说明下
原声明:void (*b[10]) (void (*)())
变量名为b,先替换右边部分括号里的,pFunParam为别名一:typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:typedef void (*pFunx)(pFunParam);
原声明的最简化版:pFunx b[10];
再举一个例子:
原声明:doube(*)() (*e)[9];
变量名为e,先替换左边部分,pFuny为别名一:typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二typedef pFuny (*pFunParamy)[9];
原声明的最简化版:pFunParamy e;
define
其定义的一般形式为:#define 标识符 字符串
其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。简单的说define的效果就是字符串的替换。
例如:1
2
3
4
5
6
7
int main(){
int s,y;
y = 4;
s = 3*M+4*M+5*M;
printf("s = %d\n",s);
}
上例程序中首先进行宏定义,定义M来替代表达式(y*y+3*y),在s=3*M+4*M+5*M中作了宏调用。在预处理时经宏展开后该语句变为:
s=3 (y y + 3 y) + 4 (y y + 3 y) + 5 (y y + 3 y);
注意宏定义两边的括号不能少,否则会导致错误:
`#define M yy+3ys转换后为:
s=3yy+3y+4yy+3y+5yy+3y;`
显然并非本意。
在使用对指针的宏定义时需要谨慎:1
2
pstr p1,p2;
替换后为:char *p1,p2
p1被定义为指向char的指针,p2是char类型的一个对象(这与typedef不同)
最后关于宏定义再说明一下:
宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如果有错误,只能在编译已被宏展开后的源程序时发现。
宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号一起置换。
宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令
宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层次代换。
习惯上宏名用大写字母表示,以便于与变量区别。
可以用宏定义表示数据类型,使书写与阅读方便。
参考资料:
《C++primer 5th》