用法
typedef
是 C 语言的关键字之一,人尽皆知的用法就是“为一个类型取别名”,比如:1
2typedef unsigned int size_t;
typedef char bool;
或者:1
2
3
4
5typedef struct Student student;
typedef struct Teacher {
char name[NAMESIZE];
int age;
} teacher;
还有:1
2
3
4
5typedef struct Node* PtrtoNode;
typedef struct Node {
PtrtoNode next;
int data;
} node;
类似的例子有很多,但实际上“为一个类型取别名”这种说法是不严谨的。私认为typedef
关键字不仅仅是“取别名”,还要从语义的角度上去理解typedef
的行为。
比如:1
2
3typedef int Arr[5];
Arr *a;
int (*parr)[5];
从语义的角度而言,在上述代码中,Arr
是一种“新类型”(这里的新类型其实并不是真正意义上新类型),表示元素个数为 5 的int
数组,对应的a
是一个指向元素个数为 5 的int
数组的数组指针。对比类型一样、用基本类型声明的数组指针parr
,也可以看出使用typedef
带来的便利性。
实际上,熟悉 C++ 的同学,可能会发现typedef
和using
的功能有点类似。
类似的用法,还有定义函数指针的用法,如下:1
typedef void (*pfun)(int);
上述代码中,定义了一个返回值为void
,参数为一个int
的函数指针类型pfun
。
陷阱
这里,还需要提一下typedef
和const
关键字一起使用时的陷阱——千万不要把typedef
和#define
的文本替换混为一谈。
比如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
typedef char *pchar;
int main() {
char s1[10] = "hello";
char s2[10] = "world";
const pchar str = s1;
// str = s2; // error
*str = 'g';
puts(s1);
const char *pc = s1;
pc = s2;
// *pc = 'g'; // error
puts(pc);
int i = 0, j = 2;
const int *pi = &j;
pi = &i;
// *pi = 2; // error
int * const ppi = &i;
*ppi = 3; // correct
printf("%d\n", *pi);
return 0;
}
/*
out:
gello
world
3
*/
在阅读下面的讨论之前,需要理解const typename *
和typename * const
的区别。
如果认为typedef
是文本替换,那么const pchar str
与const char *str
一致,对应语句*str = 'g';
就会出错,而str = s2;
就会正常执行(可以对比下面const char *
和const int
相关的代码)。但事实上,结果正好相反,编译器认为*str = 'g';
正确、str = s2;
错误,这说明,编译器并不认为str
是一个const char *
。
从语义的角度来理解,pchar
的类型是char
型的指针,在其前面加上一个const
,表示这个指针是一个常量,所以const pchar
的实际类型是char * const
,而不是const char *
,这点从后面的代码也可以对比出来。
最后需要说明的是,typedef
的用法是 C 的标准规定的,本文只是验证如何使用,而不是证明这是编译器的黑魔法😂。