简析
一般来说,C++程序会分为 头文件和源代码文件。
C++鼓励我们将组件函数放在单独的文件中,这就意味着会存在多个源代码文件,C++鼓励我们对这些源代码文件单独编译,然后将这些文件的编译版本链接。
单独编译后在链接的意义在于:如果我们要对一个源文件修改,我们就可以只对这个源文件编译,然后和其他文件的编译版本链接。
- 比如下面这个程序,有一个有文件coordin.h,两个源代码文件 file1.cpp和file2.cpp
coordin.h
|
file1.cpp
|
file2.cpp
|
可以看出来,coordin.h里面就是函数原型和结构体声明,file1.cpp 和 file2.cpp里就是函数的定义。
这个程序就是把直角坐标化成极坐标。
使用命令行生成可执行文件,是非常简单的
g++ file1.cpp file2.cpp -o myfile.exe |
g++帮你完成了很多工作,包括单独编译然后将目标代码文件,库代码和启动代码合并,并生成可执行文件.
注意:file1.cpp中没有include"file2.cpp",可见这两个源代码文件确实是单独编译的。
预处理
g++ -E .\file1.cpp -o file1.i |
预处理后得到file1.i 文件。
预处理做的事情有:
- 展开所有的宏定义并删除 #define
- 处理所有的条件编译指令,例如 #if #else #endif #ifndef …
- 把所有的 #include 替换为头文件实际内容,递归进行
- 删除所有的注释行
- 添加行号和文件名标识以供编译器使用
- 保留所有的 #pragma 指令,因为编译器要使用
file1.i 文件里面东西非常多,因为他把
<iostream>
的代码加进来了,然后我们在文件最后面,发现有coordin.h和file1.cpp中的代码。
编译
g++ -S .\file1.i -o file1.s |
file1.s 里面是x86-64汇编代码.
汇编
g++ -c .\file1.s -o file1.o |
汇编阶段是把 汇编代码变成机器码,当然用记事本看肯定是乱码,得用专门的二进制文件查看器。
链接
如果我们直接用file1.o生成可执行文件,发现会出错:
这是因为file1.cpp中用到的函数的定义是在 file2.cpp中的。
所以同样的方法我们现生成file2.cpp的.o文件 file2.o
我们可以把file2.o 先生成静态库文件,然后再链接。(当然也可以两个 .o 文件链接)
ar -rcs libfile2.a file2.o |
可以对比一下 静态库文件libfile2.a 和file2.o文件,我们发现库文件就是.o文件前面加了一些代码(索引)
最后我们把file1.o和libfile2.a链接一下,生成可执行文件。
g++ -g .\file1.o .\libfile2.a -o myfile.exe |
单独编译
一般来说,C++程序会分为 头文件和源代码文件。
C++鼓励我们将组件函数放在单独的文件中,这就意味着会存在多个源代码文件,C++鼓励我们对这些源代码文件单独编译,然后将这些文件的编译版本链接。
单独编译后在链接的意义在于:如果我们要对一个源文件修改,我们就可以只对这个源文件编译,然后和其他文件的编译版本链接。
头文件
头文件是.h文件
头文件中常包含的内容有:
- 函数原型
- 宏 和 const变量
- 结构声明
- 类声明
- 模板声明
- 内联函数
有些程序员喜欢在头文件中使用include<>
或include""
,为了避免同一个头文件被include多次,就会有:
这个代码的意思是,在include这个头文件的时候,如果没有导入过这个头文件,那就导入。
源代码文件
源代码文件是.cpp文件
源代码文件会有函数的定义和main函数等。
在源代码文件中 一般都会使用include<>
或include""
,可以导入.h文件,也可以导入.cpp文件
如何用命令行编译多个源文件?
g++ 参数列表.
-
-g 编译带调试信息的可执行文件
-g 选项告诉 GCC 产生能被 GNU 调试器GDB使用的调试信息,以调试程序。
产生带调试信息的可执行文件test
g++ -g test.cpp
-
-O[n] 优化源代码
所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,这些操作会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。
-O 同时减小代码的长度和执行时间,其效果等价于-O1
-O0 表示不做优化
-O1 为默认优化
-O2 除了完成-O1的优化之外,还进行一些额外的调整工作,如指令调整等。
-O3 则包括循环展开和其他一些与处理特性相关的优化工作。
g++ -O2 test.cpp
-
-l(小写L)指定库文件和 -L | 指定库文件路径
-l参数(小写)就是用来指定程序要链接的库,-l参数紧接着就是库名
在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接
链接glog库g++ -lglog test.cpp
如果库文件没放在上面三个目录里,需要使用-L参数(大写)指定库文件所在目录
-L参数跟着的是库文件所在的目录名
链接mytest库,libmytest.so在/home/bing/mytestlibfolder目录下
g++ -L/home/bing/mytestlibfolder -lmytest test.cpp
-
-I (大写i)指定头文件搜索目录
/usr/include目录一般是不用指定的,gcc知道去那里找,但 是如果头文件不在/usr/icnclude里我们就要用-I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上
-I/myinclude
参数了,如果不加你会得到一个”xxxx.h: No such file or directory
的错误。-I参数可以用相对路径,比如头文件在当前目录,可以用-I.
来指定。
g++ -I/myinclude test.cpp
-
-Wall 打印警告信息
打印出gcc提供的警告信息
g++ -Wall test.cpp
-
-w 关闭警告信息
关闭所有警告信息
g++ -w test.cpp
-
-std=c++11 设置编译标准
使用 c++11 标准编译 test.cpp
g++ -std=c++11 test.cpp
-
-o 指定输出文件名
指定即将产生的文件名
指定输出可执行文件名为test
g++ test.cpp -o test
-
-D 定义宏
-D 参数在实际开发中还是经常使用的,比如可通过定义不同的宏,实现选择性编译,编译时定义不同的宏就编译相对应的代码。
using namespace std;
int main(){
cout<<"hello ";
cout<<"world!"<<endl;
}g++ -D DEBUG -o test ./test.cpp
执行test.exe
后得到的是hello world!
总结来说,
g++ -L 库文件目录 -l 库文件 -I 头文件搜索目录 -o 指定输出名 1.cpp 2.cpp
例如,file1.cpp放在.\中,file2.cpp放在.\b\中,头文件在.\include
则我们编译这两个文件可以使用
g++ -o mytest -I .\include\ .\file1.cpp .\b\file2.cpp
具体的单独编译细节且看我写的这篇文章:
C++_从源码到可执行文件vscode中如何设置单独编译,只需修改
tasks.json
中的args:[]
参数就行了
"args": [ //方括号里是传给gcc的参数 |
也可以参照其他博主写的这篇
征服VS Code(1):C/C++单文件编译