Skip to content

Latest commit

 

History

History
123 lines (90 loc) · 4.68 KB

08.rst

File metadata and controls

123 lines (90 loc) · 4.68 KB
Authors: Kenneth Lee
Version: 0.1
Date: 2022-09-29
Status: Draft

解释型和编译型程序

这一章我们独立解释一下解释型和编译型程序的区别。

程序是给计算机下命令。在第一章里面我们就解释了:计算机只能听懂“汇编指令”。如果 你想给它高级指令,那么你就需要人给它做翻译。

这种翻译有两种办法,一种是,你说一句,它翻译一句,马上告诉计算机要干什么。基于 这个逻辑写出来的程序,就叫“解释型程序”,典型的代表是:Shell脚本,Python等。翻译 型程序则不一样,它是把你整个程序全部翻译成汇编指令,然后把整个汇编指令告诉CPU, 这样的程序,就叫编译型程序,也叫“翻译型程序”。

比如下面这个Python程序::

.. figure:: _static/python_exec.svg

你的源代码怎么写的,它一句一句解释,每解释一句,执行一句,解释不了,就报错。

所以,下面这个Python程序是可以正常运行的::

print("1000+2000=", 1000+2000)
我也不知道我在干什么()

因为解释第一句的时候,当场就执行了,所以,你其实是可以看得到1000+2000的那个结果 的,只是第二句话的意思是调用“我也不知道我在干什么”函数,这句话解释不了,它才报 错了。

但C++是不同的,C++首先会翻译成汇编,比如下面这个程序:

所以,如果翻译不对,程序一句话都不会执行,如果你在C++里面一样调用一个“我也不知 道我在干什么”,那么编译器直接就会说,你错了,你也看不见前面的执行结果。

正因为这样,解释型的程序的程序写的顺序,和执行的顺序是完全一样的,但编译型的程 序的顺序是可以规定的,对于C/C++来说,就是从main开始,在main的最后结束,中间是否 经过其他函数,只看你的main里面有没有调用它。

也正因为如此,在C++里面,你是可以把程序写成这样的::

#include <iostream>

using namespace std;

int add(int a, int b); // 这句话是不用翻译的,只是给编译器的一个提醒,告诉它后面如果提到这个add,
                       // add是一个函数而已,add具体包括什么指令,它是不需要管的,因为现在还没有执行。
int main(void) {
        int x1 = 3;
        int x2 = 4;
        int x3 = add(x1, x2);
        cout << "x3=" << x3 << endl;
        return 0;
}

int add(int a, int b) { // 虽然前面使用了add,但把add定义在main后面一点问题没有,
                        // 因为执行的时候,这个地方已经翻译过了
        return a+b;
}

所以,对于编译型的程序,里面是可以放很多给“编译本身”的命令的,这些命令其实不是 命令CPU怎么工作,而是命令编译器怎么完成后面的编译。比如#define就是一个编译器命 令::

#include <iostream>

using namespace std;

#define MY_VALUE 3  //这是一个编译器命令,不产生汇编代码的,只是说,如果后面提到
                    //MY_VALUE,就用3代替而已。
int add(int a, int b);
int main(void) {
        int x1 = MY_VALUE;
        int x2 = 4;
        int x3 = add(x1, x2);
        cout << "x3=" << x3 << endl;
        return 0;
}

int add(int a, int b) {
        return a+b;
}

所以,理解编译型程序的逻辑顺序,我们就要分清楚:到底我们在考虑的是编译器解释代 码的顺序,还是程序执行的时候的顺序。

其实编译和执行都是用了计算机的计算能力,前面这个main函数,你写成这样::

int main(void) {
      int x1 = 3;
      int x2 = 4;
      int x3 = (x1, x2);
      cout << x3 << endl
      return 0;
}

你生成了一个代码,在编译的时候生成了一个加法,到运行的时候把3和4加起来,这是运 行的时候,用CPU的计算能力得到3+4的结果。

但如果你写成这样::

int main(void) {
      cout << 3+4 << endl
      return 0;
}

编译器一看,“你要求打印3+4,这个结果我知道啊”,它直接就在这个地方给你放一个7。 这时,这个计算是编译的时候就已经完成了的,如果看它翻译的结果,就是这样的::

00000000000011c1 <main>:
    11c1:     f3 0f 1e fa             endbr64
    11c5:     55                      push   %rbp
    11c6:     48 89 e5                mov    %rsp,%rbp
    11c9:     be 07 00 00 00          mov    $0x7,%esi   <-- 这个地方直接用了7个计算结果了,运行的时候根本不需要算了
    11ce:     48 8d 05 6b 2e 00 00    lea    0x2e6b(%rip),%rax        # 4040 <_ZSt4cout@GLIBCXX_3.4>
    ...