赞
踩
一个完整的C语言编译器套件,主要包括以下部分:
使用vim创建一个hello.c文件
保存退出后,使用gcc命令将hello.c编译成可执行程序a.out
# gcc hello.c
# ls
a.out hello.c
# ./a.out
hello world
如果想将源文件编译为一个指定的可执行文件:hello,可以通过 gcc -o 完成
# gcc -o hello hello.c
# ls
hello hello.c
# ./hello
hello world
以hello.c为例:从一个源文件,到生成最后的可执行文件,GCC编译过程的基本流程如下
默认情况下,gcc命令会自动完成上述的整个编译过程。当然,gcc还提供了一系列参数,使用这个参数,可以让用户精准控制每一个编译过程。
-E:只做预处理,不编译
-S:只编译,将源文件编译为汇编文件
-c:只汇编,不链接
-o:指定输出的文件名
如果只对一段C语言程序做预处理操作,而不进行编译,可以通过gcc -E 参数来完成。如下面的一段程序,在程序中分别使用#include包含头文件,使用#define定义宏,使用#ifdef条件编译。
#include <stdio.h>
#define PI 3.14
int main(){
printf("hello world\n");
printf("PI=%f\n", PI);
#ifdef DEBUG
printf("debug mode\n");
#else
printf("release mode\n");
#endif
return 0;
}
对上面的C源程序使用gcc -E进行预处理,就可以生成原汁原味的C程序:
# gcc -E hello.c > hello.i
# ls
hello.c hello.i
# cat hello.i
...
extern int printf (const char *__restrict __format, ...);
...
int main(void){
printf("hello world\n");
printf("PI = %f\n", 3.14);
printf("release mode\n");
return 0;
}
通过预处理后的C程序,使用#include包含的的头文件就地展开,我们可以看到stdio.h头文件中printf函数的声明。程序中使用#define定义的宏PI,也会在实际使用的地方展开为实际的值。使用#ifdef定义的条件编译,会根据条件判断,选择实际要编译的代码分支。
如果只对C源程序做编译处理,不汇编,可以使用gcc -S 参数:会gcc会将C源程序做预处理、编译操作,生成对应的汇编文件,不再做进一步的汇编和链接操作。
# gcc -S hello.c
# ls
hello.c hello.s
当然,我们也可以将上一节经过预处理后的hello.i文件编译为hello.s汇编文件:
# gcc -S hello.i
两者生成的hello.s内容是一样的
如果只想对一个C程序做汇编操作,不进行链接,可以使用gcc -c 来完成:
# gcc -c hello.c
# ls
hello.c hello.o
gcc -c 选项,也可以对上几节生成的 hello.i、hello.s文件直接汇编,生成对应的目标文件:
# gcc -c hello.i
# ls
hello.c hello.i hello.o hello.s
# rm hello.o
# gcc -c hello.s
# ls
hello.c hello.i hello.o hello.s
默认情况下,gcc会将hello.c生成对应的hello.o目标文件。当然,我们也可以通过 -o 输出选项,生成指定的目标文件:
# ls
hello.c
# gcc -o world.o hello.c
# ls
hello.c world.o
Linux 下的静态链接库是以.a结尾的二进制文件,它作为程序的一个模块,在链接期间被组合到程序中。和静态链接库相对的是动态链接库(.so文件),它在程序运行阶段被加载进内存。
Linux 下静态链接库文件的命名规则为:
libxxx.a
首先使用 gcc 命令把源文件编译为目标文件,也即.o文件:
gcc -c 源文件列表
然后使用 ar 命令将.o文件打包成静态链接库,具体格式为:
ar rcs + 静态库文件的名字 + 目标文件列表
ar 是 Linux 的一个备份压缩命令,它可以将多个文件打包成一个备份文件(也叫归档文件),也可以从备份文件中提取成员文件。ar 命令最常见的用法是将目标文件打包为静态链接库。
对参数的说明:
# ar rcs libdemo.a a.o b.o c.o
实例演示
在用户主目录(home 目录)下创建一个文件夹 test,将 test 作为整个项目的基础目录。在 test 目录中再创建四个源文件,分别是 add.c、sub.c、div.c 和 test.h。
add.c 实现两个数相加
#include “test.h”
int add(int a,int b)
{
return a + b;
}
sub.c 实现两个数相减
#include “test.h”
int sub(int a,int b)
{
return a - b;
}
div.c 实现两个函数相除
#include “test.h”
int div(int a,int b)
{
return a / b;
}
还有一个 test.h 头文件,用来声明三个函数
#ifndef __TEST_H_
#define __TEST_H_
int add(int a,int b);
int sub(int a,int b);
int div(int a,int b);
#endif
接下来,我们就将以上代码制作成静态链接库。
首先将所有源文件都编译成目标文件:
# gcc -c *.c
然后把所有目标文件打包成静态库文件:
# ar rcs libtest.a *.o
使用静态链接库时,除了需要库文件本身,还需要对应的头文件:库文件包含了真正的函数代码,也即函数定义部分;头文件包含了函数的调用方法,也即函数声明部分。
为了使用上面生成的静态链接库 libtest.a,我们需要启用一个新的项目。在用户主目录(home 目录)中再创建一个文件夹 math,将 math 作为新项目的基础目录。
在比较规范的项目目录中,lib 文件夹一般用来存放库文件,include 文件夹一般用来存放头文件,src 文件夹一般用来存放源文件,bin 文件夹一般用来存放可执行文件。为了规范,我们将前面生成的 libtest.a 放到 math 目录下的 lib 文件夹,将 test.h 放到 math 目录下的 include 文件夹。
在 math 目录下再创建一个 src 文件夹,在 src 中再创建一个 main.c 源文件。
此时 math 目录中文件结构如下所示:
|-- include
| `-- test.h
|-- lib
| `-- libtest.a
`-- src
`-- main.c
在 main.c 中,可以像下面这样使用 libtest.a 中的函数:
#include <stdio.h>
#include "test.h" //必须引入头文件
int main(void)
{
int m, n;
printf("Input two numbers: ");
scanf("%d %d", &m, &n);
printf("%d+%d=%d\n", m, n, add(m, n));
printf("%d-%d=%d\n", m, n, sub(m, n));
printf("%d÷%d=%d\n", m, n, div(m, n));
return 0;
}
在编译 main.c 的时候,我们需要使用-I(大写的字母i)选项指明头文件的包含路径,使用-L选项指明静态库的包含路径,使用-l(小写字母L)选项指明静态库的名字。所以,main.c 的完整编译命令为:
# gcc src/main.c -I include/ -L lib/ -l test -o math.out
注意,使用-l选项指明静态库的名字时,既不需要lib前缀,也不需要.a后缀,只能写 test,GCC 会自动加上前缀和后缀。
打开 math 目录,发现多了一个 math.out 可执行文件,使用./math.out命令就可以运行 math.out 进行数学计算。
静态库里实现的函数,可能被多个应用程序调用,那么在链接时,被调用的这个函数可能就会多次链接到不同的应用程序中。
比如C标准库的printf函数,可能被一个应用程序调用多次,被不同的应用程序调用,当这些应用程序加载到内存运行时,内存中也就存在多个printf函数代码的副本,太浪费了内存空间。而且,对于应用程序来说,每一个调用的库函数都被链接进来,自身的文件体积也会大增。
动态链接跟静态链接相比,具有以下优势
但动态库也有缺点,发布软件时,动态库需要和应用程序一起发布,否则你编译的应用程序到了一个新的平台可能就无法运行。
# gcc -shared -fPIC -o libmymath.so add.c sub.c
其中的参数说明:
将动态链接库拷贝到main.c的同一目录下
# tree
.
├── inc
│ ├── add.h
│ └── sub.h
├── libmymath.so
└── main.c
在编译main.c,链接动态库libmymath.so的时候,直接指定当前目录下的libmymath.so文件:
# gcc main.c ./libmymath.so -I inc/
# ls
a.out inc libmymath.so main.c
在当前目录下运行生成的可执行文件a.out,可以正常运行。将a.out拷贝到其他目录,然后再运行,就会出现错误
Linux的动态链接库一般都放在/lib官方默认目录下。如果想让a.out放在任何路径下都可以运行,我们可以把libmymath.so动态库拷贝到/lib下,然后在编译应用程序时,通过-L参数指定动态链接库的搜索路径。编译生成的a.out在运行时,就会到指定的/lib目录下去加载动态库libmyamth.so:
# cp libmymath.so /lib # tree . ├── inc │ ├── add.h │ └── sub.h ├── libmymath.so └── main.c # gcc main.c -L/lib -lmymath -I inc # tree . ├── a.out ├── inc │ ├── add.h │ └── sub.h ├── libmymath.so └── main.c
将生成的a.out拷贝到系统的任何目录下,都可以正常运行。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。