介绍

本文将详细介绍C语言下typedef的各种用法。

1. typedef创建易于记忆的类型名

typedef int size;

说明: 此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。

2. typedef掩饰数组类型

char line[81];
char text[81];
typedef char allen[81];
allen text, line;

3. typedef掩饰指针类型

typedef char * pstr;

标准函数 strcmp()有两个const char *类型的参数。因此,它可能会误导人们像下面这样声明 strcmp()

int strcmp(const pstr, const pstr); 

说明: 这是错误的,按照顺序,const pstr被解释为char * const,而不是const char *。这个问题很容易解决:

typedef const char * cpstr;
int strcmp(cpstr, cpstr); // 现在是正确的

注:只要为指针声明typedef,那么都要在typedef名称中加一个const.

4. typedef代码简化

typedef int (*PF) (const char *, const char *); 

说明: 这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:

PF Register(PF pf);

说明: Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:

int (*Register (int (*pf)(const char *, const char *)))(const char *, const char *); 

5. typedef & 存储类关键字

typedef 就像 auto,extern,mutable,static, register一样,是一个存储类关键字,表明typedef 会真正影响对象的存储特性。

typedef register int FAST_COUNTER;   // 错误,编译通不过。

说明: 问题出在不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。

6. typedef促进跨平台开发

typedef 有另外一个重要的用途,那就是定义机器无关的类型。例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以获得最高的精度:

typedef long double REAL;

说明: 1)在不支持 long double 的机器上,该 typedef 看起来会是下面这样:typedef double REAL; 2)在连 double 都不支持的机器上,该 typedef 看起来会是这样:typedef float REAL;

7. typedef & 结构体

当用下面的代码定义一个结构时,编译器报了一个错误,莫非C语言不允许在结构中包含指向它自己的指针吗?

typedef struct tagNode
{
	char *pItem;
	pNode pNext;
} *pNode;

说明: 原因在于新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。

正确使用如下:

typedef struct tagMyStruct
{ 
int iNum;
long lLength;
} MyStruct; 

说明: 这语句实际上完成两个操作:

  • 定义一个新的结构类型
    struct tagMyStruct
    { 
      int iNum; 
      long lLength; 
    }; 
    
  • typedef为这个新的结构起了一个名字,叫MyStruct。
    typedef struct tagMyStruct MyStruct; 
    

tagMyStruct实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。 我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。

如下做法也正确:

typedef struct tagNode 
{
	char *pItem;
	struct tagNode *pNext;
} *pNode; 

typedef struct tagNode *pNode;
struct tagNode 
{
	char *pItem;
	pNode pNext;
};

注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。

8. typedef & #define的问题:

有下面两种定义pStr数据类型的方法,两者有什么不同,哪一种更好一点?

typedef char *pStr;
#define pStr char *; 

通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:

typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4; 

说明: 在上述的变量定义中,s1、s2、s3都被定义为char*,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。

总结#define与typedef使用:

  • #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
  • typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。

9. typedef & 复杂的变量声明

下面是三个变量的声明,使用typdef分别给它们定义一个别名

int *(*a[5])(int, char*);
void (*b[10]) (void (*)());
doube(*)() (*pa)[9];   //这个太狗血了~

说明: 对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。

  • example1:
    int *(*a[5])(int, char*);   
    typedef int *(*pFun)(int, char*); //pFun是我们建的一个类型别名
    pFun a[5];  //使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
    
  • example2:
    void (*b[10]) (void (*)());
    typedef void (*pFunParam)();//首先为上面表达式蓝色部分声明一个新类型
    typedef void (*pFun)(pFunParam);//整体声明一个新类型
    pFun b[10];//使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
    
  • example3: ```c doube()() (pa)[9]; typedef double(pFun)();//首先为上面表达式蓝色部分声明一个新类型 typedef pFun (pFunParam)[9];//整体声明一个新类型 pFunParam pa; //使用定义的新类型来声明对象,等价于doube()() (pa)[9];