首页 百科知识 你必须自己检查外部类型

你必须自己检查外部类型

时间:2022-09-22 百科知识 版权反馈
【摘要】:有一些C实现,但不是所有的,带有一个称为lint的程序来捕获这些错误。这不是一个有效的C程序,因为一些外部名称在两个文件中被声明为不同的类型。因此,检查类型的工作只能由连接器来完成;如果操作系统的连接器不能识别数据类型,C编译器也没法过多地强制它。这可能发生,例如,编译器可以将int安排在long的低位。避免这种类型冲突的一个方法是使用像lint这样的工具。

 

3 连接

    一个C程序可能有很多部分组成,它们被分别编译,并由一个通常称为连接器、连接编辑器或加载器的程序绑定到一起。由于编译器一次通常只能看到一个文件,因此它无法检测到需要程序的多个源文件的内容才能发现的错误。

 

    在这一节中,我们将看到一些这种类型的错误。有一些C实现,但不是所有的,带有一个称为lint的程序来捕获这些错误。如果具有一个这样的程序,那么无论怎样地强调它的重要性都不过分。

 

3.1 你必须自己检查外部类型

    假设你有一个C程序,被划分为两个文件。其中一个包含如下声明:

 

int n;

 

而令一个包含如下声明:

 

long n;

 

这不是一个有效的C程序,因为一些外部名称在两个文件中被声明为不同的类型。然而,很多实现检测不到这个错误,因为编译器在编译其中一个文件时并不知道另一个文件的内容。因此,检查类型的工作只能由连接器(或一些工具程序如lint)来完成;如果操作系统的连接器不能识别数据类型,C编译器也没法过多地强制它。

 

    那么,这个程序运行时实际会发生什么?这有很多可能性:

 

实现足够聪明,能够检测到类型冲突。则我们会得到一个诊断消息,说明n在两个文件中具有不同的类型。

你所使用的实现将intlong视为相同的类型。典型的情况是机器可以自然地进行32位运算。在这种情况下你的程序或许能够工作,好象你两次都将变量声明为long(或int)。但这种程序的工作纯属偶然。

n的两个实例需要不同的存储,它们以某种方式共享存储区,即对其中一个的赋值对另一个也有效。这可能发生,例如,编译器可以将int安排在long的低位。不论这是基于系统的还是基于机器的,这种程序的运行同样是偶然。

n的两个实例以另一种方式共享存储区,即对其中一个赋值的效果是对另一个赋以不同的值。在这种情况下,程序可能失败。

    这种情况发生的里一个例子出奇地频繁。程序的某一个文件包含下面的声明:

 

char filename[] = "etc/passwd";

 

而另一个文件包含这样的声明:

 

char *filename;

 

    尽管在某些环境中数组和指针的行为非常相似,但它们是不同的。在第一个声明中,filename是一个字符数组的名字。尽管使用数组的名字可以产生数组第一个元素的指针,但这个指针只有在需要的时候才产生并且不会持续。在第二个声明中,filename是一个指针的名字。这个指针可以指向程序员让它指向的任何地方。如果程序员没有给它赋一个值,它将具有一个默认的0值(NULL)([译注]实际上,在C中一个为初始化的指针通常具有一个随机的值,这是很危险的!)。

 

    这两个声明以不同的方式使用存储区,它们不可能共存。

 

    避免这种类型冲突的一个方法是使用像lint这样的工具(如果可以的话)。为了在一个程序的不同编译单元之间检查类型冲突,一些程序需要一次看到其所有部分。典型的编译器无法完成,但lint可以。

 

    避免该问题的另一种方法是将外部声明放到包含文件中。这时,一个外部对象的类型仅出现一次[7]

 

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈