gcc的全称是GNU Compiler Collection,它是一个可以编译多种语言的编译器。最开始gcc是做为C语言的编译器(GNU C Compiler),如今除了c语言,还支持C++、java、Pascal等语言。gcc支持多种硬件平台。html
gcc编译程序主要通过四个过程:前端
预处理其实是将头文件、宏进行展开。编译阶段,gcc调用不一样语言的编译器,例如c语言调用编译器ccl。gcc其实是个工具链,在编译程序的过程当中调用不一样的工具。汇编阶段,gcc调用汇编器进行汇编。连接过程会将程序所须要的目标文件进行连接成可执行文件。汇编器生成的是可重定位的目标文件,学过操做系统,咱们知道,在源程序中地址是从0开始的,这是一个相对地址,而程序真正在内存中运行时的地址确定不是从0开始的,并且在编写源代码的时候也不能知道程序的绝对地址,因此重定位可以将源代码的代码、变量等定位为内存具体地址。下面以一张图来表示这个过程,注意过程当中文件的后缀变化,编译选项和这些后缀有关。java
这是GCC编译的四个步骤。编程
来看一下gcc经常使用选项bash
选项名 | 做用 |
---|---|
-o | 产生目标(.i、.s、.o、可执行文件等) |
-E | 只运行C预编译器 |
-S | 告诉编译器产生汇编程序文件后中止编译,产生的汇编语言文件拓展名为.s |
-c | 通知gcc取消链接步骤,即编译源码,并在最后生成目标文件 |
-Wall | 使gcc对源文件的代码有问题的地方发出警告 |
-Idir | 将dir目录加入搜索头文件的目录路径 |
-Ldir | 将dir目录加入搜索库的目录路径 |
-llib | 链接lib库 |
-g | 在目标文件中嵌入调试信息,以便gdb之类的调试程序调试 |
如今咱们有源文件hello.c,下面是一些gcc的使用示例:架构
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时能够加上-Wall选项。下面这个例子若是不加上-Wall选项,编译器不会报出任何错误或警告,可是程序的结果却不是预期的:模块化
//bad.c #include<stdio.h> int main() { printf("the number is %f ",5); //程序输出了the number is 0.000000,结果错误 return 0; }
使用-Wall选项:函数
gcc -Wall bad.c -o bad工具
gcc将输出警告信息:操作系统
warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf("the number is %f\n",5);
假设如今有三个文件: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 生成可执行文件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):程序在运行的时候才去连接共享库的代码,多个程序共享使用库的代码,这样就减小了程序的体积。
通常头文件或库文件的位置在:
为了生成.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可以找获得共享库,有下面几种方法:
其中,shared选项表示生成共享库格式。fPIC表示产生位置无关码(position independent code),位置无关码表示它的运行、加载与内存位置无关,能够在任何内存地址进行加载。
库的搜索路径遵循几个搜索原则:从左到右搜索-I -l指定的目录,若是在这些目录中找不到,那么gcc会从由环境 变量指定的目录进行查找。头文件的环境变量是C_INCLUDE_PATH,库的环境变量是LIBRARY_PATH.若是仍是找不到,那么会从系统指定指定的目录进行搜索。