Middle Earth

[systemd notes] struct without definition (II)

关于 C 语言中的 “incomplete” 类型。

为前一篇文章找到依据了,C 标准对指针类型的定义如下:
A pointer type may be derived from a function type, an object type, or an incomplete
type, called the referenced type.
所以,指针可以从 incomplete 类型变化而来。上篇文章的例子中,没有定义的 struct FDSet 就是一个 “incomplete” 类型。这种还不”完整”的类型是可以用于定义指针的,所以 systemd 里的用法是有正式的依据的。

什么是 incomplete 类型,可以参阅 C 语言标准(草稿),C99 with Technical corrigenda TC1, TC2, and TC3 included,第 6.2.5 节;Sun Studio 手册, 6.11 Incomplete Types (写的不错。解释的很清楚,而且不像标准里面那样晦涩);MSDN 文章, Incomplete Types (比较简短,可做为补充阅读)。

下面是对 Sun Studio 文章的一点摘要。

按照标准里的说法,C 语言中的所有数据类型分为三种:函数,object,incomplete。函数类型最好理解,无需解释。其他所有的都是 object 类型,除非这个类型的大小(所占用的内存)不确定。而这个时候也就是 incomplete 类型。

incomplete 类型(只)有三种情况:void,未指定长度的数组,未指定内容的 struct 或 union。后两种情况比较类似,而 void 类型比较特殊,因为 void 永远无法”变成” complete。但是对于 incomplete 的 struct 和 union,只要把内容确定了 (即给出 struct/union 的明确的定义),它们就变成 complete 了 (也就是一般的 object 类型)。

上边是关于 incomplete 类型的定义,Sun Studio 的那篇文章随后讲到了它的使用 (6.11.3 Declarations,在声明里的使用;6.11.4 Expressions,在表达式里的使用)。

多数的声明是允许使用 incomplete 类型的,只有少数几个不允许 (比如声明数组的元素,struct/union 的成员,在函数的局部空间内的 object)。文中特别指出了四种合法的用法:
* Pointers to incomplete types
* Functions returning incomplete types
* Incomplete function parameter types
* typedef names for incomplete types
第一和第四种情况类似,在上一篇的 systemd 的例子中都用到了。而第二和第三种情况不太一样。这两种情况是说,在函数形参和返回值的声明里,可以使用 incomplete 类型。但是,当函数被定义或者被调用的时候,必须要使用正常的 object (显而易见,否则系统怎么知道分配多少内存?)。所以在函数定义和调用之前,声明里用到的 incomplete 类型需要变为 complete 类型 (通过给出 struct/union 的真正定义)。当然,除非用的是 void,void 是不能被 complete 的。所以 void 在这里是个特例。void 用于形参和返回值的时候,表示不需要参数或者没有返回值,而不是指代真正的参数或返回值。所以函数定义的时候,仍然可以使用 void 这种 incomplete 类型。

举个例子。跟其他 incomplete 类型一样,void 是不能拿来定义变量的。比如下边的小程序:
int main()
{
void bar;

return 0;
}
gcc 会报错,与上篇文章里的例子 (struct foo bar) 类似,不过错误信息不一样:
test.c:3: error: variable or field ‘bar’ declared void

在表达式中对 complete 与否的要求是不一样的,这里不再赘述。而至于为什么需要 incomplete 类型,也不再赘述。

Comments