程序中的头文件包含两种情况∶
A)#include<>
B)#include “myinc.h”
其中,A类使用尖括号(< >),B类使用双引号(“ ”)。对于A类,预处理程序cpp在系统预设包含文件目录(如/usr/include)中搜寻相应的文件,而对于B类,cpp在当前目录中搜寻头文件,这个选项的作用是告诉cpp,如果在当前目录中没有找到需要的文件,就到指定的dirname目录中去寻找。在程序设计中,如果我们需要的这种包含文件分别分布在不同的目录中,就需要逐个使用-I选项给出搜索路径。
综上可见:
● 组成一个程序的每个源文件通过编译过程分别转换为目标代码。
● 每个目标文件由链接器捆绑在一起,同时引入标准C函数库中任何被该程序所用到的函数,当然也可以搜索到个人工作目录路径下的库函数。
程序在运行过程中要经过两个环境:翻译环境,执行环境。
翻译环境:源代码转换为机器指令。
执行环境:用于实际执行代码。
编译具体步骤如下:
先来看一下gcc的使用方法和常用选项
提示:gcc --help
Ⅰ、使用方法:
gcc [选项] 文件名
Ⅱ、常用选项:
选项 含义
-v 查看gcc编译器的版本,显示gcc执行时的详细过程
-o
-E Preprocess only; do not compile, assemble or link;只预处理,不会编译、汇编、链接
-S Compile only; do not assemble or link;只编译,不会汇编、链接
-c Compile and assemble, but do not link; 编译和汇编,不会链接
一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)、和连接(linking)才能变成可执行文件。
gcc -E -o hello.i hello.c
预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些代码输出到一个“.i”文件中等待进一步处理。
用户可以使用gcc的选项”-E”进行查看。
gcc -S -o hello.s hello.i
编译就是把C/C++代码(比如上面的".i"文件)“翻译”成汇编代码。Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc把代码翻译成汇编语言。用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
gcc -c -o hello.o hello.s
汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现位ELF目标文件(OBJ文件)。
gcc -o hello hello.o
链接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,最终生成可以在特定平台运行的可执行程序。(arm,x86)
在这里涉及到一个重要的概念:函数库。
读者可以重新查看这个小程序,在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf” 了,而这也就是链接的作用。
你可以用ldd命令查看动态库加载情况:
[root]# ldd hello.exe
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。gcc在编译时默认使用动态库。
在编译过程中。除非使用了"-c",“-S”,或"-E"选项(或者编译错误阻止了完整的过程),否则统一完整链接步骤。
譬如:gcc hello.c
和gcc -o hello hello.c
都已经完成链接操作
gcc -c -o hello.o hello.c
不作最后一步链接,得到hello.o二进制OBJ文件
gcc -v -o hello hello.o
我们来看一样链接过程是怎样的:
行。 动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行。
动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。 动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行。
默认使用动态链接:
gcc -o hello_shared hello.o
就是在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时连接的目的。优点是多个程序可以共享同一段代码,而不需要在磁盘上存储多个拷贝,缺点是由于是运行时加载,可能会影响程序的前期执行性能。
优点
节省内存并减少页面交换
dll文件与exe文件独立,只需要保证接口不变,更换dll文件对exe无影响,极大地提高可维护性和可拓展性
不同的编译语言可以用同一个dll文件
适用于大规模的软件开发,是的开发过程独立,耦合度小,便于不同开发者和开发组织者之间进行开发和测试。
缺点
如果程序依赖的dll文件不存在,载入动态链接时,程序会终止并给出错误提示,运行时动态链接程序会加载失败
速度慢
静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过静态链接生成的程序体积较大。
gcc -static -o hello_static hello.o
就是在编译链接时直接将需要的执行代码拷贝到调用处,优点就是在程序发布的时候就不需要的依赖库,也就是不再需要带着库一块发布,程序可以独立执行,但是体积可能会相对大一些。
优点
代码装载速度快,执行速度比动态链接快
只需要保证开发者的计算机有正确的lib文件
缺点
生成可执行文件较大,可能包含相同的代码拷贝
多次调用一个外部的函数(dll文件orlib文件),静态链接可能会有多份拷贝,动态链接只有一份拷贝
静态链接的话lib文件中的指令会被包含到exe执行文件中,动态链接的话dll文件不必包含在exe执行文件中,exe可以随时动态的卸载和引用这个dll文件。
静态链接库不能再包含其他动态链接库和静态库,而在动态链接库中可以再包含其他动态链接库和静态库。
程序执行过程:
● 程序必须载入内存中。在有操作系统的环境中:一般此过程由操作系统完成。独立环境中,程序载入必须手工完成,也可能通过可执行代码置入只读内存来完成。
● 程序的执行便开始。接着调用main()函数。
● 开始执行程序代码。这个时候程序将使用一个运行时堆栈,存储函数的局部变量,函数参数,返回数据和返回的地址。同时也可以使用静态内存,存储于静态内存中的变量在程序的整个运行过程中一直保留它们的值。
● 终止程序。正常终止main函数,也可能会中途意外终止。
另外,需要指出的是,make和cmake主要命令只有一条,make用于处理makefile,cmake用来转译CMakeLists.txt,而qmake是一个体系,用于支撑一个编程环境,它还包含除qmake之外的其它多条命令(比如uic,rcc,moc)。
交叉编译(或交叉建立)是这样一种过程,它在一种机器结构下编译的软件将在另一种完全不同的机器结构下执行。一个常见的例子是在 PC 机上为运行在基于 ARM、PowerPC或 MIPS 的目标机的编译软件。幸运的是,GCC 使得这一过程所面临的困难要比听起来小得多。
GCC 中的一般工具通常都是通过在命令行上调用命令(如 gcc)来执行的。在使用交叉编译的情况下,这些工具将根据它编译的目标而命名。例如,要使用交叉工具链为 ARM 机器编译简单的 Hello World 程序,你可以运行如下所示的命令:使用如下命令编译并测试这个代码: arm-linux-gcc -o hello hello.c。
arm-linux-gcc
arm-linux-gcc 是基于 ARM 目标机的交叉编译软件, arm-linux-gcc 跟 GCC 所需的安装包不同,但仅仅是名字不同而已,这是为什么呢?
x86 跟 ARM 所使用的指令集是不一样的,所以所需要的 binutils 肯定不一样;上面提到过 gcc-core 是依赖于 binutils 的,自然 ARM 跟 x86 所使用的 gcc-core 包也不一样;glibc 一个 c 库,最终是以库的形式存在于编译器中,自然 ARM 所使用的 glibc 库跟 x86 同样也不一样,其它的依此类推。
arm-elf-gcc
arm-elf-gcc 跟 arm-linux-gcc 一样,也是是基于 ARM 目标机的交叉编译软件。但是它们不是同一个交叉编译软件,两者是有区别的,两者区别主要在于使用不同的 C 库文件。arm-linux-gcc 使用 GNU 的 Glibc,而 arm-elf-gcc 一般使用 uClibc/uC-libc 或者使用 RedHat专门为嵌入式系统的开发的C库newlib。只是所应用的领域不同而已,Glibc是针对PC开发的,uClibc/uC-libc是与Glibc API兼容的小型化C语言库,实现了Glibc部分功能。
arm-linux-*针对运行linux的ARM机器,其依赖于指定的C语言库Glibc,因为同样使用Glibc的linux而使得arm-linux-*在运行linux的ARM机器上编译显得更加和谐。
arm-elf-*则是一个独立的编译体系,不依赖于指定的C语言库Glibc,可以使用newlib等其他C语言库,不要求操作系统支持,当其使用为嵌入式系统而设计的一些轻巧的C语言库时编译裸机程序(没有linux等大型操作系统的程序),如监控程序,bootloader等能使得系统程序更加小巧快捷。
1 | Configure(配置编译环境) |
2 | cmake = qmake(生成makefile) |
3 | make (从Makefile中读取指令,然后编译。) |
4 | make install(从Makefile中读取指令,安装程序) |
我们知道编译和链接阶段是靠g++和gcc编辑器来完成,但是如果编译和链接的阶段如果源文件太多,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile。
///////////////////////////////////////////////详细过程///////////////////////////////////////////////////////////////////
1.gcc是GNU Compiler Collection(就是GNU编译器套件),也可以简单认为是编译器,它可以编译很多种编程语言(括C、C++、Objective-C、Fortran、Java等等)。
2.当你的程序只有一个源文件时,直接就可以用gcc命令编译它。
3.但是当你的程序包含很多个源文件时,用gcc命令逐个去编译时,你就很容易混乱而且工作量大
4.所以出现了make工具
make工具可以看成是一个智能的批处理工具,它本身并没有编译和链接的功能,而是用类似于批处理的方式—通过调用makefile文件中用户指定的命令来进行编译和链接的。
5.makefile是什么?简单的说就像一首歌的乐谱,make工具就像指挥家,指挥家根据乐谱指挥整个乐团怎么样演奏,make工具就根据makefile中的命令进行编译和链接的。
6.makefile命令中就包含了调用gcc(也可以是别的编译器)去编译某个源文件的命令。
7.makefile在一些简单的工程完全可以人工手下,但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改。
8.这时候就出现了Cmake这个工具,cmake就可以更加简单的生成makefile文件给上面那个make用。当然cmake还有其他功能,就是可以跨平台生成对应平台能用的makefile,你不用再自己去修改了。
9.可是cmake根据什么生成makefile呢?它又要根据一个叫CMakeLists.txt文件(学名:组态档)去生成makefile。
10.到最后CMakeLists.txt文件谁写啊?亲,是你自己手写的。
11.当然如果你用IDE,类似VS这些一般它都能帮你弄好了,你只需要按一下那个三角形
12.cmake是make maker,生成各种可以直接控制编译过程的控制器的配置文件,比如makefile、各种IDE的配置文件。
13.make是一个简单的通过文件时间戳控制自动过程、处理依赖关系的软件,这个自动过程可以是编译一个项目。
/////////////////////////linux平台下的编译流程////////////////////
文本程序到可执行文件生成无论在什么平台大致分为以下几个部分:
1.用编辑器编写源代码,如.c文件。
2.用编译器编译代码生成目标文件,如.o。
3.用链接器连接目标代码生成可执行文件,如.exe。
Linux平台下,.o文件一般是通过编译的但还未链接的目标文件,.out文件一般都是经过相应的链接产生的可执行文件(linux下)。当然这是一般情况下人们这么设置,而真正的,在linux中 .o通常保存的是可执行代码 ,至于可执行文件则没有规定扩展名,用的是文件属性位来决定的是否可执行。在chmod中设置。
我们知道编译和链接阶段是靠g++和gcc编辑器来完成,这两个编译阶段是相同的,但是链接阶段g++默认链接c++库,所以一般情况下用gcc编译c文件,而g++编译cpp文件。当然g++也可以编译c文件,而gcc编译cpp文件则需要在后面加上参数-lstdc++,作用就是链接c++库。
但是如果编译和链接的阶段如果源文件太多,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具,它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile,所以编写makefile文件也是一个程序员所必备的技能。
对于一个大工程,编写makefile实在是件复杂的事,于是人们又想,为什么不设计一个工具,读入所有源文件之后,自动生成makefile呢,于是就出现了cmake工具,它能够输出各种各样的makefile或者project文件,从而帮助程序员减轻负担。但是随之而来也就是编写cmakelist文件,它是cmake所依据的规则。所以在编程的世界里没有捷径可走,还是要脚踏实地的。
原文件—cmakelist —cmake —makefile —make —生成可执行文件(make中则包含了多条链接以及gcc/g++编译语句)。