1、基本编译命令
- 首先准备三组文件:add.h/add.cpp、sub.h/sub.cpp、mul.h/mul.cpp;.h中声明,.cpp中进行实现,然后再main.cpp中引入这三个头
#ifndef INC_01_ADD_H
#define INC_01_ADD_H
int add(int a, int b);
#endif
#include "add.h"
int add(int a, int b)
{
return a + b;
}
sub.h/sub.cpp、mul.h/mul.cpp略
- 再在main.cpp中引入这个三个头文件
#include <iostream>
#include "add.h"
#include "sub.h"
#include "mul.h"
int main() {
int a = 2, b = 3;
printf("a + b = %d\n", add(a, b));
printf("a - b = %d\n", sub(a, b));
printf("a * b = %d\n", mul(a, b));
return 0;
}
- 然后执行编译命令
g++ add.cpp sub.cpp mul.cpp main.cpp -o main.out
- 最后执行main.out文件
./main.out
可以看到最后的输出结果是正确的,通过联合编译生成最后我们需要的结果。
2、Makefile优化
2.1、将命令写入makefile文件
main.out:
g++ add.cpp sub.cpp mul.cpp main.cpp -o main.out
- 最后每次执行make就能生成出main.out,但是这样写不好!因为我们把所有的命令都写死了。
- 一旦需要更改其中某一个文件那么需要将所有的文件都重新进行编译,太过于死板不够灵活
2.2、分文件编译
main.out: main.o sub.o add.o mul.o
g++ add.o sub.o mul.o main.cpp -o main.out
add.o:add.cpp
g++ -c add.cpp -o add.o
sub.o:sub.cpp
g++ -c sub.cpp -o sub.o
mul.o:mul.cpp
g++ -c mul.cpp -o mul.o
-
这种写法相对于上文写法要好一点,make在执行时会对比上一次的编译。
-
make通过观察文件有没有新的改动,如果没有新的改动将不会重新编译,如果有改动只重新编译改动的文件!
-
可以通过修改其中一个文件的代码来测试,例如把add.cpp中的+号加法改成*号乘法
- 可以看到第一次编译所有的文件都编译:add、sub、mul、main
- 改动完add.cpp后只编译了add.cpp的文件
- 最后再次执行什么都没有做
3、编译流程解析
gcc、g++、c++编译执行的流程大致分为四个步骤:预处理、编译、汇编、连接
- 预处理:展开宏、头文件、替换条件编译、删除注释、空行空白等,主要检查和删除一些不必要的东西。
- 使用-E指令将hello.c文件生成hello.i文件
- 编译:检查语法规范然后编译成汇编语言,这也就是为什么如果写错变量或者语法错误会过不去编译就卡在这里。
- 使用-S指令将hello.i文件进行编译生成hello.S的汇编指令文件
- 汇编:将编译生成的汇编指令翻译成二进制的机器指令
- 使用-c指令将汇编指令翻译成二进制机器指令hello.o文件
- 连接:数据段合并、地址回填
- 最后不需要加入任何参数直接生成可执行的目标文件,一般命名为xxx.out 或者 xxx文件
这里需要注意任何命令都是向前兼容的,例如直接使用-S指令表示预处理 + 编译,而使用-c表示预处理 + 编译 + 汇编!
- 常用的编译指令集合
-I:指定头文件所在目录位置
-E:预处理
-S:编译,得到汇编指令集
-c:制作预处理、编译、汇编。得到二进制文件
-g:编译时添加调试语句, 支持gdb调试
-Wall:显示所有警告信息
-Errorwarning:把所有的警告当做报错处理,一般是程序交付使用
-D:向程序动态植入宏定义
-l:指定动态库库名
-L:指定动态库路径
-o:修改得到的文件名