《C语言程序设计:现代方法》看到第七章——基本类型,对书上7.6节的关于typedef的用法说明不太清楚,感觉书上的两页介绍不够详细,在网上再找了些资料,做下汇总:
typedef是为现有类型(包含变量类型,指针/数组类型,结构类型和函数类型)创建一个新的名字,以增加代码的可读性,可维护性和可移植性。
用法1. 定义易于记忆的类型名:
typedef 数据类型关键字 自定义类型名;
如: typedef float Dollars;
然后就可以直接用Dollars关键字来定义变量了,如:
Dollars cash_in, cash_out; //它等同于float cash_in, cash_out;
上面是typedef的最简单的一个用法。
用法2. 定义一个指针类型的别名:
先看这行代码
char* pa, pb; // 声明了一个指向字符变量的指针,和一个字符变量,这多数不符合我们的意图
如果想定义两个都是指向字符变量的指针的,可利用typedef来实现:
typedef char* PCHAR;
PCHAR pa, pb;
这种用法很有用,特别是char* pa, pb的定义,初学者往往认为是定义了两个字符型指针,其实不是,而用typedef char* PCHAR就不会出现这样的问题,减少了错误的发生。
用法3. 定义一个数组类型的别名
typedef int A[5];
定义了一个变量A的类型为一个含有5个元素的整型数组
"typedef int A[5]"中A是含有5个元素的数组类型的一个typedef-name。
下面两条语句:
A a = {3, 4, 5, 7, 8}; /*正确*/
A b = { 3, 4, 5, 7, 8, 9}; /* 会给出Warning: excess elements in array initializer */
用法4. 定义一个结构类型的别名
typedef struct
{
... //
}
Foo_t;
变量Foo_t的类型为struct { ... // } ;
=> "typedef struct { ... // } Foo_t "中Foo_t是"struct { ... // }"的一个typedef-name。这里struct {...//}是一个无"标志名称(tag name)"的结构体声明。
这种用法比较省事,尤其在大量使用的时候。
用法5. 为复杂的声明定义一个新的简单的别名
原声明:void (*b[10]) (void (*)());
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];
理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。
举例:
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。
用法6. 定义与平台无关的类型
例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:
typedef long double REAL;
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
typedef double REAL;
并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:
typedef float REAL;
你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string<char, char_traits<char>,allocator<char>> 和 basic_ofstream<char, char_traits<char>>。
typedef的两个陷阱:
陷阱一:
可以象下面这样隐藏指针语法:
typedef char * pstr;
int mystrcmp(pstr, pstr);
这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *’类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():
int mystrcmp(const pstr, const pstr);
这是错误的,按照顺序,‘const pstr’被解释为‘char * const’(一个指向 char 的常量指针),而不是‘const char *’(指向常量 char 的指针)。这个问题很容易解决:
typedef const char * cpstr;
int mystrcmp(cpstr, cpstr); // 现在是正确的
记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。
陷阱二:
typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef static int INT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。
参考资料:
1.《C语言程序设计:现代方法》Page92, 93
2. Using typedef to Curb Miscreant Code(URL:http://www.devx.com/cplus/10MinuteSolution/17527/1954?pf=true)
使用 typedef 抑制劣质代码(URL: http://www.vckbase.com/document/viewdoc/?id=1365)
3. 也谈typedef(URL: http://bigwhite.blogbus.com/logs/20147715.html)
4. 关于typedef的用法总结(URL: http://www.cnblogs.com/csyisong/archive/2009/01/09/1372363.html)