dfsdf
一. 编译C程序
1. 后缀名及涵义。
.a 静态对象库(文档).c 需要预处理的C 语言源代码
.h C 语言源代码的头文件
.i 无需预处理的C 语言源代码。该类文件是编译过程的中间产物
.o 目标文件,格式和应用的连接相符。该类文件是编译过程的中间产物
.s 汇编语言代码(assembly language code )。该类文件是编译过程的中间产物
.so 共享对象库(shared object library)
2. gcc的默认编译(单源程序到可执行程序)
通过检查命令行中的文件名后缀,编译程序可以确定所编译的是 C 语言源文件。GCC
采取的默认动作是将源文件编译为目标文件,然后将目标文件连接到可执行文件,最后再
删除目标文件。由于该命令没有指定生成的可执行文件的名字,所以编译程序会用当前目
录下的默认文件名a.out。在命令行中键入该程序名,会运行该程序并显示结果。
$ a.out
hello, world
3. gcc 的 -c选项(源文件到目标文件)
选项-c 会明确指示GCC去编译源代码,但不在硬盘上留下目标文件,且跳过将目标文
件连接到可执行程序这一步。这种情况下的默认输出文件名和输入文件名一样,但以.o 作
后缀。例如,下面的命令会生成目标文件helloworld.o:
$ gcc -c helloworld.c
选项-o 可用于修改生成的目标文件名。下面的命令会生成目标文件,名为 harumph.o:
$ gcc -c helloworld.c -o harumph.o
在构造目标库的过程中,或在创建目标文件集合的时候,其中的目标文件可用于之后
的连接操作,有条命令可由多个源文件创建出目标文件。下面的命令会创建目标文件
arglist.o、ponder.o 和listsort.o :
$ gcc -c arglist.c ponder.c listsort.c
4. 多源文件到可执行文件。
GCC编译程序可以自动处理连接,甚至在编译多个源文件的时候也可以自动连接。例
如,下面保存在文件hellomain.c 中的源代码,调用函数 sayhello():
/* hellomain.c */
void sayhello(void);
int main(int argc,char *argv[])
{
sayhello();
return(0);
}
而以下源代码保存在文件sayhello.c中,定义了函数sayhello() :
/* sayhello.c */
#include stdio.h
void sayhello()
{
printf("hello, world\n");
}
下面的命令将两个程序编译成一个目标文件,并将它们连接到一个可执行程序 hello
上,最后删掉目标文件:
$ gcc hellomain.c sayhello.c -o hello
5. 预处理以及汇编
$ gcc -E helloworld.c -o helloworld.i (-E为预处理选项)
$ gcc -S helloworld.c (-S为汇编选项)
二. 静态库与动态库
1. 静态库
静态库是一些.o 文件的集合,它们是由编译程序按照通常的方式生成的。将程序连接
到库中的目标文件和将程序连接到目录中的目标文件是一样的。静态库的另一个名字是文
档(archive ),而管理这些文档内容的工具叫做ar 。
要构造一个库,最基本的一步是要编译库中的目标模块。例如,以下两个源文件名为
hellofirst.c 和hellosecond.c:
/* hellofirst.c */
#include <stdio.h>
void hellofirst()
{
printf("The first hello\n");
}
/* hellosecond.c */
#include <stdio.h>
void hellosecond()
{
printf("The second hello\n");
}
可用下面命令将这两个源文件编译成目标文件:
$ gcc -c hellofirst.c hellosecond.c
使用工具ar 的-r 选项,可以创建新的库,并会将目标文件插入其中。选项-r 会创建库,
如果库中不存在所命名的目标模块,就会将它加入文档(如果必要,也会替换原来的目标
模块)。下面的命令创建名为 libhello.a 的库,其中包含本例的两个目标模块:
$ ar -r libhello.a hellofirst.o hellosecond.o
现在该库已经完成,可以使用了。下面的程序twohellos.c 会调用新库中的这两个函数:
/* twohellos.c */
void hellofirst(void);
void hellosecond(void);
int main(int argc,char *argv[])
{
hellofirst();
hellosecond();
return(0);
}
如下所示,通过在命令行中指定库,用一条命令就能够编译并连接程序twohellos :
$ gcc twohellos.c libhello.a -o twohellos
静态库的命名习惯是以字母lib 开头,以后缀.a 结束。所有系统库都使用这种命名规则,
并且允许通过选项-l (ell),在命令行中使用库名的缩写形式。下面命令和前面命令的惟一
区别在于期望gcc 对库进行查找的位置有所不同:
$ gcc twohellos.c -lhello -o twohellos (缩写形式)
明确指出完全路径名会令编译程序在所指路径中查找库。库名既可指明绝对路径(例
如 /usr/worklibs/libhello.a ),也可以是到当前目录的相对路径(例如../lib/ libhello.a )。选项
-l 不能指明路径(-L可指明路径),但是可以指示编译程序在系统库中进行查找。
2. 共享库(动态库)
共享库是目标文件的集合,但是这些目标文件是由编译程序按照特殊方式生成的。对
象模块的每个地址(变量引用和函数调用)都是相对地址,不是绝对地址。因此允许在运
行程序的时候,可以动态加载和执行共享模块。所以最后使用的时候比静态库小得多。
构造共享库最基本的一步是编译库中的对象模块。例如,下面的两个源文件名为
shellofirst.c 和shellosecond.c :
/* shellofirst.c */
#include <stdio.h>
void shellofirst()
{
printf("The first hello from a shared library\n");
}
/* shellosecond.c */
#include <stdio.h>
void shellosecond()
{
printf("The second hello from a shared library\n");
}
使用下列命令可将这两个源文件编译成一个目标文件:
$ gcc -c -fpic shellofirst.c shellosecond.c
选项-c 明确指出编译程序要生成的.o 目标文件。而选项-fpic 使得输出的对象模块是
按照可重定位地址(relocatable addressing )方式生成的。缩写pic代表位置独立代码(position
independent code)。
下面的gcc 命令使用目标文件构造共享库hello.so:
$ gcc -shared shellofirst.o shellosecond.o -o hello.so
选项-o 为输出文件命名,而后缀.so 告诉 GCC该目标文件是要连接到共享库的。通常,
连接程序要为主函数main()定位,并用它作为程序的入口,但输出模块没有这样的入口,
选项-shared 对防止出错信息是必要的。
编译程序将后缀为.c 的文件看作是 C 语言源代码程序,并且知道如何将它编译成目标
文件。因此,可以将前面的两个命令合成一个命令,用下面的命令可以将模块直接编译并
保存为共享库:
$ gcc -fpic -shared shellofirst.c shellosecond.c -o hello.so
下面是文件stwohellos.c 中的程序,是主函数,它调用了共享库中的两个函数:
/* stwohellos.c */
void shellofirst(void);
void shellosecond(void);
int main(int argc,char *argv[])
{
shellofirst();
shellosecond();
return(0);
}
可以使用下列命令将该程序编译并连接到共享库:
$ gcc stwohellos.c hello.so -o stwohellos
程序stwohellos 已经可以运行了,但要运行程序必须也要能够定位共享库 hello.so,因
为库中的函数必须在运行的时候载入。