C 语言程序设计:问与答

问:本教程中提到 C 语言不是为开发大型程序设计的。UNIX 不是大型程序吗?

答:在 C 语言被设计出来时还不是。在 1978 年的一篇论文中,Ken Thompson 估计 UNIX 内核约有 10 000 行 C 代码(加上一小部分汇编代码)。UNIX 其他部分的大小也类似。在 1978 年的另一篇论文中,Dennis Ritchie 和他的同事将 PDP-11 的 C 编译器大小设定为 9660 行。按现在的标准,这绝对只是小型程序。

问:C 库中有什么抽象数据类型吗?

答:从技术上说,没有。但有一些很接近,包括 FILE 类型。在对文件进行操作之前,必须声明 FILE * 类型的变量:

1
FILE *fp;

这个 fp 变量随后会被传递给不同的文件处理函数。

程序员需要把 FILE 作为一种抽象,在使用时不需要知道 FILE 具体是怎样的。FILE 可能是一个结构类型,但 C 标准并不保证这一点。实际上,最好不要管 FILE 值究竟是如何存储的,因为 FILE 类型的定义对不同的编译器可能(也确实经常)是不一样的。

当然,我们总是可以通过查看 stdio.h 文件来找到 FILE 到底是什么。如果这么做,那么就没什么可以阻止我们编写代码来访问 FILE 的内部机制。例如,我们可能发现 FILE 结构中有一个名为 bsize(文件的缓冲区大小)的成员:

1
2
3
4
5
typedef struct {
  ...
  int bsize;      /* buffer size */
  ...
} FILE;

一旦知道了 bsize 成员,就无法阻止我们直接访问特定文件的缓冲区大小:

1
printf("Buffer size: %d\n", fp->bsize);

然而,这样做并不好,因为其他 C 编译器可能将缓冲区大小存储在其他名字中,或者用不同的方式跟踪这个值。试图修改 bsize 成员则更糟糕:

1
fp->bsize = 1024;

这是一件非常危险的事,除非我们知道文件存储的全部细节。即使我们的确知道相关的细节,不同的编译器或是同一编译器的新版本也可能不一样。

问:除了不完整结构类型,还有别的不完整类型吗?

答:最常见的不完整类型之一出现于声明数组但不指定大小时:

1
extern int a[];

在这个声明(第一次遇到这个声明是在 C 语言头文件简介)之后,a 具有不完整类型,因为编译器不知道 a 的大小。有可能 a 在程序的另一个文件中定义,该定义补充了缺失的长度信息。另一种不完整类型出现在没有指定数组的长度但提供了初始化器的数组声明中:

1
int a[] = {1, 2, 3};

在这个例子中,数组 a 刚开始具有不完整类型,但初始化器使得该类型完整了。

声明联合标记而不指明联合的成员也会创建不完整类型。 弹性数组成员(C 语言弹性数组成员,C99 的特性)就具有不完整类型。最后,void 也是不完整类型。void 类型具有不同寻常的性质,它永远不能变成完整类型,因此无法声明这种类型的变量。

问:不完整类型在使用上有别的限制吗?

答:sizeof 运算符不能用于不完整类型(这不奇怪,因为不完整类型的大小未知)。结构或联合的成员(弹性数组成员除外)不可以具有不完整类型。类似地,数据元素不可以具有不完整类型。最后,函数定义中的形式参数不可以具有不完整类型(在函数声明中可以)。编译器会“调整”函数定义中的每个数组形式参数使其具有指针类型,从而阻止其具有不完整类型。

请参阅

(完)

comments powered by Disqus