天天看点

《C陷阱与缺陷》 第四章 连接

4.1  什么是连接器

C语言中的一个重要思想: 分别编译,然后整合到一起。

典型的连接器把有编译器或汇编器生成的若干目标模块,整合成一个被称为载入模块或可执行模块的实体。

连接器通常把目标模块看成是由一组外部对象组成的。所谓外部对象,指的是每个没有声明为static的函数或者外部变量。

连接器的重要工作是处理外部对象的命名冲突。

连接器的另一个工作是对其他模块中的外部对象的引用,比如调用printf时,该引用指向一个位于某个库文件中的外部对象。在连接器生成载入模块的过程中,必须同时记录这些外部对象的引用。

4.2 声明与定义

一个外部变量只能够定义一次,可以声明多次。

int a;  当第一次出现,是个定义,赋值为0(VS2005上是0)。其他情况下出现应该视作声明。

int a = 0;是一个定义。如果之前有同样的全局变量a,则命名重复。

extern int a; 是一个声明。表明a的定义在这之前的别处。

4.3命名冲突与static修饰符

为了避免同名冲突,在只在一个文件中使用的全局变量或全局函数,最好用static修饰符修饰,使其作用域限定在本文件内。

4.4形参,实参与返回值

这一节前面一长串讲的一些感觉没有什么东西,无非是些形参,实参,返回值的默认转换。最后讲了一个有趣的程序:

#include <stdio.h>
int main()
{
int i;
char c;
for(i = 0; i<5; i++)
{
scanf("%d", &c);
printf("%d", i);
}
printf("\n");
return 0;
}
           

乍看没啥问题,但是c是一个char,而scanf每次传入是个整型,这样,就可能将值赋到i,从而影响整个循环。

4.5 检查外部类型

介绍了这样一种情况:假定一个文件包含对n的声明:extern int n;

另一个文件包含外部变量n的定义 long n;

这样的程序编译器可能查不出错误,甚至有的能够顺利运行,但是不建议这么做。

4.6 头文件

一个简单而好的规则:每个外部对象只在一个地方声明,而且就在头文件中,所有用到该外部对象的模块都包含这个头文件。这样,就能很好解决4.5章中的问题了。

继续阅读