当一个c或cpp文件在编译时,预处理器首先递归包含头文件,形成一个含有所有必要信息的单个源文件,这个源文件就是一个编译单元。
C++中的编译单元编译单元,指的是代码的物理组织形式。根据C++标准,每一个cpp 文件就是一个编译单元。
编译器不会去编译 .h
或者 .hpp
文件;
编译器只会编译 .c
或 .cpp
文件;
简单来说,当一个c或cpp文件在编译时,预处理器首先递归包含头文件,这也就是为什么常会有:#ifndef……#define……#endif。之后,形成一个含有所有必要信息的单个源文件,这个源文件就是一个编译单元。这个编译单元会被编译成为一个与cpp文件名同名的目标文件 。
编译器不能检查跨越目标文件或编译单元之间的名称冲突,这是链接器的工作。链接器把不同编译单元中产生的符号联系起来,构成一个可执行程序。如:
//文件first.cpp int integerValue = 0; int main(){ int integerValue = 0; return 0; }; //文件second.cpp int integerValue = 0; /* 错误: error LNK2005: "int integerValue" (?integerValue@@3HA) 已经在 second.obj 中定义 first.obj */ GCC将C++代码转为机器码,理论上需要四个步骤:预处理(preprocessing)、编译(compilation)、汇编(assembly)以及链接(linking)3;四个步骤对应四个主体:预处理器(preprocessor)、编译器(compiler)、汇编器(assembler)以及链接器(linker)。实际预处理与编译其实是一个步骤,共需要三个步骤:预处理&编译、汇编以及链接。
Java中的编译单元当编写一个Java源代码文件时,此文件以.java结尾,被称为编译单元。
1、 编译单元中可以有一个public类,且只能有一个public类,作为外界访问该类的接口,该类的名称必须与文件名称一样。
2、 编译单元中可以没有public类,但必须有一个类名称与文件名称相同。
3、 编译单元中可以有一些额外的类,这些类在包访问权限的1。
代码组织
1、当编译(javac)一个.java文件时,在该编译单元(即.java文件)中的每个类都会有一个输出文件.class文件,每个输出文件的名称与.java文件中每个类的名称相同。
2、Java可运行程序是一组可以打包并压缩为一个Java文档文件(JAR文件)的.class文件。Java解释器负责这些文件的查找、装载和解释。
Java解释器运行过程
1、 找出环境变量CLASSPATH(通过操作系统设置,也可不用设置,一般编译环境会为你设置),CLASSPATH包含一个或多个目录,用来查找.class文件的根目录。
2、 从根目录开始,解释器获取包的名称并将每个句点替换成\
3、 得到的路径与CLASSPATH中的各个不同的项相连接,解释器就在这些目录中查找与你创建的类名称相关的.class文件2。
在多个编译单元中如何定义常量(C++)【方法一】: 在某个公用的头文件中直接在某个名字空间中或者全局名字空间中定义符号常量并初始化(有无static)无所谓,例如:
// CommonDef.h
const int MAX_LENGTH=1024;
然后每一个使用它的编译单元#include改头文件即可
【方法二】: 在某个公用头文件中并且在某个名字空间中或者全局名字空间中将符号常量声明为extern的,例如:
//CommonDef.h
extern const int MAX_LENGTH;
并且在某个源文件中定义一次并初始化:
const int MAX_LENGTH=1024;
然后每一个使用它的编译单元#include上述头文件即可。
两种方法的比较
优点:
方法一:
维护方便
方法二:
(1)节约存储,每一个编译单元访问都是这个唯一的定义。
(2)修改初值后只需重新编译定义所在编译单元即可,影响面很小。
缺点:
方法一:
(1)如果修改常量初值,则将影响多个编译单元,所有受影响的编译单元必须重新编译。
(2)每一个符号常量在每一个包含了它们的编译单元内都存在一份独立的拷贝内容,每个编译单元访问的就是各自的拷贝内容,因此浪费存储空间。
方法二:
如果要改变初值,要改变源文件3。
本词条内容贡献者为:
宋春霖 - 副教授 - 江南大学