11.5 typedef

本章接触到了许多复杂的数据类型,这些类型的名称通常都比较臃肿、复杂,写代码时很容易出错。C语言提供了一种为数据类型名称取别名的方法,这就是typedef这个关键字的用途。例如下面的语句:

11.5 typedef - 图1

表示的含义就是:“INT”是一种数据类型的名称,这个数据类型就是“int”这种类型。之后就可以用“INT”这个标识符去做“int”可以做的一切事情了。比如定义变量:

11.5 typedef - 图2

这和:

11.5 typedef - 图3

是完全等效的。

那么,这样做的意义何在?

这样做可以增加代码的可读性,使代码更加干净、简洁,并降低代码中可能出现的错误。比如:

11.5 typedef - 图4

“u”的数据类型是“int( [5])(int )”,是一个数组类型,这种数组有5个元素,每个元素都是指针,这种指针指向的是参数为“int *”类型返回值为“int”类型的函数。如果在代码中多次使用这种复杂的类型名称无疑容易出错,其含义也晦涩难懂。这时就可以为这种复杂的数组类型取一个有意义的名称。

11.5 typedef - 图5

那么以后在代码中在写这种数据类型名称的时候,直接写FZSZ就可以了。比如,可以用这个名称定义变量:

11.5 typedef - 图6

使用typedef语句为数据类型取别名的要点是,把新的类型的别名写在用原类型定义变量时写变量的位置。比如:

11.5 typedef - 图7

很多简单的数据也用typedef语句定义别名,常见的有“size_t”,事实上这个类型是一种“unsigned”整数类型,但是可能在甲环境中是“unsigned”,在乙环境中是“unsigned long”。在代码中使用“size_t”这种别名的好处是,一旦涉及到代码的移植问题,只要简单地修改一下typedef语句就可以了,不需要在代码中每个出现“size_t”的位置修改。这和把常量写成宏有利于代码修改是一个道理。所以使用typedef语句的另一个层面的原因是可以增强代码的可移植性。

使用typedef语句给出结构体等数据类型一个新名称还有另外一个好处,即可以省却为结构体名称的标签部分取名的烦恼(取名从来是很难的事情)。

11.5 typedef - 图8

在前面,曾经用#define预处理命令完成过类似的任务,但是两者之间还是有区别的。例如:

11.5 typedef - 图9

实际上表示的是:

11.5 typedef - 图10

也就是说,“b”是“char”类型。而:

11.5 typedef - 图11

表示的是a和b都是“char *”。此外有些复杂的数据类型,用#define是无法胜任的,比如:

11.5 typedef - 图12

给出了一种数组类型的名称,可以用ARR定义数组,但是#define做不到这点。所以在学习了typedef之后,原则上以后不应该再使用预处理的办法了。

在C语言中,所有的标识符都有其有效区间,typedef定义的类型的新名称也不例外。在某个“{}”内的typedef语句定义的类型新名称只在该块内有效。在函数外部用typedef语句定义的类型新名称的有效范围是从定义处直到源文件结束。

typedef有时候可能会让不熟悉用法的人发懵,比如:

11.5 typedef - 图13

你能看明白后一语句在说什么吗?实际上,这是在描述fl和f2这两个函数的函数原型。