1. 首先来看下一般的liunx中C程序具体的编译过程
参考网站http://blog.csdn.net/gengyichao/article/details/6544266上的图
将编写的一个.c文件(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译阶段和链接这两个主要阶段:
1) 编译阶段先通过“编译器”把一个.c 文件编译成 .s的汇编文件;再经过“汇编器”把这个.s的汇编代码汇编成.o 目标文件;
注:把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后产生相应的汇编代码文件,是程序构建的核心部分,也是最复杂的部分之一。汇编:将汇编代码根据指令对照表转变成机器可以执行的指令,一个汇编语句一般对应一条机器指令;
2)“链接器”通过连接其他 .o的库文件和1)中的.o 目标代码生成可执行文件。
该文件流被这三种“器”的加工,分别表现出四种形式,可以总结为三“器”四“文件”,这就是c程序的编译和链接过程。
特别地,编译器在将源文件编译成汇编文件的过程又分为:预处理阶段处理源代码中的以”#”开始的预编译指令,如”#include”、”#define”等(生成.i代码文件)和优化阶段许多IDE和编译器将编译和链接的过程合并在一起,称为构建(Build)
- 2. 接下来具体看下在编译和连接的时涉及到的具体文件名称
基本文件
.c 未经过预处理的C源码
.h C头文件
.i 经过预处理的C源码
.s 生成的汇编语言代码
.o 编译之后产生的目标文件,相当于windows中的.obj文件
库文件
.a 为静态库文件
.so 为共享库
解释:*.c一般使我们自己编辑的代码,使我们劳动的结晶;*.h一般是我们手工生成的接口文件,如果愿意,也可在*.c完成后用GCC的选项-aux-info帮我们生成;*.i是经过预处理后的生成文件,是由GCC在选项-E编译下自动生成的文件;*.o是编译后产生的目标文件;*.s是GCC在选项-S编译下生成的汇编语言代码,对于性能要求很高的程序可以先生成汇编语言文件并对汇编做优化,然后用优化后的汇编生成目标文件并链接
以server.c 为例举例几个文件的gcc产生过程
生成server.i
$ gcc -E server.c -o server.i
生成汇编语言文件 server.s
$ gcc -s server.i -o server.s
生成目标文件 server.o
$ gcc -c server.i
$ gcc -c server.s
生成可执行文件
$ gcc -o server server.o
运行及结果
$ ./server
直接生成
$ gcc -c server.c
3. Liunx编程中非常重要的库文件介绍
有时候需要把一组代码编译成一个库,这个库在很多项目中都要用到,例如libc就是这样一个库,我们在不同的程序中都会用到libc中的库函数(例如printf),也会用到libc中的变量(例如以后要讲到的environ变量)。使用GNU的工具我们如何在Linux下创建自己的程序函数库,一个“程序
函数库”简单的说就是一个文件包含了一些编译好的代码和数据,这些编译好的代码和数据可以在事后供其他的程序使用。程序函数库可以使整个程序更加模块化,更容易重新编译,而且更方便升级。程序函数库可分为3种类型:静态函
数库(static libraries)、共享函数库(shared libraries)和动态加载函数库(dynamically loaded libraries)。
1)共享库
.so 为共享库,全名shared
object,用于动态连接的,和dll差不多共享库以.so结尾. (so == share object) 在程序的链接时候并不像静态库那样在拷贝使用函数的代码,而只是作些标记。然后在程序开始启动运行的时候,动态地加载所需模块。所以,应用程序在运行的时候仍然需要共享库的支持。
共享库链接出来的文件比静态库要小得多。
.la为libtool自动生成的一些共享库,vi编辑查看,主要记录了一些配置信息。
如果你要编写的共享函数库支持所有有用的特性,你在编写的过程中必须遵循一系列约定。你必须理解库的不同的名字间的区别,例如它的
“soname”和“real name”之间的区别和它们是如何相互作用的。你同样还要知道你应该把这些库函数放在你文件系统的什么位置等等。下面我们具体看看这些问题。 每个共享函数库都有个特殊的名字,称作“soname”。Soname名字命名必须以“lib”作为前缀,然后是函数库的名字,然后是“.so”,最后是版本号信息。不过有个特例,就是非常底层的C库函数都不是以lib开头这样命名的。 每个共享函数库都有一个真正的名字(“real name”),它是包含真正库函数代码的文件。真名有一个主版本号,和一个发行版本号。最后一个发行版本号是可选的,可以没有。主版本号和发行版本号使你可以知道你到底是安装了什么版本的库函数。
另外,还有一个名字是编译器编译的时候需要的函数库的名字,这个名字就是简单的soname名字,而不包含任何版本号信息。
2)静态库
.a为静态库文件表示Archive,是好多个.o目标文件合在一起,用于静态连接,静态库在程序链接的时候使用,链接器会将程序中使用到函数的代码从库文件中拷贝到应用程序中。一旦链接完成,在执行程序的时候就不需要静态库了。由于每个使用静态库的应用程序都需要拷贝所用函数的代码,所以静态链接的文件会比较大。
静态函数库实际上就是简单的一个普通的目标文件的集合,一般来说习惯用“.a”作为文件的后缀。可以用ar这个程序来产生静态函数库文件。Ar 是archiver的缩写。静态函数库现在已经不在像以前用得那么多了,主要是共享函数库与之相比较有很多的优势的原因。慢慢地,大家都喜欢使用共享函数
库了。不过,在一些场所静态函数库仍然在使用,一来是保持一些与以前某些程序的兼容,二来它描述起来也比较简单。 静态库函数允许程序
员把程序link起来而不用重新编译代码,节省了重新编译代码的时间。不过,在今天这么快速的计算机面前,一般的程序的重新编译也花费不了多少时间,所以
这个优势已经不是像它以前那么明显了。静态函数库对开发者来说还是很有用的,例如你想把自己提供的函数给别人使用,但是又想对函数的源代码进行保密,你就
可以给别人提供一个静态函数库文件。理论上说,使用ELF格式的静态库函数生成的代码可以比使用共享函数库(或者动态函数
库)的程序运行速度上快一些,大概1-5%。 创建一个静态函数库文件,或者往一个已经存在地静态函数库文件添加新的目标代码,可以用下面的命令:
ar rcs my_library.a file1.o file2.o
一旦
你创建了一个静态函数库,你可以使用它了。你可以把它作为你编译和连接过程中的一部分用来生成你的可执行代码。如果你用gcc来编译产生可
执行代码的话,你可以用“-l”参数来指定这个库函数。你也可以用ld来做,使用它的“-l”和“-L”参数选项。
3)动态加载库(dynamically loaded (DL) libraries)
动态加载库(dynamically loaded (DL) libraries)是指在程序运行过程中可以加载的函数库。而不是像共享库一样在程序启动的时候加载。DL对于实现插件和模块非常有用,因为他们可以让程序在允许时等待插件的加载。在Linux中,动态库的文件格式跟共享库没有区别,主要区别在于共享库是运行时加载。
静态函数库是在程序执行前就加入到目标程序中去了;而共享函数库则是在程序启动的时候加载到程序中,它可以被
不同的程序共享;动态加载函数库则可以在程序运行的任何时候动态的加载。实际上,动态函数库并非另外一种库函数格式,区别是动态加载函数库是如何被程序员
使用的。后面我们将举例说明。
4.最后扒一扒在使用“yar.h”时出现的“undefined reference to”
首先,代码很简单就是包含“yar.h”头文件
Gcc编译连接有错误
很明显,yar.h中并没有link到其他的.c文件上,看下yar.h
使用命令
-L.表示将当前目录加入到库搜索路径。默认的库搜索路径在/usr/lib目录下。另外这里说明一下易混淆的参数-I, 它表示搜索头文件的路径。这样gcc在查找头文件的时候会首先到-I指定的目录查找,然后才是系统默认目录
-l参数: -lname表示库搜索目录下的libname.a
或者libname.so文件 , 这也是为什么库文件都以lib开头的原因之一。一个惯例嘛。当然了,如果你的库文件不是libserver,而是server. 那就不能用-l参数编译了。
Ok,解决,没有出现错误
参考:
- http://hahack.com/wiki/c-linker.html
- http://bbs.chinaunix.net/thread-2037617-1-1.html
- http://laokaddk.blog.51cto.com/368606/489821