现在的位置: 首页 > 综合 > 正文

C陷阱与缺陷书摘 —链接

2013年08月08日 ⁄ 综合 ⁄ 共 1857字 ⁄ 字号 评论关闭

1. 链接器

 

分别编译(separate compilation):若干个源文件可以在不同的时候单独进行编译,然后在恰当的时候整合到一起。典型的链接器是把由编译器或汇编器生成的若干个目标模块,整合成一个被称为载入模块或可执行文件的实体,该实体能够被操作系统直接执行。其中,某些目标模块是直接作为输入提供给连接器的;而另外一些目标模块则是根据连接过程的需要,从库文件中取得的。

 

链接器的输入是一个组目标模块和库文件。链接器的输出是一个载入模块。链接器读入目标模块和库文件,同时生成载入模块。对每个目标模块中的每个外部对象[注释1]
,链接器都要检查载入模块,看是否已有同名的外部对象。如果没有,链接器就将该外部对象添加到载入模块;如果有,链接器就要处理命名冲突。

 

注释1:链接器通常把目标模块看成是由一组外部对象(external object)组成的。每个外部对象代表着机器内存中的某个部分,并通过一个外部名称来识别。
因此,程序中的每个函数和每个外部变量,如果没有被声明为static,就都是一个外部对象。某些C编译器会对静态函数和静态变量的名称做一定改变,将它们也作为外部对象。由于经过了“名称修饰”,所以它们不会与其它源程序文件中的同名函数或同名变量名发生命名冲突。

 

2. 声明与定义

 

(1)声明语句 

                                     int a;

如果上述语句出现在所有函数体之外,那么它就被称为外部对象a的定义。这个语句说明了a是一个外部整形变量,同时为a分配了存储空间。

 

(2)声明语句

                                    int a = 7;

在定义a的同时也明确了a的初始值。该语句不仅为a分配内存,而且也说明了该内存中应该存储的值。

 

(3)声明语句

                                    extern int a;

并不是对a定义。该语句仍然说明了a是一个外部整型变量,但因为它包含了extern关键字,显示地说明了a的存储空间是在程序的其他地方分配的。从连接器角度看,上述声明是一个对外部变量a的引用,而不是对a的定义。

 

每个外部对象都必须在程序的某个地方进行定义。因此,如果一个程序中包括了语句 “extern int a;”,则它必须在别的某个地方包括语句“int a;”。这两个语句可以在同一源文件中,也可以位于程序的不同源文件中。

 

原则:每个外部变量只定义一次。

 

3. 命名冲突与static修饰符

 

两个具有相同名称的外部对象实际上代表的是同一对象,即使coder的本意并非如此,但系统却会如此处理。因此,如果在不同的源文件中都包括了定义“int a;”,那么,它或者表示程序错误(连接器禁止外部变量重复定义),或者在两个源文件中共享a的同一实例(无论是否应该共享)。

 

static修饰符是一个能够减少此类命名冲突的有用工具。修饰外部变量或者函数,则该变量或者函数仅在当前源文件可用。

 

4. 形参、实参与返回值

 

如果任何一个函数在调用它的每个文件中,都在第一次被调用前进行了声明或定义,那么就不会有任何与返回值类型相关的麻烦。

 

如果一个函数在被定义或者声明之前被调用,那么它的返回值类型就默认为整形。

 

函数只能有一个定义,如果函数的调用与定义分别位于不同的文件中,那么我们必须在调用它的文件中声明该函数。(定义一次,声明多次。)

 

如果一个函数没有float、short或者char类型的参数,在函数声明中完全可以省略参数类型的说明(注:函数定义中不能省略参数类型的说明)。

 

5. 头文件

 

每个外部对象只在一个地方声明。这个声明的地方一般就在一个头文件中,需要用到该外部对象的所有模块都应该包括这个头文件。特别需要指出的是:定义该外部对象的模块也应包括这个头文件

 

示例:

定义头文件file.h

                             extern char filename[];

需要用到外部对象filename的每个源文件都应加上如下包含语句:

                             #include “file.h”

最后,我们需要选择一个源文件,在其中给出filename的初始值,不妨称为file.c

                             #include "file.h"

                             char filename[]="/etc/passwd";

抱歉!评论已关闭.