C++ 编程
一个 编译器 是一个将用一种 计算机语言(源代码)编写的 计算机程序 翻译成等效的用计算机的原生 机器语言 编写的程序的程序。这个翻译过程,包括几个不同的步骤,被称为 编译。由于编译器本身就是一个程序,用计算机语言编写,所以这种情况似乎是一个悖论,类似于 先有鸡还是先有蛋 的困境。编译器可能不是用最终可编译的语言创建的,而是用之前可用的语言或甚至用机器代码创建的。
编译器的 编译 输出是翻译或 编译 程序的结果。输出中最重要的一部分保存到一个名为 目标文件 的文件中,它包含将源文件转换为目标文件的转换。
然后,如果目标文件是可执行格式,计算机可以运行(执行)该 编译 程序的指令。但是,编译还需要额外的步骤:预处理和链接。
定义编译器在构建(创建)程序(可执行或不可执行)期间执行的时间和操作(即 编译时操作)。C++ 语言中“static”的大多数用法与编译时信息直接相关。
编译时执行的操作通常包括 词法分析、语法分析、各种类型的 语义分析(例如,类型检查、一些 类型转换 和 模板实例化)以及 代码生成。
编程语言的定义将指定源代码必须满足的编译时要求才能成功编译。
编译时发生在 链接时(将一个或多个编译文件的输出连接在一起)和运行时(程序执行时)之前。在一些编程语言中,可能需要在运行时进行一些编译和链接。
- 运行时
运行时 或 执行时间 从程序开始执行的那一刻开始,到程序退出时结束。在这个阶段,编译器无关紧要,没有控制权。这是关于优化的最重要的位置(程序只编译一次,但运行很多次)和调试(跟踪和交互只有在这个阶段才能实现)。但运行时也是一些 类型转换可能发生 的地方,运行时类型信息 (RTTI) 与此相关。运行时的概念将在相关时再次提及。
这也被称为扫描或 标记化。它发生在语法分析之前,并将代码转换为 标记,这些标记是程序实际使用的代码部分。源代码以字符(排列在行上)的形式表达为每个保留关键字的特殊标记序列,以及数据类型和标识符以及值的标记。词法分析器是编译器的一部分,它从源代码中删除空格和其他不可编译字符。它使用空格来分隔不同的标记,并忽略空格。
为了简单说明这个过程
int main()
{
std::cout << "hello world" << std::endl;
return 0;
}
根据使用的词法规则,它可能被 标记化 为
1 = string "int" 2 = string "main" 3 = opening parenthesis 4 = closing parenthesis 5 = opening brace 6 = string "std" 7 = namespace operator 8 = string "cout" 9 = << operator 10 = string ""hello world"" 11 = string "endl" 12 = semicolon 13 = string "return" 14 = number 0 15 = closing brace
因此,对于这个程序,词法分析器可能会发送类似的东西
1 2 3 4 5 6 7 8 9 10 9 6 7 11 12 13 14 12 15
给下一个将要解析的语法分析器。当它可以处理数值并能够区分语言语法(例如分号)和所有其他内容时,语法分析器更容易应用语言规则,并且知道每个事物的数据类型。
此步骤(有时也称为语法检查)确保代码有效,并将按顺序组成可执行程序。语法分析器将规则应用于代码,检查以确保每个左括号都有一个对应的右括号,每个声明都有一个类型,以及该类型存在,以及……语法分析比词法分析更复杂 =)。
例如
int main()
{
std::cout << "hello world" << std::endl;
return 0;
}
- 语法分析器将首先查看字符串“int”,将其与定义的关键字进行比较,并发现它是一个整数类型。
- 然后,分析器会将下一个标记视为标识符,并检查以确保它使用的是有效的标识符名称。
- 然后它将查看下一个标记。因为它是一个左括号,所以它将“main”视为一个函数,而不是一个变量声明(如果它找到一个分号)或一个整数变量的初始化(如果它找到一个等号)。
- 在左括号之后,它将找到一个右括号,这意味着该函数有 0 个参数。
- 然后它会查看下一个标记,发现它是一个左大括号,所以它会认为这是函数 main 的实现,而不是 main 的声明(如果下一个标记是分号的话),即使在 C++ 中不能声明 main。它可能还会创建一个计数器来跟踪语句块的级别,以确保括号成对出现。*之后它会查看下一个标记,可能不会对其进行任何操作,但然后它会看到 :: 运算符,并检查 "std" 是否是一个有效的
命名空间
。 - 然后它会看到下一个标记 "cout" 作为 "std"
命名空间
中标识符的名称,并发现它是一个模板。 - 分析器会看到下一个标记 << 运算符,因此它会检查 << 运算符是否可以与 cout 一起使用,以及下一个标记是否可以与 << 运算符一起使用。
- 下一个标记 ""hello world"" 后面的标记也会发生同样的事情。然后它会再次遇到 "std" 标记,查看它后面的 :: 运算符标记,并检查
命名空间
是否再次存在,然后检查 "endl" 是否在命名空间
中。 - 然后它会看到分号,所以它会将其视为语句的结束。
- 接下来它会看到关键字
return
,然后期望下一个标记是一个整数值,因为 main 返回一个整数,它会找到 0,这是一个整数。 - 然后下一个符号是分号,所以这是语句的结束。
- 下一个标记是右大括号,所以这是函数的结束。并且没有更多标记了,所以如果语法分析器没有在代码中发现任何错误,它会将标记发送给编译器,以便程序可以被转换为机器语言。
这只是语法分析的一个简单视图,真正的语法分析器并不真正以这种方式工作,但其思想是相同的。
以下是一些语法分析器会查找的关键字,以确保您没有将它们用作标识符名称,或者了解您正在定义的变量类型或正在使用的函数,这些函数包含在 C++ 语言中。
编译速度
[edit | edit source]有几个因素决定了编译的速度,例如
- 硬件
- 资源(慢速 CPU、内存不足,甚至慢速 HDD 都会有影响)
- 软件
- 编译器本身,新的总是更好,但可能取决于您希望项目移植到什么程度。
- 为程序选择的方案(对象依赖结构、包含)也会起作用。
经验表明,如果您遇到编译速度慢的问题,您尝试编译的程序设计很差,花点时间构建自己的代码以最大程度地减少更改后的重新编译。大型项目总是编译速度更慢。使用预编译头文件和外部头文件保护。我们将在本书的 优化 部分讨论减少编译时间的方法。
哪里可以获得编译器
[edit | edit source]在选择编译器时,您必须考虑您的系统操作系统、个人偏好以及您在使用它时可以获得的文档。
如果您没有、不想或不需要在机器上安装编译器,可以使用 http://ideone.com(或 http://codepad.org,但您需要更改代码以不需要交互式输入)上的 WEB 免费编译器。如果您需要,您总是可以在本地获得一个。
有很多编译器,甚至更多的 IDE 可用,有些是免费的和开源的。IDE 通常会将所需的编译器包含在安装中(GCC 是最常见的)。
GCC
[edit | edit source]GCC 是最成熟、最兼容的 C++ 编译器之一,也称为 GNU 编译器集合。它是一套由自由软件基金会开发的免费编译器,理查德·斯托曼是主要架构师之一。
互联网上有许多不同的预编译 GCC 二进制文件;下面列出了一些流行的选择(以及详细的安装步骤)。您可以在 GCC 网站上轻松找到有关如何在其他操作系统上执行此操作的信息。
IDE(集成开发环境)
[edit | edit source]集成开发环境 是一种软件开发系统,它通常将编辑器、编译器和调试器集成在一个一起分发的集成包中。有些 IDE 需要用户自己进行组件集成,而另一些则将 IDE 称为他们用于编程的一组独立工具。
一个好的 IDE 是一个允许程序员使用它来抽象和加速一些更常见任务,同时在阅读和管理代码方面提供一些帮助的 IDE。除了编译器之外,C++ 标准对不同的实现没有控制。大多数 IDE 都是面向视觉的,尤其是新的 IDE,它们将提供图形调试器和其他视觉辅助工具,但有些人仍然喜欢像 Vim 或 Emacs 这样的强大文本编辑器提供的视觉简洁。
在选择 IDE 时,请记住,您也在投入时间来精通它的使用。完整性、稳定性和跨操作系统的可移植性将非常重要。
对于 Microsoft Windows,您还有 Microsoft Visual Studio Community(最新版本 2019),目前可以免费获得,并且包含大多数功能。它包含一个 C++ 编译器,可以从命令行或提供的 IDE 中使用。
在本书的 附录 B:外部参考 中,您将找到对可以使用的其他免费编译器和 IDE 的引用。
在 Windows 上
[edit | edit source]Cygwin
- 转到 http://www.cygwin.com 并单击页面右上角的“立即安装 Cygwin”按钮。
- 在弹出的窗口中单击“运行”,然后多次单击“下一步”,接受所有默认设置。
- 当该窗口弹出时,选择任何下载站点(“ftp.easynet.be”等);按“下一步”,Cygwin 安装程序应开始下载。
- 当“选择包”窗口出现时,向下滚动到“Devel”标题,并单击它旁边的“+”。在现在显示的包列表中,向下滚动并找到“gcc-c++”包;这是编译器。单击“跳过”一词一次,它应该更改为类似“3.4”之类的数字(版本号),并且“gcc-core”以及现在将要下载的几个其他必需包旁边会出现一个“X”。
- 单击“下一步”,编译器以及 Cygwin 工具应开始下载;这可能需要一段时间。在等待的同时,转到 http://www.crimsoneditor.com 并下载该免费程序员编辑器;它功能强大,但对于初学者来说易于使用。
- Cygwin 下载完成后,点击“下一步”等完成安装,双击桌面上的 Cygwin 图标启动 Cygwin “命令提示符”。 您的主目录将自动设置在 Cygwin 文件夹中,现在应该在“C:\cygwin”中(Cygwin 文件夹在某种程度上就像您 Windows 机器上的一个小 Unix/Linux 计算机——当然不是技术上,但把它想象成那样可能会有所帮助)。
- 在 Cygwin 提示符下输入“g++”并按“回车”; 如果出现“g++: no input files”或类似的信息,则表示您已成功安装了 gcc C++ 编译器到您的计算机上(恭喜——您还刚刚收到您的第一个错误信息!)。
MinGW + DevCpp-IDE
- 前往 http://www.bloodshed.net/devcpp.html ,(严重过时,上次更新于 2005 年)(http://orwelldevcpp.blogspot.com/) (Updated Branch project) 选择您想要的版本(最终向下滚动),然后点击相应的下载链接! 对于最新版本,您将被重定向到 http://www.bloodshed.net/dev/devcpp.html
- 向下滚动以阅读许可证,然后到下载链接。 下载包含 Mingw/GCC 的版本。 它比自己组装要容易得多。 经过短暂的延迟(只有几天),您将始终获得与 devcpp IDE 打包的最新版本的 MinGW。 它与手动下载所需模块完全相同。
- 您将获得一个可执行文件,可以在任何 WinNT 版本的用户级别下执行。 但是,如果您希望将其设置为所有用户,则需要管理员权限。 它将安装 devcpp 和 mingw 到您想要的文件夹中。
- 启动 IDE 并体验您的第一个项目!
您会发现它与 MSVC 大致相似,包括菜单和按钮放置。 当然,如果您熟悉前者,许多方面会有所不同,但只需点击几下即可让您的第一个程序运行。
DJGPP
- 前往 Delorie 软件 并下载 GNU C++ 编译器和其他必要的工具。 该网站提供了一个 Zip Picker,以帮助您确定需要哪些文件,该文件可在主页面上获得。
- 使用 unzip32 或其他提取工具将文件放置到您选择的目录中(例如 C:\DJGPP)。
- 设置环境变量以配置 DJGPP 进行编译,方法是将行添加到 autoexec.bat 或自定义批处理文件中
set PATH=C:\DJGPP\BIN;%PATH%
set DJGPP=C:\DJGPP\DJGPP.ENV
- 如果您运行的是 MS-DOS 或 Windows 3.1,则需要在 config.sys 中添加几行,如果它们不存在。
shell=c:\dos\command.com c:\dos /e:2048 /p
files=40
fcbs=40,0
注意: DJGPP 下的 GNU C++ 编译器名为 gpp。
- 对于 Gentoo,GCC C++ 是系统核心的一部分(因为 Gentoo 中的所有内容都是编译的)
- 对于 Redhat,获取一个 gcc-c++ RPM,例如使用 Rpmfind,然后使用以下命令安装(作为 root):rpm -ivh gcc-c++-version-release.arch.rpm
- 对于 Fedora,使用以下命令安装 GCC C++ 编译器(作为 root):dnf install gcc-c++
- 对于 Mandrake,使用以下命令安装 GCC C++ 编译器(作为 root):urpmi gcc-c++
- 对于 Debian,使用以下命令安装 GCC C++ 编译器(作为 root):apt-get install g++
- 对于 Ubuntu,使用以下命令安装 GCC C++ 编译器:sudo apt-get install g++
- 对于 openSUSE,使用以下命令安装 GCC C++ 编译器(作为 root):zypper in gcc-c++
- 如果您无法成为 root 用户,请从 [1] 获取 tarball 并按照其中的说明在您的主目录中编译和安装。
Xcode(Apple 的 OSX 和 iOS 的 IDE)v4.1 以上版本使用 Clang [2],它是 GCC 编译器的免费开源替代方案,与 GCC 大致兼容(甚至使用相同的命令行参数)。 IDE 还捆绑了 GCC C++ 编译器的旧版本。 它可以通过与 Linux 相同的方式从终端调用,但也可以在 XCode 的其中一个项目中进行编译。