0-2 程序与编程语言介绍
现代计算机运行速度惊人,且日趋迅捷。然而计算机也存在显著局限:它们仅能原生理解有限指令集,必须被精确告知具体操作步骤。
计算机程序computer program是由指令序列组成的,它指导计算机按特定顺序执行特定操作。程序通常使用编程语言programming language编写,这种语言专为编写计算机指令而设计。现存多种编程语言,各自满足不同的需求。编写程序的行为(及艺术)称为编程programming。本章后续课程将具体讲解如何使用C++语言创建程序。
当计算机执行程序指令所描述的操作时,我们称其正在运行running或执行executing该程序。计算机不会在未被指令启动时自行执行程序,通常需要用户启动launch(或运行run、执行execute)程序,但程序也可由其他程序调用启动。
程序在计算机硬件hardware上运行,硬件由构成计算机的物理组件组成。典型计算设备中的主要硬件包括:
- CPU(中央处理器,常被称为计算机的“大脑”),实际执行指令的部件。
- 内存,程序执行前加载程序的存储空间。
- 交互设备(如显示器、触摸屏、键盘或鼠标),用于实现人与计算机的交互。
- 存储设备(如硬盘、固态硬盘或闪存),可在计算机关闭后持续保存信息(包括已安装程序)。
相对而言,软件software一词泛指系统中为在硬件上运行而设计的程序。
在现代计算中,程序不仅与硬件交互,还与系统中的其他软件(特别是操作系统)进行交互。平台platform一词指代兼容的软硬件组合(操作系统、浏览器等),为软件运行提供环境。例如,“PC”一词在日常使用中指代基于x86架构CPU运行Windows操作系统的平台。
平台通常为运行在其上的程序提供实用服务。例如桌面应用可能请求操作系统分配内存块、创建文件或播放声音。运行中的程序无需了解这些操作的具体实现方式。若程序使用了平台提供的功能或服务,便会形成平台依赖性,未经修改无法在其他平台运行。能够轻松在不同平台间迁移的程序被称为可移植portable程序。为使程序能在不同平台运行而进行的修改行为称为移植porting。
在探讨过程序之后,我们现在来讨论编程语言。这不仅是一堂历史课,还将介绍未来课程中会涉及的术语。
机器语言
计算机的CPU无法理解C++语言。相反,CPU只能处理用机器语言machine language(或机器码machine code)编写的指令。特定CPU能够理解的所有可能机器语言指令的集合称为指令集instruction set。
以下是一个机器语言指令示例:10110000 01100001。
CPU将每条指令理解为执行特定任务的命令,例如“比较这两个数字”或“将此数值复制到该内存位置”。计算机诞生初期,程序员必须直接用机器语言编写程序,这既困难又耗时。
本教程系列不涉及指令的组织与解释机制,但需注意以下几点:
首先,每条指令由1和0的序列构成。单个0或1称为二进制位binary digit(bit)。机器语言指令的位数存在差异——例如某些CPU始终处理32位指令,而另一些CPU(如你可能使用的x86系列)的指令长度可变。
其次,每个兼容的CPU家族(如x86、Arm64)都有专属的机器语言,且不同CPU家族的机器语言互不兼容。这意味着为某CPU家族编写的机器语言程序无法在其他家族的CPU上运行!
相关内容:
“CPU家族”的正式名称为“指令集架构instruction set architecture”(简称ISA)。维基百科在此处列出了不同的CPU家族。
汇编语言
机器语言指令(如10110000 01100001)对CPU而言是理想的,但人类难以理解。由于程序(至少在历史上)是由人类编写和维护的,因此编程语言的设计理应以人类需求为出发点。
汇编语言assembly language(通常简称为汇编assembly)是一种编程语言,本质上是作为更易于人类阅读的机器语言而存在。以下是上述指令在x86汇编语言中的对应形式:mov al, 0x61。
可选阅读:
本指令展示了使汇编语言比机器语言更易读的诸多特性。
- 操作(指令的功能)通过简短助记符(通常为3-5个字母的名称)标识。mov易于理解为“移动”的助记符,该操作将位从一个位置复制到另一个位置。
- 寄存器(CPU内部的高速存储单元)通过名称访问。al是x86 CPU中特定寄存器的名称。
- 数字可采用更便捷的格式表示。汇编语言通常同时支持十进制数(如97)和十六进制数(如0x61)。
很容易理解,汇编指令 mov al, 0x61 将十六进制数 0x61 复制到 CPU 的 al 寄存器中。
由于CPU无法理解汇编语言,汇编程序必须先被翻译成机器语言才能执行。这种翻译工作由称为汇编器assembler的程序完成。由于每条汇编语言指令通常都设计成与对应的机器语言指令相对应,因此翻译过程通常相当直接。
正如每个CPU家族都有专属的机器语言,每个CPU家族也拥有对应的汇编语言(其设计初衷是为该CPU家族编译成机器语言)。这意味着汇编语言种类繁多。尽管概念相似,不同汇编语言支持的指令集、采用的命名规范等均存在差异。
低级语言介绍
机器语言和汇编语言被视为低级语言,因为这些语言对机器架构的抽象程度极低。换言之,编程语言本身是针对其运行环境的特定指令集架构量身定制的。
低级语言low-level languages存在若干显著缺点:
- 用低级语言编写的程序不具备可移植性。由于低级语言针对特定指令集架构定制,用其编写的程序同样如此。将此类程序移植到其他架构通常非易事。
- 用低级语言编程需要精通架构细节。例如指令 mov al, 061h 要求开发者知晓 al 指代该特定平台的 CPU 寄存器,并理解如何操作该寄存器。在不同架构中,该寄存器可能名称不同、存在不同限制,甚至根本不存在。
- 低级程序难以理解。虽然单条汇编指令可能相当直观,但要推断一段汇编代码的实际功能仍很困难。由于汇编程序执行简单任务也需要大量指令,代码往往相当冗长。
- 由于汇编语言仅提供基础功能,编写复杂程序相当困难。程序员必须自行实现所有所需功能。
低级语言的核心优势在于运行速度快。在性能要求严苛的代码段中,汇编至今仍在使用。此外还有其他特殊场景会采用汇编语言,其中一种情况我们稍后将详细探讨。
高级语言简介
为解决上述诸多缺陷,人们开发了新的“高级”编程语言,如C、C++、Pascal(以及后来的Java、JavaScript和Perl等语言)。
以下是上述指令在C/C++中的实现:a = 97;。
与汇编程序(必须编译为机器语言)类似,用高级语言high-level Languages编写的程序也必须先转换为机器语言才能运行。主要有两种实现方式:编译和解释。
C++程序通常采用编译方式。编译器compiler是一种程序(或程序集合),它读取一种语言(通常是高级语言)的源代码,并将其转换为另一种语言(通常是低级语言)。例如,C++编译器将C++源代码转换为机器代码。
可选阅读:
大多数C++编译器也可配置为生成汇编代码。当程序员希望查看编译器为程序某段代码生成的具体指令时,此功能便显得尤为实用。
编译器输出的机器码可被封装成可执行文件(包含机器语言指令),该文件可分发给他人并由操作系统启动。值得注意的是,运行可执行文件无需安装编译器。
早期编译器较为原始,生成的汇编或机器代码运行缓慢且未经优化。但经过多年发展,编译器已能高效生成快速优化的代码,某些情况下甚至能超越人类编写代码的水平!
以下是编译过程的简化示意图:

或者,解释器interpreter是一种无需预先编译源代码即可直接执行指令的程序。解释器通常比编译器更灵活,但在运行程序时效率较低,因为每次运行程序都需要进行解释过程。这也意味着解释器必须安装在所有将运行解释型程序的机器上。
以下是解释过程的简化示意图:

可选阅读:
关于编译器与解释器的优劣对比,可参阅此处。编译程序的另一优势在于:分发编译程序时无需附带源代码。在非开源环境中,这对知识产权(IP)保护至关重要。
大多数高级语言既可编译也可解释执行。传统上,C、C++和Pascal等高级语言采用编译方式,而Perl和JavaScript等“脚本”语言则多为解释执行。某些语言(如Java)则混合使用两种方式。我们稍后将深入探讨C++编译器。
高级语言的优势
高级语言之所以得名,是因为它们能提供远离底层架构的高抽象度。
以指令 a = 97; 为例。该指令允许我们在内存某处存储数值 97,而无需精确知晓存储位置,也无需了解 CPU 存储该值所需的具体机器码指令。事实上,这条指令完全不涉及平台特异性。编译器会自动完成将此 C++ 指令转换为平台特异性机器码的工作。
高级语言使程序员能够在无需深入了解运行平台的情况下编写程序。这不仅简化了编程过程,更显著提升了程序的可移植性。只要编写得当,我们甚至能编写出能在所有配备C++编译器的平台上运行的单一C++程序!这种为多平台设计的程序被称为跨平台程序cross-platform。

面向高级读者的说明:
以下是可能影响C++代码可移植性的部分因素:
- 许多操作系统(如Microsoft Windows)提供平台专属功能,可在代码中调用。这些功能能显著简化特定操作系统的程序开发,或实现比常规方式更深入的系统集成。
- 许多第三方库仅支持特定平台。若使用此类库,程序将受限于该库支持的平台范围。
- 部分编译器支持专属扩展功能,这些特性仅在特定编译器中可用。若使用此类功能,程序在未修改的情况下将无法被其他不支持相同扩展的编译器编译。待您安装编译器后,我们将深入探讨这些内容。
- 在特定情况下,C++语言允许编译器自行决定行为方式。我们在第1.6课“实现定义的行为”章节中将深入探讨未初始化变量与未定义行为。
若仅针对单一平台开发,可移植性或许无关紧要。但如今多数应用为扩大覆盖范围而支持多平台。例如移动应用通常需要同时支持iOS和Android系统。
即使可移植性在初期看似无用,许多最初仅针对单一平台(如PC)开发的应用程序,在取得一定成功并引发市场关注后,最终都选择移植到其他平台(如Mac和各类游戏主机)。若开发之初未考虑可移植性,后期移植应用程序将耗费更多精力。
在本教程中,我们将尽可能避免使用特定平台的代码,确保程序能在任何配备现代C++编译器的平台上运行。
高级语言还具备其他优势:
-
用高级语言编写的程序更易于阅读、编写和学习,因为其指令更接近我们日常使用的自然语言和数学表达。在多数情况下,高级语言完成相同任务所需指令更少。例如C++中只需一行代码a = b * 2 + 5;,而汇编语言则需4至6条不同指令。这使得高级语言编写的程序更简洁,更易于理解。
-
高级语言通常具备额外功能,能简化常见编程任务,例如申请内存块或文本处理。例如,只需一条指令即可判断“abc”字符是否存在于大段文本中(若存在,则需检查多少个字符才找到“abc”)。这能显著降低开发复杂度并缩短开发周期。
命名法:
尽管C++在技术上被视为高级语言,但新型编程语言(如脚本语言)提供了更高的抽象层次。因此,相较之下,C++有时会被不准确地称为“低级语言”。
作者注:
如今,C++或许更准确地应被称为中级语言。但这也凸显了C++的核心优势之一:它常能提供在不同抽象层级上工作的能力。你可以选择在较低层级操作以获得更佳性能和精度,或在较高层级操作以获得更大便利和简洁性。
规则、最佳实践与警告
在接下来的教程中,我们将重点阐述以下三大类别的诸多要点:
规则:
规则是语言要求你必须遵守的指令。不遵守规则通常会导致程序无法正常运行。
最佳实践:
最佳实践是指应当遵循的做法,因为这种做法要么是惯例(符合惯用表达),要么是推荐做法。也就是说,要么所有人都这样做(若你另辟蹊径,就会做出出人意料之举),要么它普遍优于其他替代方案。
警告:
警告是指你不应采取的行为,因为这些行为通常会导致意外后果。

浙公网安备 33010602011771号