学习笔记 之 链接详解

来源:互联网 发布:通信系统仿真软件 编辑:程序博客网 时间:2024/06/05 19:37

<!-- [if gte mso 9]><![endif]-->

学习linx C一站式学习的一点笔记

一、定义和声明

externstatic关键字

实例的使用,下面的讨论基于以下两个实例。


 

 

main.c编译

 

由于编译器在处理函数调用代码时没有找到函数原型,只好根据函数调用代码做隐式声明,把这三个函数声明为

int push(char);

int pop(void);

int is_empty(void);

 

1为什么编译器在处理函数调用代码时需要有函数原型?

因为必须知道参数的类型和个数以及返回值的类型才知道生成什么样的指令。

2为什么隐式声明靠不住呢?

因为 隐式声明是从函数调用代码推导而来的,而事实上函数定义的形参类型可能跟函数调用代码传的实参类型并不一致,如果函数定义带有可变参数(例如printf),那么从函数调用代码也看不出来这个函数带有可变参数,另外,从函数调用代码也看不出来返回值应该是什么类型,所以隐式声明只能规定返回值都是int型的。

3既然隐式声明靠不住,那编译器为什么不自己去找函数定义,而非要让我们在调用之前写函数原型呢?

因为编译器往往不知道去哪里找函数定义,像上面的例子,我让编译器编译main.c,而这几个函数的定义却在stack.c里,编译器又怎么会知道呢?所以编译器只能通过隐式声明来猜测函数原型,这种猜测往往会出错。利用extern来连接。


 

extern关键字表示这个标识符具有External Linkagemain.cstack.c中都有声明(在stack.c 中的声明同时也是定义),那么这些声明指的是同一个函数,链接之后是同一个GLOBAL符号,代表同一个地址

3如果用static关键字修饰一个函数声明,则表示该标识符具有Internal Linkage

<!-- [if gte mso 9]><![endif]-->

虽然在foo.c中定义了函数foo,但这个函数只具有 Internal Linkage,只有在 foo.c 中多次声明才表示同一个函数,而在 main.c 中声明就不表示它了。如果把 foo.c 编译成目标文件,函数名 foo 在其中是一个 LOCAL 的符号,不参与链接过程,所以在链接时, main.c 中用到一个 External Linkage foo 函数,链接器却找不到它的定义在哪儿,无法确定它的地址,也就无法做符号解析,只好报错。凡是被多次声明的变量或函数,必须有且只有一个声明是定义,如果有多个定义,或者一个定义都没有,链接器就无法完成链接。

注意,变量声明和函数声明有一点不同,函数声明的extern可写可不写,而变量声明如果不写extern意思就完全变了。若一个文件的变量不想被extern可将变量设置成static类似于C++中的private。

 

二、头文件

假如foo.c和main.c中都需要使用stack.c中定义的函数,若在main 和foo中都要写三个函数的声明。若更多的模块需要,则写函数声明比较麻烦,采用头文件。例如上例定义stack.h头文件。

 

 

则再需要的模块中,包含其头文件就可以了。在stack.h中我们又看到两个新的预处理指示#ifndef STACK_H和#endif,意思是说,如果STACK_H这个宏没有定义过,那么从#ifndef到#endif之间的代码就包含在预处理的输出结果中,否则这一段代码就不出现在预处理的输出结果中。stack.h这个头文件的内容整个被#ifndef和#endif括起来了,如果在包含这个头文件时STACK_H这个宏已经定义过了,则相当于这个头文件里什么都没有,包含了一个空文件。

三、静态库

静态库是以.a作为后缀。生成静态库的命令是ar,对于上例使用的命令为:

ar rs libstack.a stack.o 

参数命令解释:r表示将后面的文件列表添加到文件包,如果文件包不存在就创建它,如果文件包中已有同名文件就换成新的。s是专用于生成静态库的,表示为静态库创建索引,这个索引被链接器使用。

在链接静态库时,链接器会把静态库中的目标文件取出来和可执行文件真正链接在一起。而动态库是在运行是做动态链接。