不完全类型和复杂声明
不完全类型是暂时没有完全定义好的类型,编译器不知道这种类型该占几个字节的存储空间,例如:
struct s;
union u;
char str[];
具有不完全类型的变量可以通过多次声明组合成一个完全类型,比如数组str声明两次:
char str[];
char str[10];
当编译器碰到第一个声明时,认为str是一个不完全类型,碰到第二个声明时str就组合成完全类型了,如果编译器处理到程序文件的末尾仍然无法把str组合成一个完全类型,就会报错。
有些情况下这么做有一定的理由,比如第一个声明是写在头文件里的,第二个声明写在.c文件里,这样如果要改数组长度,只改.c文件就行了,头文件可以不用改。
不完全的结构体类型有重要作用:
struct s {
struct t *pt;
};
struct t {
struct s *ps;
};
struct s和struct t各有一个指针成员指向另一种类型。编译器从前到后依次处理,当看到strucs { struct t* pt; };时,认为struct t是一个不完全类型, pt是一个指向不完全类型的指针,尽管如此,这个指针却是完全类型,因为不管什么指针都占4个字节存储空间,这一点很明确。然后编译器又看到struct t { struct s *ps; };,这时struct t有了完整的定义,就组合成一个完全型了, pt的类型就组合成一个指向完全类型的指针。由于struct s在前面有完整的定义,所以struct s *ps;也定义了一个指向完全类型的指针。
这样的类型定义是错误的:
struct s {
struct t ot;
};
struct t {
struct s os;
};
编译器看到struct s { struct t ot; };时,认为struct t是一个不完全类型,无法定义成员ot,因为不知道它该占几个字节。所以结构体中可以递归地定义指针成员,但不能递归地定义变量成员,你可以设想一下,假如允许递归地定义变量成员, struct s中有一个struct t, struct t中又有一个struct s, struct s又中有一个struct t,这就成了一个无穷递归的定义。
以上是两个结构体构成的递归定义,一个结构体也可以递归定义:
struct s {
char data[6];
struct s* next;
};
当编译器处理到第一行struct s {时,认为struct s是一个不完全类型,当处理到第三行struct s*next;时,认为next是一个指向不完全类型的指针,当处理到第四行};时, struct s成了一个完全类型, next也成了一个指向完全类型的指针。