主要内容:
- 编译程序的过程:preprocessor、compiler、assembler、linker、loader
- Makefile
- 类的Makefile
- 其他方式:.h & .cpp
一、编译器compiler,汇编器assembler,连接器linker,加载器loader[1]
1、编译程序compiling program
- 当我们在ubuntu中敲入g++ Myfile.cpp –o Myfile –std=c++11的时候,4个实体涉及到了编译过程:preprocessor,compiler,assembler,linker
-
- 首先,预处理器cpp将所有的宏定义和include语句进行扩展,然后将结果传递给真正的编译器。这一步生成的结果是C code。如果我们的代码中没有预处理声明,其实不必运行预处理器。
- 然后,编译器(compiler)能够将预处理过的C code翻译成汇编代码(assembly code),进行各种优化和寄存器分配。
- 然后,汇编器(assembler)将汇编代码翻译为机器代码(machine code),又称为目标文件(object file)。
- 目标文件和其他的目标文件&libraries链接,通过链接器linker给所有的引用分配内存地址。
- 例子,在ubuntu中生成code1.cpp文件,通过emacs编译
#include
回到ubuntu中,利用g++编译可执行的程序
g++ code1.cpp -o code1 -std=c++11
- 整体的流程如下
二、拆分code1.cpp
- 为了能够讲解整个流程,将code1.cpp拆分为3个文件
- add.h + add.cpp + code1.cpp
- add.h——定义成员函数
- add.h + add.cpp + code1.cpp
#include
-
-
- add.cpp——成员函数的细节
-
// file:add.cpp
-
-
- code1.cpp——主函数
-
#include
-
- 如何将code1.cpp变成code1.o?
- 首先,分别编译(-c=compile)两个cpp文件,生成目标文件(.o)
- 如何将code1.cpp变成code1.o?
g++ -c code1.cpp
g++ -c add.cpp
-
-
- 然后,对两个目标文件进行链接(linking)
-
g++ add.o code1.o -o code1
-
-
- 执行最后的程序
-
./code1
三、Makefile
- 上述文件的依存关系
- 那么,我们能够通过Makefile,一步实现所有的步骤
- 第一步,在ubuntu中创建Makefile,并且编辑
touch Makefile
emacs Makefile
-
- 进入emacs中编辑Makefile
- Makefile编辑如下。注意:每一句的第二行,前面的空格是按一下Tab键,千万不是随机的空格键。否则编译不通过。
add.o : add.h add.cpp //依存关系
g++ -c add.cpp //ubuntu中的运行代码,下同
code1.o : add.h code1.cpp
g++ -c code1.cpp
code1 : add.o code1.o
g++ code1.o add.o -o code1
clean :
rm -f add.o code1.o code1
Makefile中的编辑,实际上就是我们列表的依存关系(第一行) + ubuntu中一步一步运行(第二行)的结果。
-
- 在ubuntu中输入,make,最后就得到了main文件。直接运行./main。
- make clean——删除中间文件。
三、类的Makefile
例如,我定义一个复数的类。
首先,建立3个文件,分别是①complex.hpp(存放类的框架);②complex.cpp(存放类的具体的定义);③main.cpp(存放调用这个类函数的主函数)
danielshen@DESKTOP-VOSNAQJ:~$ touch complex.hpp
danielshen@DESKTOP-VOSNAQJ:~$ touch complex.cpp
danielshen@DESKTOP-VOSNAQJ:~$ touch main.cpp
再依次编辑;
danielshen@DESKTOP-VOSNAQJ:~$ emacs complex.hpp
danielshen@DESKTOP-VOSNAQJ:~$ emacs complex.cpp
danielshen@DESKTOP-VOSNAQJ:~$ emacs main.cpp
第一个complex.hpp中的编辑如下:
#include
第二个complex.cpp编辑如下:
//file: complex.cpp
第三个主函数编辑如下:
#include
然后,在ubuntu中创建一个Makefile
danielshen@DESKTOP-VOSNAQJ:~$ touch Makefile
danielshen@DESKTOP-VOSNAQJ:~$ emacs Makefile
文件之间的关系如表
更新代码时,我们主要依照关系,更细相关文件。
Makefile编辑如下。注意:每一句的第二行,前面的空格是按一下Tab键,千万不是随机的空格键。否则编译不通过。
main
然后在ubuntu中运行:
danielshen@DESKTOP-VOSNAQJ:~$ make
结果是:
g++ -c complex.cpp
g++ -c main.cpp
g++ complex.o main.o -o main
运行主函数
danielshen@DESKTOP-VOSNAQJ:~$ ./main
结果是
Destroying : 4 + (6)i.
c1 = 1+1i
c2 + c3 = 4+6i
Destroying : 4 + (6)i.
Destroying : 3 + (4)i.
Destroying : 1 + (2)i.
Destroying : 1 + (1)i.
可以查看当前文件夹中的文件
danielshen@DESKTOP-VOSNAQJ:~$ ls
有
Makefile complex.cpp complex.hpp complex.o main.cpp main.o
Makefile~ complex.cpp~ complex.hpp~ main main.cpp~
我们可以clean一下
danielshen@DESKTOP-VOSNAQJ:~$ make clean
运行结果为
rm -f complex.o main.o main
再看文件夹中的文件:
danielshen@DESKTOP-VOSNAQJ:~$ ls
只剩下
Makefile complex.cpp complex.hpp main.cpp
Makefile~ complex.cpp~ complex.hpp~ main.cpp~
因此,这个例子可以看出来,类的模板在hpp中定义,类具体在cpp中定义,调用时在main函数中声明.hpp文件即可。同时搞清楚Makefile中不同文件之间的关联性。
四、.h和.cpp在一个文件夹中
1、#ifndef #define #endif[2][3]
- 经常看到这三个符号在文件的开头和结尾部分,作用:
- 统称为#include guards。
- 大型软件工程中,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行的文件的时候,就会出现大量“重定义”的错误;从而避免头文件的重定义。
- 一旦headers被included,那么将会检查“唯一值”是否定义(唯一值就是#ifndef后面跟着的大写字符串)。如果没有定义,那么将会定义,并继续剩下的部分。防止对任何标识符(例如类型、枚举、静态变量)进行双重声明。
- 例子:
首先在ubuntu中生成3个文件,分别是"p.h" "c.h" "main.cpp",然后通过emacs以及进入编辑。
p.h,定义坐标类
#ifndef POINT_H
c.h定义城市名+坐标类:
#ifndef CREATURE_H
主函数main.cpp
#include
然后在ubuntu中,输入
g++ main.cpp -o main -std=c++11
就可以调用两个类、同时运行主函数。
参考
- ^https://courses.cs.washington.edu/courses/cse378/97au/help/compilation.html
- ^https://stackoverflow.com/questions/1653958/why-are-ifndef-and-define-used-in-c-header-files
- ^https://www.cnblogs.com/xuepei/p/4027946.html