C语言typedef关键字—伟大的缝纫师
关于马甲的笑话。有这样一个笑话:一个猎人在河边抓捕一条蛇,蛇逃进了水里。过一会,一个乌龟爬到岸边。猎人一把抓住这个乌龟,大声的说道:小样,别你为你穿了个马甲我就不认识你了!
typedef 关键字是个伟大的缝纫师,擅长做马甲,任何东西穿上这个马甲就立马变样。它可以把狼变成一头羊,也能把羊变成一头狼。甚至还可以把长着翅膀的鸟人变成天使,同样也能把美丽的天使变成鸟人。所以,你千万不要得罪它,一定要掌握它的脾气,不然哪天我把你当鸟人,你可别怪我。^_^。
在实际项目中,为了方便,可能很多数据类型(尤其是结构体之类的自定义数据类型)需要我们重新取一个适用实际情况的别名。这时候typedef 就可以帮助我们。例如:
typedef struct student
{
//code
}Stu_st,*Stu_pst;//命名规则请参考本章前面部分
A),struct student stu1;和Stu_st stu1;没有区别。
B),struct student *stu2;和Stu_pst stu2;和Stu_st *stu2;没有区别。
这个地方很多初学者迷惑,B)的两个定义为什么相等呢?其实很好理解。我们把“struct student { /*code*/}”看成一个整体,typedef 就是给“struct student {/*code*/}”取了个别名叫“Stu_st”;同时给“struct student { /*code*/} *”取了个别名叫“Stu_pst”。只不过这两个名字同时取而已,好比你给你家小狗取了个别名叫“大黄”,同时你妹妹给小狗带了小帽子,然后给它取了个别名叫“小可爱”。^_^。
好,下面再把typedef 与const 放在一起看看:
C),const Stu_pst stu3;
D),Stu_pst const stu4;
大多数初学者认为C)里const 修饰的是stu3 指向的对象;D)里const 修饰的是stu4这个指针。很遗憾,C)里const 修饰的并不是stu3 指向的对象。那const 这时候到底修饰的是什么呢?我们在讲解const int i 的时候说过const 放在类型名“int”前后都行;而const int *p 与int * const p 则完全不一样。也就是说,我们看const 修饰谁都时候完全可以将数据类型名视而不见,当它不存在。反过来再看“const Stu_pst stu3”,Stu_pst 是“struct student { /*code*/} *”的别名, “struct student {/*code*/} *”是一个整体。对于编译器来说,只认为Stu_pst 是一个类型名,所以在解析的时候很自然的把“Stu_pst”这个数据类型名忽略掉。
现在知道const 到底修饰的是什么了吧?^_^。
E) #define INT32 int
unsigned INT32 i = 10;
F)typedef int int32;
unsigned int32 j = 10;
其中F)编译出错,为什么呢?E)不会出错,这很好理解,因为在预编译的时候INT32被替换为int,而unsigned int i = 10;语句是正确的。但是,很可惜,用typedef 取的别名不支持这种类型扩展。另外,想想typedef static int int32 行不行?为什么?
下面再看一个与#define 宏有关的例子:
G) #define PCHAR char*
PCHAR p3,p4;
H)typedef char* pchar;
pchar p1,p2;
两组代码编译都没有问题,但是,这里的p4 却不是指针,仅仅是一个char 类型的字符。这种错误很容易被忽略,所以用#define 的时候要慎之又慎。关于#define 当然还有很多话题需要讨论,请看预处理那一章。当然关于typedef 的讨论也还没有结束,在指针与数组那一章,我们还要继续讨论。
1)#define a int[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
2) typedef int a[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
3) #define a int*[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
4) typedef int * a[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
5) #define *a int[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
6) typedef int (* a)[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
7) #define *a * int[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
8) typedef int * (* a)[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
请判断这里面哪些定义正确,哪些定义不正确。另外,int[10]和a[10]到底该怎么用?
typedef 关键字是个伟大的缝纫师,擅长做马甲,任何东西穿上这个马甲就立马变样。它可以把狼变成一头羊,也能把羊变成一头狼。甚至还可以把长着翅膀的鸟人变成天使,同样也能把美丽的天使变成鸟人。所以,你千万不要得罪它,一定要掌握它的脾气,不然哪天我把你当鸟人,你可别怪我。^_^。
一、历史的误会——也许应该是typerename。
很多人认为typedef 是定义新的数据类型,这可能与这个关键字有关。本来嘛,type 是数据类型的意思;def(ine)是定义的意思,合起来就是定义数据类型啦。不过很遗憾,这种理解是不正确的。也许这个关键字该被替换为“typerename”或是别的词。typedef 的真正意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别名,而非定义一个新的数据类型。比如:华美绝伦的芍药,就有个别名---“将离”。中国古代男女交往,往往以芍药相赠,表达惜别之情,送芍药就意味着即将分离。所以文人墨客就给芍药取了个意味深长的别名-----“将离”。这个新的名字就表达了那种依依不舍的惜别之情…这样新的名字与原来的名字相比,就更能表达出想要表达的意思。在实际项目中,为了方便,可能很多数据类型(尤其是结构体之类的自定义数据类型)需要我们重新取一个适用实际情况的别名。这时候typedef 就可以帮助我们。例如:
typedef struct student
{
//code
}Stu_st,*Stu_pst;//命名规则请参考本章前面部分
A),struct student stu1;和Stu_st stu1;没有区别。
B),struct student *stu2;和Stu_pst stu2;和Stu_st *stu2;没有区别。
这个地方很多初学者迷惑,B)的两个定义为什么相等呢?其实很好理解。我们把“struct student { /*code*/}”看成一个整体,typedef 就是给“struct student {/*code*/}”取了个别名叫“Stu_st”;同时给“struct student { /*code*/} *”取了个别名叫“Stu_pst”。只不过这两个名字同时取而已,好比你给你家小狗取了个别名叫“大黄”,同时你妹妹给小狗带了小帽子,然后给它取了个别名叫“小可爱”。^_^。
好,下面再把typedef 与const 放在一起看看:
C),const Stu_pst stu3;
D),Stu_pst const stu4;
大多数初学者认为C)里const 修饰的是stu3 指向的对象;D)里const 修饰的是stu4这个指针。很遗憾,C)里const 修饰的并不是stu3 指向的对象。那const 这时候到底修饰的是什么呢?我们在讲解const int i 的时候说过const 放在类型名“int”前后都行;而const int *p 与int * const p 则完全不一样。也就是说,我们看const 修饰谁都时候完全可以将数据类型名视而不见,当它不存在。反过来再看“const Stu_pst stu3”,Stu_pst 是“struct student { /*code*/} *”的别名, “struct student {/*code*/} *”是一个整体。对于编译器来说,只认为Stu_pst 是一个类型名,所以在解析的时候很自然的把“Stu_pst”这个数据类型名忽略掉。
现在知道const 到底修饰的是什么了吧?^_^。
二、typedef 与#define 的区别
噢,上帝!这真要命!别急,要命的还在后面呢。看如下例子:E) #define INT32 int
unsigned INT32 i = 10;
F)typedef int int32;
unsigned int32 j = 10;
其中F)编译出错,为什么呢?E)不会出错,这很好理解,因为在预编译的时候INT32被替换为int,而unsigned int i = 10;语句是正确的。但是,很可惜,用typedef 取的别名不支持这种类型扩展。另外,想想typedef static int int32 行不行?为什么?
下面再看一个与#define 宏有关的例子:
G) #define PCHAR char*
PCHAR p3,p4;
H)typedef char* pchar;
pchar p1,p2;
两组代码编译都没有问题,但是,这里的p4 却不是指针,仅仅是一个char 类型的字符。这种错误很容易被忽略,所以用#define 的时候要慎之又慎。关于#define 当然还有很多话题需要讨论,请看预处理那一章。当然关于typedef 的讨论也还没有结束,在指针与数组那一章,我们还要继续讨论。
三、#define a int[10]与typedef int a[10];
留两个问题:1)#define a int[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
2) typedef int a[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
3) #define a int*[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
4) typedef int * a[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
5) #define *a int[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
6) typedef int (* a)[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
7) #define *a * int[10]
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
8) typedef int * (* a)[10];
A),a[10] a[10];
B),a[10] a;
C),int a[10];
D),int a;
E),a b[10];
F),a b;
G),a* b[10];
H),a* b;
请判断这里面哪些定义正确,哪些定义不正确。另外,int[10]和a[10]到底该怎么用?