[debug] dbms lab (1) 文件架构相关的背景知识——链接和多文件结构

链接问题

一个大一些的工程往往不是只有一个程序文件,经常由好多C程序文件构成,有的时候里面个别程序可能还用的其他语言,编码完成后常常分别编译,编译完成再link到一起。这就需要我们了解文件链接的问题。

外部链接

某个C程序需要用到其他程序中定义过的变量,一般都加extern前缀,编译时编译器会预留访问链接的空位,等到link阶段再在整个工程的其他C编译结果中去对号,把访问链接填上。这就是外部链接。如果你程序全写在一个文件里,那永远都不会有外部链接。

内部链接

内部链接常指一个程序文件中全局变量,可以被程序文件内各个子程序访问,这在编译过程中处理,和link阶段不发生关系。如果变量前加了static,那么它永远不会被外部程序访问,它不会被编译程序写入目标代码的链接区。

无链接

无链接,就是在一个单体程序里,比如一个子程序,定义一个变量只给这个程序段用,那就是无链接。编译器和link都不需要对这样的变量做跨程序段的地址链接,这样的变量都是直接分配寄存器或者近堆中的直接地址(每个子程序都有自己的基本存储空间,被调用时得到分配,返回时被释放,我习惯叫它近堆,标准叫啥早不记得了)。
变量是这样,程序代码段也大体差不多。每次在程序文件中调用一个文件内部的子程序,就产生一个内7a686964616fe4b893e5b19e31333365653765部链接;如果调用外部文件中的子程序,就产生一个外部链接。只有没有任何子程序,所有代码都写在一个文件里的程序,才是无链接程序。

多文件结构

C++中,源程序要被翻译成可执行文件,都要经过三个步骤:预处理、编译和链接。

预处理

阅读源程序,执行预处理指令,嵌入指定源文件。预处理指令以“#”号开始。如#include指令实现文件包含。当一个.cpp文件编译前,它首先递归地包含头文件,形成一个含有所有必要信息的单个源文件,也就是一个翻译单元。

编译

编译器每次翻译一个.cpp文件(翻译单元),并输出对象文件(.o或.obj)。对象文件含有.cpp文件内定义的所有函数编译后的机器码,也包含.cpp文件内定义的全局变量和静态变量。此外,对象文件也可能含有未定义引用,这些未定义的引用就是该翻译单元内有声明,但是在这个.cpp文件中没有定义的函数和全局变量。

那么,这些没有定义的东西在哪?答案是这些东西定义在其他.cpp文件中。
要怎么找到呢?这就是链接器的任务了。

链接

有外部链接的定义可以在对象文件中产生外部符号,这些外部符号可以被所有其他的翻译单元访问,用来解析他们未定义的引用。链接器的工作就是读取所有对象文件,并尝试解决对象文件之间的交差引用。

如果成功,则产生可执行程序。当无法解决外部引用的时候,根据情况链接器有两种报错:

  1. 当找不到引用的目标时,就会产生“无法解决的外部符号”错误。
  2. 当找到两个或以上相同名字的实体(函数或变量时),就会产生“符号被多重定义”错误。

因此,要让程序正确地链接:

  1. 不能声明一个实体,却没有相应的定义。比如在A.cpp里面声明一个void fun();但在这个文件和其他文件中都没有这个函数的定义(也就是函数的实现),这就会产生“无法解决的外部符号”错误。
  2. 同时,也不能重复地定义具有外部链接性的实体。比如,在A.cpp文件里面定义int x,同时在B.cpp里面又定义一个int x。这样就会出现“符号被多重定义”。

头文件

了解了上面几点知识,我们就可以理解和回答一些问题了。

(1)为什么不要把外部链接的定义放在头文件里面?
因为我们知道cpp在预编译的时候会递归包含头文件,因此,如果一个头文件包含了一个外部链接的定义,其他包含它的.cpp文件都会有一个相同的外部链接的定义。出现“符号被多重定义“也就不难理解了。要特别注意的是,类的静态成员变量和一般的静态变量不一样,它具有外部链接性,因此假设你在头文件中定义一个:

class A
{
static int member;
}

那么该静态成员变量的定义不能放在这个头文件里面。而是应该在某个.cpp文件里面写定义: int A::member;

(2)有内部链接性的定义可以放到头文件中去吗?
要让内部链接的定义影响程序的其他部分,可以把它放到头文件中,这样包含这个头文件的其他文件都知道了这个定义。但不推荐在头文件中定义const或者static常量,因为会污染全局命名空间(这里我们或许可以采用匿名空间的方式进行封装),同时在每个包含头文件的翻译单元中浪费了数据空间。由于声明可以看做是内部链接,因此我们可以把声明放在头文件中的时候,例如在头文件中放 void fun(); 所有包含这个头文件的其他文件都可以使用fun()函数了。

(3)为什么我们经常在头文件中定义类,却在cpp文件中定义类成员函数?
因为类定义是内部链接性,而类的成员函数的定义是外部链接性的。

(4)可不可以在一个头文件中包含其他所有的头文件,然后其他cpp文件都include这个头文件?
不要这样,因为这样的话,每此更改这个头文件,都会引起其他的所有cpp文件的重新编译,严重影响编译速度,同时也浪费了数据空间。

以上部分参考内容的版权声明:
————————————————
版权声明:本文为CSDN博主「牧行」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/muxing_csdn/article/details/51078080

根据以上的内容,我将文件结构确定如下:

main.cpp

ui.cpp
ui.hpp

interpreter.cpp
interpreter.hpp

in_out.cpp
in_out.hpp

exception.cpp
exception.hpp
time_cnt.cpp
time_cnt.hpp

调用关系如图:
从图中其实不难看出,不重要的组件反而占据了相对靠“中心”的位置。
核心的流是左边这一竖列。
[debug] dbms lab (1) 文件架构相关的背景知识——链接和多文件结构_第1张图片

makefile问题

繁琐程度逐级递增。

  1. 直接在main.cppinclude相关.cpp文件
  2. tasks.json中添加相关.cpp文件
  3. 编写makefile文件

你可能感兴趣的:(C/C++)