typedef和define

概述

typedef和define在C和C++的代码中比较常见,它们之间有些类似,但更多的是不同,特别是在带有指针的操作上。

typedef

typedef是类型别名的一种操作,类型别名是一个名字,它是某种类型的同义词。

1
2
typedef double wages;    //wages是double的同义词
typedef wages base,*p; //base是double的同义词,p是double *的同义词

首先说一下常用方式

一般typedef有两种使用方式比较常见,第一种是对基本数据类型取别名,这种方法的目的在于使程序更加易于阅读:

1
2
3
4
typedef apple int;
typedef orange int;
apple a1,a2;
orange o1,o2;

这就很明显a1,a2是苹果对象,o1,o2是橘子对象。
第二种是在自定义数据结构中的使用,这种方法的目的在于书写更加方便:

1
2
3
4
5
6
typedef struct tagPOINT
{
int x;
int y;
}POINT;
POINT p1;

这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候,虽然这种方法用途不是很大,但是确实比较常见。

接下来说下typedef中的一些坑点。


1
2
3
typedef char *pstring;
const pstring cstr = 0; //cstr是指向char的常量指针
const pstring *ps; //ps是一个指针,它的对象是指向char的常量指针

上述两条声明语句的基本数据类型都是const pstring,const是对给定类型的修饰。pstring实际上是指向char的指针,因此,const pstring就是指向char的常量指针,而非指向常量字符的指针。切不可将其简单的替换进行理解,比如:
const char *cstr = 0; //这是对const pstring cstr的错误理解
声明语句中用到pstring时,其基本数据类型是指针。可是用char *重写了声明语句后,数据类型就变成了char,*成为了声明符的一部分。这样改写的结果是,const char成了基本数据类型。前后两种声明的含义截然不同,前者声明了一个指向char的常量指针,改写后的形式则声明了一个指向const char的指针。


1
2
typedef char* PCHAR;
PCHAR pa, pb;

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
#define M (y*y+3*y)
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 y
y+3ys转换后为:s=3yy+3y+4yy+3y+5yy+3y;`
显然并非本意。
在使用对指针的宏定义时需要谨慎:

1
2
#define pstr char*
pstr p1,p2;

替换后为:
char *p1,p2
p1被定义为指向char的指针,p2是char类型的一个对象(这与typedef不同)
最后关于宏定义再说明一下:

  • 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如果有错误,只能在编译已被宏展开后的源程序时发现。

  • 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号一起置换。

  • 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令

  • 宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层次代换。

  • 习惯上宏名用大写字母表示,以便于与变量区别。

  • 可以用宏定义表示数据类型,使书写与阅读方便。


参考资料:
《C++primer 5th》