@TOC
一 . 背景介绍
GCC — GNU Compiler Collection : GUN 编译器集合,它可以编译C、C++、JAV、Fortran、Pascal、Object-C、Ada等语言
而对于gcc 和g++ 而言, 实际上它们就像驱动程序, 用来调用相应的编译器,
- gcc 调用 GCC 中的GUN C Compiler, 也就是C 编译器 (显示中是 cc1)
- g++调用 GCC 中的GUN C++ Compiler,也就是C++ 编译器(显示中是 cc1plus)
二 . gcc /g++的区别
1 . 调用的编译器不同 ,传递给编译器的参数不同
2 . 对于 .c和.cpp文件, gcc分别当成C文件和Cpp文件进行编译(调用cc1或cc1plus编译器)
3 . 对于 .c和.cpp文件, g++统一当作Cpp文件进行编译(调用cc1plus编译器)
4 . 使用g++进行编译时, 会默认链接C++的 STL标准库
5 . 使用gcc对*.cpp文件进行编译时, 并不会默认链接STL标准库, 但是可以通过参数 –lstdc++ 来强制链接STL标准库 (但是这并不意味着, 它与g++等价)
参考如下:
三 . 使用gcc/g++编译文件的过程
- Step1:Call a preprocessor – 调用预处理器
- Step2:Call an actual compiler – 调用实际的编译器
- Step3:Call an assembler – 调用汇编程序
- Step4:Call a linker – 调用链接器
1> 预处理阶段
调用相应的预处理器, 例如 C PreProcesser 对源代码进行预处理 (.i文件)
什么是预处理指令:
- 预处理器指令是在编译代码之前执行的一系列指令。 它们不是C语言的一部分,而是由C编译器提供的特殊功能。 预处理器指令通常用于定义常量、包含头文件、条件编译等。
- 头文件展开 :
- 对于语句 预处理命令#include<…> 将相应的头文件从源文件的当前行开始拷贝, 并删除#include<…>
- 也就是用 头文件的全部内容替换#include<…>
- 宏替换 :
- 对于 预处理命令#define 这种宏 , 进行替换
- 实际上进行的就是 文本替换
- 删除注释 :
- 将源文件中的注释全部删除
- 条件编译 :
- #ifdef、#ifndef、#else、#endif: 这些指令进行条件编译
- #pragma:设置编译器选项或行为。
补充 : 可以使用指令 gcc -E file.c -o file.i 形成相应的预处理文件 , 后缀默认为 .i
参考如下 :
2> 编译阶段
调用实际的编译器, (例如cc1)将预处理后的源代码转换为汇编代码(.s文件)
- 词法分析
- 语法和语义分析
- 符号汇总 :
- 汇总全局符号(包括:全局变量名、全局函数名、main等)
- 源代码转换为汇编代码 :
- 可以使用指令 gcc -S file.i -o file.s 形成相应的编译文件 后缀默认为 .s
3> 汇编阶段
调用相应的汇编程序 例如 as, 把汇编代码变成 ELF 格式的二进制 (.o 文件)
- 汇编代码转换为 机器代码
- 该阶段将汇编代码文件转换为机器码文件,即将汇编语言代码转换为机器语言代码。 在这个阶段,汇编器会将汇编代码转换为机器指令,生成一个目标文件。
- 该目标文件全称为 可重定位目标文件 (二进制文件)
- 形成符号表
- 对编译阶段汇总的符号为其分配相应的地址, 形成符号表
4> 链接阶段
调用相应的链接器, 例如 ld, 把 ELF 格式的二进制 link 在一起, 形成可执行程序
- 段合并
- 将相应段进行合并
- 符号表合并
- 将各个.o文件的符号表合并, 合并后的符号表中的符号均对应其绝对地址
- 链接库文件和库函数
- 链接相应的库文件,
- 形成可执行程序
- 一个或多个目标文件形成一个可执行程序
另外,链接分为动态链接与静态链接
- 静态链接 :
- 在链接阶段, 将相应库文件的全部代码拷贝至目标文件(.o文件)中, 这被称为静态链接。
- 静态链接所形成的可执行程序在运行时不再依赖于库文件。
- 动态链接 :
- 在连接阶段,将相应库函数的地址拷贝至目标文件(.o)中, 这被称为动态连接。
- 动态连接所形成的可执行程序在运行时会根据该地址从相应动态库中调用该函数, 因此动态连接形成的可执行程序每次运行都需要库文件的支持。
四 . 再谈动静态库
Linux下库文件名的后缀
- 动态库 : .so
- 静态库 : .a
- 一般来说在Linux中所有库文件都以 lib 开头, 例如 C标准库 libc-2.17.so
Windows下库文件名的后缀
- 动态库 : .dll
- 静态库 : .lib
查看可执行程序所链接的库文件
- ldd filename
- 该指令可查看可执行程序所链接的库文件
补充 : 该网址包含部分动态态库知识
五 . C 头文件与库文件的区别
1> 头文件与库文件简介
- 头文件 : 声明和定义
- 函数的声明等
- 结构体的定义等
- 库文件 : 已经编译好的二进制代码
- 动态库 : 动态链接形成的可执行程序在运行时需要该库文件的支持
- 静态库 : 静态链接形成的可执行程序可以脱离该库文件独立运行
2> 编译过程中与头文件或库文件相关报错
- undefined reference to ‘xxx’
- 找不到库文件
- /usr/bin/ld:cannot find -lxxx.
- 找不到库文件
- xxx.h:No such file or directory
- 找不到头文件
具体了解该过程可参考 头文件与库文件的区别
3> 补充
- 库文件通过头文件向外导出接口, 用户通过头文件找到库文件
- 库文件为已编译好的二进制代码, 头文件为源代码
- 头文件可视, 库文件不可视