liunx程序编译过程介绍及bug解决

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)

  1. 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,解决,没有出现错误

参考:

    1. http://hahack.com/wiki/c-linker.html
    2. http://bbs.chinaunix.net/thread-2037617-1-1.html
    3. http://laokaddk.blog.51cto.com/368606/489821
时间: 2024-10-09 23:57:58

liunx程序编译过程介绍及bug解决的相关文章

CUDA程序编译过程中产生警告的解决方法

有时候经常使用别人用Tabhost+其它的实现demo.单纯利用Tabhost该如何使用呢? 下面看例子: public class MainActivity extends TabActivity { public TabHost tabHost; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取对象 tabHost = getTabH

C语言程序编译过程

最近在编译DM8168的ARM端程序时经常出现未定义.重定义等报错,由于源码文件多,包含关系比较多,所以自己添加时容易乱.深深的体会到,好的代码风格是如此重要,之前也在看代码重构,以后应该更加注意代码的质量.经思考总结规律如下: 1.公用的数据结构等写为一个头文件,其他源文件包含此头文件.同时为了让不同源文件里的函数都可以使用,公用的函数可以放在此头文件中声明. 2.其他源文件里声明的变量,如果想在另一个文件里用,需要extern声明,这样可以避免各种全局变量的交互混杂. 理解的比较浅,希望高人

李洪强iOS开发之C语言程序编译过程

汇编语言 指令用特定的名字来标记,这就是汇编语言 人比较容易看懂汇编语言 汇编直接和程序一一对应的 有汇编器把程序翻译成机器码 把高级语言编译成计算机识别的语言 程序编译过程 命令行 UNIX 系统中自带了C语言的编译器,编译器的名字叫CC CC 的含义是C Compler Linux系统是一个开源的,它自带的C编译器叫GCC GCC 不仅可以编译C 还可以编译Python 和OC MAC OS 的编译器是苹果公司自己研发的 Clang 在用CC或者GCC 的命令的时候,本质是调用了clang

Linux 程序编译过程的来龙去脉

大家肯定都知道计算机程序设计语言通常分为机器语言.汇编语言和高级语言三类.高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类,一种是编译型语言,例如C,C++,Java,另一种是解释型语言,例如Python.Ruby.MATLAB .JavaScript. 本文将介绍如何将高层的C/C++语言编写的程序转换成为处理器能够执行的二进制代码的过程,包括四个步骤: 预处理(Preprocessing) 编译(Compilati

linux程序编译过程

大家肯定都知道计算机程序设计语言通常分为机器语言.汇编语言和高级语言三类.高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类,一种是编译型语言,例如C,C++,Java,另一种是解释型语言,例如Python.Ruby.MATLAB .JavaScript. 本文将介绍如何将高层的C/C++语言编写的程序转换成为处理器能够执行的二进制代码的过程,包括四个步骤: 预处理(Preprocessing) 编译(Compilati

C程序编译过程浅析【转】

转自:http://blog.csdn.net/koudaidai/article/details/8092647 前几天看了<程序员的自我修养——链接.装载与库>中的第二章“编译和链接”,主要根据其中的内容简单总结一下C程序编译的过程吧. 我现在一般都是用gcc,所以自然以GCC编译hellworld为例,简单总结如下. hello.c源代码如下: ?[Copy to clipboard] C 1 2 3 4 5 6 [c] view plaincopy <span style=&qu

程序编译过程

编译程序(Compiler,compiling program)也称为编译器,是指把用高级程序设计语言书写的源程序,翻译成等价的机器语言格式目标程序的翻译程序.作为一个程序员,我们应该了解它的过程,才能 更好的工作和学习. 编译过程课分为下列几个过程 编译C程序涉及很多多步骤,第一步称之为预处理阶段,C预处理器在源代码编译之前对其进行一些文本性质的操作. 注释的删除 插入#include指令包含的文件内容,定义和替换#include指令定义的符号以及确定代码的部分是否应该根据一些条件编译指令进行

【转】android程序编译过程

现在很多人想对Android工程的编译和打包进行自动化,比如建立每日构建系统.自动生成发布文件等等.这些都需要我们对Android工程的编译和打包有一个深入的理解,至少要知道它的每一步都做了什么,需要什么环境和工具,输入和输出是什么.那么我们就来挖掘一下Android的编译过程中的细节. 首先,我们假定你的系统(什么系统都行,不限于Linux还是Windows系统,当然,我在这里默认使用Linux系统来举例子,但在 Windows中几乎没有什么差别)已经安装了JDK和Android SDK.再假

C程序编译过程浅析

前几天看了<程序员的自我修养——链接.装载与库>中的第二章“编译和链接”,主要根据其中的内容简单总结一下C程序编译的过程吧. 我现在一般都是用gcc,所以自然以GCC编译hellworld为例,简单总结如下. hello.c源代码如下: /* 何问起 hovertree.com */ int main() { printf(“Hello, world.\n”); return 0; } 通常我们使用gcc来生成可执行程序,命令为:gcc hello.c,默认生成可执行文件a.out 其实编译(