0.2 — 程序和编程语言简介

现代计算机速度惊人,而且一直在变得更快。然而,计算机也有一些显著的限制:它们只能原生理解有限的指令集,并且必须被准确地告知要做什么。

**计算机程序**是一系列指令,它指导计算机按照指定的顺序执行某些操作。计算机程序通常用**编程语言**编写,编程语言是旨在方便编写计算机指令的语言。有许多不同的编程语言可用,每种语言都迎合了不同的需求集。编写程序的行为(和艺术)称为**编程**。我们将在本章接下来的课程中更具体地讨论如何在 C++ 中创建程序。

当计算机执行计算机程序中指令描述的操作时,我们称其正在**运行**或**执行**该程序。计算机不会在接到指令之前开始执行程序。这通常需要用户**启动**(或**运行**或**执行**)程序,尽管程序也可以由其他程序启动。

程序在计算机的**硬件**上执行,硬件由构成计算机的物理组件组成。典型计算设备上的显著硬件包括

  • CPU(中央处理单元,通常称为计算机的“大脑”),它实际执行指令。
  • 内存,计算机程序在执行前会加载到其中。
  • 交互设备(例如显示器、触摸屏、键盘或鼠标),它们允许人与计算机交互。
  • 存储设备(例如硬盘、固态硬盘或闪存),即使计算机关机也能保留信息(包括已安装的程序)。

相比之下,**软件**一词广义地指系统中设计为在硬件上执行的程序。

在现代计算中,程序通常不仅与硬件交互,还与系统上的其他软件(特别是操作系统)交互。**平台**一词指兼容的硬件和软件(操作系统、浏览器等)集合,它为软件运行提供环境。例如,“PC”一词口语上用于指由在 x86 系列 CPU 上运行的 Windows 操作系统组成的平台。

平台通常为在其上运行的程序提供有用的服务。例如,桌面应用程序可能会请求操作系统为其分配一块空闲内存,在那里创建一个文件,或播放声音。正在运行的程序不必知道这是如何实际实现的。如果程序使用平台提供的功能或服务,它就会依赖于该平台,并且未经修改无法在其他平台上运行。可以轻松地从一个平台传输到另一个平台的程序称为**可移植的**。修改程序使其在不同平台上运行的行为称为**移植**。

既然我们已经讨论了程序,接下来让我们讨论编程语言。这不仅仅是一节历史课,我们还将介绍将在未来课程中出现的术语。

机器语言

计算机的 CPU 无法理解 C++。相反,CPU 只能处理用**机器语言**(或**机器代码**)编写的指令。给定 CPU 可以理解的所有可能的机器语言指令集称为**指令集**。

这是一个机器语言指令示例:10110000 01100001

每个指令都被 CPU 理解为执行一个非常具体任务的命令,例如“比较这两个数字”或“将这个数字复制到那个内存位置”。早在计算机刚发明的时候,程序员必须直接用机器语言编写程序,这是一项非常困难且耗时的工作。

这些指令如何组织和解释超出了本教程系列的范围,但有几点值得注意。

首先,每个指令由一系列 1 和 0 组成。每个单独的 0 或 1 称为**二进制数字**,简称**位**。机器语言指令中的位数各不相同——例如,某些 CPU 处理始终为 32 位长的指令,而其他一些 CPU(例如您可能正在使用的 x86 系列)的指令长度可变。

其次,每个兼容的 CPU 系列(例如 x86、Arm64)都有自己的机器语言,并且这种机器语言与其他 CPU 系列的机器语言不兼容。这意味着为某个 CPU 系列编写的机器语言程序无法在不同系列的 CPU 上运行!

相关内容

“CPU 系列”正式称为“指令集架构”(简称“ISA”)。维基百科在这里列出了不同的 CPU 系列:https://en.wikipedia.org/wiki/Comparison_of_instruction_set_architectures#Instruction_sets

汇编语言

机器语言指令(例如10110000 01100001)非常适合 CPU,但人类难以理解。由于程序(至少在历史上)是由人类编写和维护的,因此编程语言应该在设计时考虑到人类的需求。

**汇编语言**(通常简称**汇编**)是一种编程语言,它本质上充当更易于人类阅读的机器语言。以下是 x86 汇编语言中的相同指令:mov al, 0x61

选读

此指令说明了许多使汇编比机器语言更易读的功能。

  • 操作(指令所做的事情)由一个简短的助记符(通常是 3-5 个字母的名称)标识。mov很容易理解为“移动”的助记符,它是一个将位从一个位置复制到另一个位置的操作。
  • 寄存器(作为 CPU 本身一部分的快速内存位置)通过名称访问。al是 x86 CPU 上特定寄存器的名称。
  • 数字可以用更方便的格式指定。汇编语言通常支持十进制数字(例如97)和十六进制数字(例如0x61)。

很容易理解汇编指令mov al, 0x61将十六进制数字0x61复制到alCPU 寄存器中。

由于 CPU 不理解汇编语言,因此汇编程序必须先翻译成机器语言才能执行。这种翻译由一个名为**汇编器**的程序完成。因为每个汇编语言指令通常旨在反映等效的机器语言指令,所以翻译过程通常很简单。

就像每个 CPU 系列都有自己的机器语言一样,每个 CPU 系列也有自己的汇编语言(旨在汇编成该相同 CPU 系列的机器语言)。这意味着存在许多不同的汇编语言。尽管概念上相似,但不同的汇编语言支持不同的指令,使用不同的命名约定等。

低级语言简介

机器语言和汇编语言被认为是低级语言,因为这些语言对机器架构提供了最小的抽象。换句话说,编程语言本身是根据它将运行的特定指令集架构量身定制的。

低级语言有许多显著的缺点

  • 用低级语言编写的程序不可移植。由于低级语言是为特定指令集架构量身定制的,因此用该语言编写的程序也是如此。将此类程序移植到其他架构通常并非易事。
  • 用低级语言编写程序需要对架构本身有详细的了解。例如,指令mov al, 061h需要知道al指的是此特定平台上可用的 CPU 寄存器,并了解如何使用该寄存器。在不同的架构上,此寄存器可能名称不同,有不同的限制,或者根本不存在。
  • 低级程序难以理解。虽然单个汇编指令可能非常易懂,但要推断出一段汇编代码实际在做什么仍然很困难。而且由于汇编程序即使执行简单任务也需要许多指令,因此它们往往很长。
  • 编写复杂程度高的汇编程序很困难,因为该语言只提供原始功能。程序员需要自己实现所需的一切。

低级语言的主要优点是它们速度快。当代码中有性能关键部分时,汇编至今仍在使用。它还用于其他一些情况,我们稍后将讨论其中一种情况。

高级语言简介

为了解决上述许多缺点,开发了新的“高级”编程语言,例如 C、C++、Pascal(以及后来的 Java、Javascript 和 Perl 等语言)。

以下是 C/C++ 中与上面相同的指令:a = 97;

就像汇编程序(必须汇编成机器语言)一样,用高级语言编写的程序必须先翻译成机器语言才能运行。这有两种主要方式:编译和解释。

C++ 程序通常被编译。**编译器**是一个程序(或程序集合),它读取一种语言(通常是高级语言)的源代码并将其翻译成另一种语言(通常是低级语言)。例如,C++ 编译器将 C++ 源代码翻译成机器代码。

选读

大多数 C++ 编译器也可以配置为生成汇编代码。当程序员希望查看编译器为程序的某个部分生成了哪些特定指令时,这很有用。

然后,编译器输出的机器代码可以打包成可执行文件(包含机器语言指令),该文件可以分发给其他人并由操作系统启动。值得注意的是,运行可执行文件不需要安装编译器。

最初,编译器是原始的,生成速度慢、未优化的汇编或机器代码。然而,多年来,编译器在生成快速、优化代码方面变得非常出色,在某些情况下甚至比人类做得更好!

以下是编译过程的简化表示

Example of compiling

或者,**解释器**是一个直接执行源代码中指令的程序,无需先编译。解释器通常比编译器更灵活,但在运行程序时效率较低,因为每次运行程序都需要进行解释过程。这也意味着解释器必须安装在将运行解释程序的每台机器上。

以下是解释过程的简化表示

Example of interpreting

选读

关于编译器和解释器优缺点的良好比较可以在这里找到。

编译程序的另一个优点是分发编译程序不需要分发源代码。在非开源环境中,这对于知识产权(IP)保护目的很重要。

大多数高级语言既可以编译也可以解释。传统上,C、C++ 和 Pascal 等高级语言是编译的,而 Perl 和 Javascript 等“脚本”语言倾向于被解释。一些语言,如 Java,结合使用了这两种方式。我们很快将更详细地探讨 C++ 编译器。

高级语言的优点

高级语言之所以得名,是因为它们提供了对底层架构的高度抽象。

考虑指令a = 97;。这条指令允许我们把值97存储在内存的某个地方,而不需要知道这个值会精确地放在哪里,也不需要知道CPU需要哪条特定的机器码指令来存储这个值。事实上,这条指令根本没有任何平台特定的内容。编译器会完成所有工作,找出如何将这条C++指令转换为平台特定的机器码。

高级语言允许程序员在不了解其将运行的平台的情况下编写程序。这不仅使程序更容易编写,而且大大提高了它们的可移植性。如果我们小心谨慎,我们可以编写一个C++程序,它可以在所有具有C++编译器的平台上编译!旨在在多个平台上运行的程序被称为**跨平台**。

Example of portability

致进阶读者

以下是可能阻碍C++代码可移植性的一些因素(部分列表)

  • 许多操作系统,例如Microsoft Windows,提供平台特定功能,您可以在代码中使用这些功能。这可以大大简化为特定操作系统编写程序,或提供比其他方式更深入的与该操作系统的集成。
  • 许多第三方库仅在某些平台上可用。如果您使用其中一个,您将仅限于该库支持的平台。
  • 某些编译器支持编译器特定的扩展,这些功能仅在该编译器中可用。如果您使用这些扩展,您的程序将无法被不支持相同扩展的其他编译器编译,除非进行修改。我们稍后会讨论这些,在您安装了编译器之后。
  • 在某些情况下,C++语言允许编译器决定某些行为。我们在课程1.6 -- 未初始化变量和未定义行为中的“实现定义行为”下进一步讨论了这一点。

如果您只针对单个平台,那么可移植性可能不那么重要。但是现在许多应用程序都针对多个平台,以扩大其覆盖范围。例如,移动应用程序可能希望同时针对 iOS 和 Android。

即使可移植性最初看起来没什么用,许多最初只针对单个平台(例如 PC)的应用程序在取得一定程度的成功和关注后,决定移植到其他平台(例如 Mac 和各种游戏机)。如果您一开始就没有考虑可移植性,那么以后移植您的应用程序会更麻烦。

在本教程中,我们将尽可能避免使用平台特定的代码,以便我们的程序可以在任何拥有现代 C++ 编译器的平台上运行。

高级语言还有其他优点

  • 用高级语言编写的程序更易读、易写、易学,因为它们的指令更接近我们日常使用的自然语言和数学。在许多情况下,高级语言执行相同任务所需的指令比低级语言少。例如,在 C++ 中,您可以在一行中编写a = b * 2 + 5;。在汇编语言中,这将需要 4 到 6 条不同的指令。这使得用高级语言编写的程序更加简洁,从而更容易理解。
  • 高级语言通常包含额外的功能,可以更容易地执行常见的编程任务,例如请求一块内存或操作文本。例如,只需一条指令即可确定文本块中是否存在字符“abc”(如果存在,需要检查多少个字符才能找到“abc”)。这可以大大降低复杂性并缩短开发时间。

命名法

尽管C++从技术上讲被认为是一种高级语言,但较新的编程语言(例如脚本语言)提供了更高的抽象级别。因此,C++有时在比较中被不准确地称为“低级语言”。

作者注

今天,C++ 可能更准确地被描述为一种中级语言。然而,这也凸显了 C++ 的一个关键优势:它通常能够以不同抽象级别工作。您可以选择在较低级别操作以获得更好的性能和精度,或在较高级别操作以获得更大的便利性和简单性。

规则、最佳实践和警告

在本教程中,我们将强调以下三个类别的许多重要观点

规则

规则是语言要求您**必须**执行的指令。不遵守规则通常会导致程序无法正常工作。

最佳实践

最佳实践是您**应该**做的事情,因为这种做法要么是惯例(地道),要么是推荐的。也就是说,要么每个人都这样做(如果您不这样做,您将做一些人们不期望的事情),要么它通常优于替代方案。

警告

警告是您**不应该**做的事情,因为它们通常会导致意想不到的结果。

guest
您的电子邮箱地址将不会被显示
发现错误?请在上方留言!
与勘误相关的评论在处理后将被删除,以帮助减少混乱。感谢您帮助使网站对每个人都更好!
来自 https://gravatar.com/ 的头像与您提供的电子邮箱地址相关联。
有回复时通知我:  
793 评论
最新
最早 最多投票
内联反馈
查看所有评论