GCC编译程序
包括四个过程:
- 预处理(pre—processing)
- 编译(compiling)
- 汇编(assembling)
- 链接(linking)
预处理:将头文件和宏定义进行展开
编译阶段:gcc调用不同语言的编译器,例如C语言调用的编译器ccl,编译程序的过程中gcc会调用不同的工具
汇编阶段:gcc调用汇编器进行汇编,汇编器可生成可重定位的目标文件,重定位能够将源代码的代码、变量等定位为内存的具体地址
链接阶段: 将程序所需要的目标文件进行连接成可执行文件
GCC常用选项
选项名 | 作用 |
---|---|
-o | 产生目标(.i、.s、.o、可执行文件等) |
-E | 只运行C预编译器 |
-S | 告诉编译器产生汇编程序文件后停止编译,产生的汇编语言文件拓展名为.s |
-c | 通知gcc取消连接步骤,即编译源码,并在最后生成目标文件 |
-Wall | 使gcc对源文件的代码有问题的地方发出警告 |
-Idir | 将dir目录加入搜索头文件的目录路径 |
-Ldir | 将dir目录加入搜索库的目录路径 |
-llib | 连接lib库 |
-g | 在目标文件中嵌入调试信息,以便gdb之类的调试程序调试 |
下面通过简单例子进行操作说明:
我们有源文件hello.c
gcc -E hello.c -o hello.i 对hello.c文件进行预处理,生成hello.i文件
gcc -s hello.i -o hello.s 对预处理文件进行编译,生成汇编文件
gcc -c hello.s -o hello.o 对汇编文件进行编译,生成目标文件
gcc hello.o -o hello 对目标文件进行链接,生成可执行文件
gcc hello.c -o hello 直接编译链接成可执行的目标文件
gcc -c hello.c 或 gcc -c hello.c -o hello.o 编译生成可重定位的目标文件
GCC编译多个文件
假设有三个文件,分别是hello.c、hello.h、main.c
/*
hello.c
*/
#include <stdio.h>
#include "hello.h"
void printHello(){
printf("Hello World!\n");
}
/*
main.c
*/
#include <stdio.h>
#include "hello.h"
int main(){
printHello();
return 0;
}
/*
hello.h
*/
#ifndef _HELLO_
#define _HELLO_
void printHello();
#endif
编译上面三个文件,可以一次执行:
gcc hello.c main.c -o main
也可以单独编译,单独编译的好处是当其中某一个模块发送改变时,只需要编译该模块就行,不需要重新编译全部的文件,这样可以节省编译的时间。
gcc -Wall -c main.c -o main.o
gcc -Wall -c hello.c -o hello.o
gcc -Wall main.o hello.o -o main
外部库的使用
在使用C语言和其他语言进行程序设计的时候,我们需要头文件来提供对常数的定义和对系统及库函数调用的声明。库文件是一些预先编译好的函数集合,那些函数都是按照可重用原则编写的。它们通常由一组互相关联的可重用原则编写的,它们通常由一组互相关联的用来完成某项常见工作的函数构成。使用库的优点在于:
- 模块化的开发
- 可重用性
- 可维护性
库又可以分为静态库与动态库:
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。静态库比较占用磁盘空间,而且程序不可以共享静态库。运行时也是比较占内存的,因为每个程序都包含了一份静态库。
- 动态库(.so或.sa):程序在运行的时候才去链接共享库的代码,多个程序共享使用库的代码,这样就减少了程序的体积。
一般头文件或库文件的位置在:
- /usr/include及其子目录底下的include文件夹
- /usr/local/include及其子目录底下的include文件夹
- /usr/lib
- /usr/local/lib
- /lib
静态库的生成
为了生成.a文件,我们需要先生成.o文件。下面这行命令将我们的hello.o打包成静态库libhello.a:
ar rcs libhello.a hello.o
ar是gun归档工具,rcs表示replace and create,如果libhello之前存在,将创建新的libhello.a并将其替换。
然后就可以这样来使用静态库libhello.a
gcc -Wall main.c libhello.a -o main
还有另外一种使用方式:
gcc -Wall -L. main.c -o main -lhello 【lhello 是 libhello的缩写】
其中 -L表示库文件的位置在当前目录下,由于libhello.a是我们自己生成的,并存放在当前录下下,所以需要加上-L.选项。默认库文件是在系统的目录下进行搜索。同样的,-I.选项用于头文件的搜索。
共享库的生成
生成一个共享库,名称的规则是libxxx.so。将刚才hello.o生成libhello.so的命令为:
gcc -shared -fPIC hello.o -o libhello.so
生成了共享库之后,可以这样来使用共享库:
gcc -Wall main.o -o main -L. -lhello
该命令与使用静态库的命令相同,但是在共享库与静态库共存的情况下,优先使用共享库。
共享库有时候并不不在当前的目录下,为了让gcc能够找得到共享库,有下面几种方法:
- 拷贝.so文件到系统共享库路径下,一般指/usr/lib
- 在~/.bash_profile文件中,配置LD_LIBRARY_PATH变量
- 配置/etc/ld.so.conf,配置完成后调用ldconfig更新ld.so.cache
其中,shared选项表示生成共享库格式。fPIC表示产生位置无关码(position independent code),位置无关码表示它的运行、加载与内存位置无关,可以在任何内存地址进行加载。
库的搜索路径
库的搜索路径遵循几个搜索原则:从左到右搜索-I -l指定的目录,如果在这些目录中找不到,那么gcc会从由环境 变量指定的目录进行查找。头文件的环境变量是C_INCLUDE_PATH,库的环境变量是LIBRARY_PATH.如果还是找不到,那么会从系统指定指定的目录进行搜索。