目录
1.9 编程机制
生成程序的具体过程因计算机环境而异。C 是可移植性语言,因此可以在许多环境中使用,包括 UNIX、Linux、MS-DOS(一些人仍在使用)、Windows 和 Macintosh OS。有些产品会随着时间的推移发生演变或被取代,本书无法涵盖所有环境。
首先,来看看许多 C 环境(包括上面提到的 5 种环境)共有的一些方面。虽然不必详细了解计算机内部如何运行 C 程序,但是,了解一下编程机制不仅能丰富编程相关的背景知识,还有助于理解为何要经过一些特殊的步骤才能得到 C 程序。
1.9.1 C 源代码文件
源文件基本结构
用 C 语言编写程序时,编写的内容被储存在文本文件中,该文件被称为源代码文件(source code file)。大部分 C 系统,包括之前提到的,都要求文件名以 .c 结尾(如,wordcount.c 和 budget.c)。在文件名中:
- 点号(.)前面的部分称为基本名(basename);
- 点号后面的部分称为扩展名(extension)。
因此, budget 是基本名,c 是扩展名。基本名与扩展名的组合(budget.c)就是文件名。
文件名长度限制
源代码文件名应该满足特定计算机操作系统的特殊要求。例如:
-
MS-DOS:这个较老的操作系统要求文件的基本名不能超过 8 个字符,扩展名最多 3 个字符,因此整个文件名(包括点号)不能超过 12 个字符。例如,wordcount.c 在 MS-DOS 中是无效的文件名,因为它超过了 8.3 格式的限制。
-
UNIX / Linux:早期的一些 UNIX 系统限制整个文件名(包括扩展名)不超过 14 个字符。然而,现代的 UNIX 和 Linux 系统通常允许更长的文件名,最长可达 255 个字符。
-
Windows:现代 Windows 系统支持长文件名,理论上可以长达 255 个字符,但实际使用中建议保持合理的长度以确保兼容性和易读性。
-
Mac OS:与现代 Windows 类似,Mac OS 也支持长文件名,同样建议保持适中的长度。
接下来,我们来看一下具体的应用,假设有一个名为 concrete.c 的源文件,其中的 C 源代码如程序清单 1.2 所示。
程序清单 1.2 C 程序
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
如果看不懂程序清单 1.2 中的代码,不用担心,我们将在第 2 章学习相关知识。
1.9.2 目标代码文件、可执行文件和库
编译与链接
C 编程的基本策略是,用程序把源代码文件转换为可执行文件(其中包含可直接运行的机器语言代码)。典型的 C 实现通过编译和链接两个步骤来完成这一过程。编译器把源代码转换成中间代码,链接器把中间代码和其他代码合并,生成可执行文件。
C 使用这种分而治之的方法方便对程序进行模块化,可以独立编译单独的模块,稍后再用链接器合并已编译的模块。通过这种方式,如果只更改某个模块,不必因此重新编译其他模块。另外,链接器还将你编写的程序和预编译的库代码合并。
目标代码文件
中间文件有多种形式。我们在这里描述的是最普遍的一种形式,即把源代码转换为机器语言代码,并把结果放在目标代码文件(或简称目标文件)中(这里假设源代码只有一个文件)。虽然目标文件中包含机器语言代码,但是并不能直接运行该文件。因为目标文件中储存的是编译器翻译的源代码,这还不是一个完整的程序。
启动代码
目标代码文件缺失启动代码(startup code)。启动代码充当着程序和操作系统之间的接口。例如,可以在 MS Windows 或 Linux 系统下运行 IBM PC 兼容机。这两种情况所使用的硬件相同,所以目标代码相同,但是 Windows 和 Linux 所需的启动代码不同,因为这些系统处理程序的方式不同。
库函数
目标代码还缺少库函数。几乎所有的 C 程序都要使用 C 标准库中的函数。例如,concrete.c 中就使用了 printf () 函数。目标代码文件并不包含该函数的代码,它只包含了使用 printf() 函数的指令。printf() 函数真正的代码储存在另一个被称为库的文件中。库文件中有许多函数的目标代码。
链接器的作用
链接器的作用是,把你编写的目标代码、系统的标准启动代码和库代码这 3 部分合并成一个文件,即可执行文件。
对于库代码,链接器只会把程序中要用到的库函数代码提取出来(见图 1.4)。
目标文件 vs 可执行文件
简而言之,目标文件和可执行文件都由机器语言指令组成的。然而,目标文件中只包含编译器为你编写的代码翻译的机器语言代码,可执行文件中还包含你编写的程序中使用的库函数和启动代码的机器代码。
在有些系统中,必须分别运行编译程序和链接程序,而在另一些系统中,编译器会自动启动链接器,用户只需给出编译命令即可。
接下来,了解一些具体的系统。
1.9.3 UNIX 系统
由于 C 语言因 UNIX 系统而生,也因此而流行,所以我们从 UNIX 系统开始(注意:我们提到的 UNIX 还包含其他系统,如 FreeBSD,它是 UNIX 的一个分支,但是由于法律原因不使用该名称)。
在 UNIX 系统上编辑
UNIX C 没有自己的编辑器,但是可以使用通用的 UNIX 编辑器,如 emacs、jove、vi 或 X Window System 文本编辑器。
作为程序员,要负责输入正确的程序和为储存该程序的文件起一个合适的文件名。如前所述,文件名应该以 .c 结尾。注意,UNIX 区分大小写。因此,budget.c、BUDGET.c 和 Budget.c 是 3 个不同但都有效的 C 源文件名。但是 BUDGET.C 是无效文件名,因为该名称的扩展名使用了大写 C 而不是小写 c。
假设我们在 vi 编译器中编写了下面的程序,并将其储存在 inform.c 文件中:
#include <stdio.h>
int main(void)
{
printf ("A.c is used to end a c program filename.\n");
return 0;
}
以上文本就是源代码,inform.c 是源文件。注意,源文件是整个编译过程的开始,不是结束。
在 UNIX 系统上编译
虽然在我们看来,程序完美无缺,但是对计算机而言,这是一堆乱码。计算机不明白 #include 和 printf 是什么(也许你现在也不明白,但是学到后面就会明白,而计算机却不会)。
如前所述,我们需要编译器将我们编写的代码(源代码)翻译成计算机能看懂的代码(机器代码)。最后生成的可执行文件中包含计算机要完成任务所需的所有机器代码。
以前,UNIX C 编译器要调用语言定义的 cc 命令。但是,它没有跟上标准发展的脚步,已经退出了历史舞台。但是,UNIX 系统提供的 C 编译器通常来自一些其他源,然后以 cc 命令作为编译器的别名。因此,虽然在不同的系统中会调用不同的编译器,但用户仍可以继续使用相同的命令。
编译 inform.c,要输入以下命令:
cc inform.c
几秒钟后,会返回 UNIX 的提示,告诉用户任务已完成。如果程序编写错误,你可能会看到警告或错误消息,但我们先假设编写的程序完全正确(如果编译器报告 void 的错误,说明你的系统未更新成 ANSIC 编译器,只需删除 void 即可)。
如果使用 ls 命令列出文件,会发现有一个 a.out 文件(见图1.5)。该文件是包含已翻译(或已编译)程序的可执行文件。
要运行该文件,只需输入:
a.out
输出内容如下:
A.c is used to end a c program filename.
如果要储存可执行文件(a.out),应该把它重命名。否则,该文件会被下一次编译程序时生成的新 a.out 文件替换。
如何处理目标代码?C 编译器会创建一个与源代码基本名相同的目标代码文件,但是其扩展名是 .o。在该例中,目标代码文件是 inform.o。然而,却找不到这个文件,因为一旦链接器生成了完整的可执行程序,就会将目标代码文件(inform.o)删除。如果原始程序有多个源代码文件,则保留目标代码文件。学到后面多文件程序时,你会明白到这样做的好处。
1.9.4 GNU 编译器集合和 LLVM 项目
GNU 项目与 GCC 编译器
GNU 项目始于 1987 年,是一个开发大量免费 UNIX 软件的集合(GNU 的意思是 “GNU's Not UNIX",即 GNU 不是 UNIX)。GNU 编译器集合(也被称为 GCC,其中包含 GCC C 编译器)是该项目的产品之一。
GCC 在一个指导委员会的带领下,持续不断地开发,它的 C 编译器紧跟 C 标准的改动。GCC 有各种版本以适应不同的硬件平台和操作系统,包括 UNIX、Linux 和 Windows。用 gcc 命令便可调用 GCC C 编译器。许多使用 gcc 的系统都用 cc 作为 gcc 的别名。
扩展:MinGW 的安装
你可以在电脑中安装 MinGW 来使用 GCC 编译器。详细的安装步骤和指南,请参考我撰写的这篇博客文章,其中提供了清晰的说明和操作流程。文章直达链接:C 语言开发工具的选择,MinGW 的安装与配置,VS Code 的安装与配置、插件推荐
LLVM 项目与 Clang 编译器
LLVM 项目成为 cc 的另一个替代品。该项目是与编译器相关的开源软件集合,始于伊利诺伊大学的 2000 份研究项目。它的 Clang 编译器处理 C 代码,可以通过 clang 调用。有多种版本供不同的平台使用,包括 Linux。2012 年,Clang 成为 FreeBSD 的默认 C 编译器。Clang 也对最新的 C 标准支持得很好。
查看编译器版本信息
GNU 和 LLVM 都可以使用 -v 选项来显示版本信息,因此各系统都使用 cc 别名来代替 gcc 或 clang 命令。以下组合可以显示你所使用的编译器及其版本。
cc -v
在 Windows 中,你可以通过以下指令在命令行(CMD)中查看已安装的 GCC 编译器的版本信息:
gcc -v:提供详细的编译环境信息,包括配置选项、库路径等,有助于调试和理解编译过程。
gcc --version:用于快速查看 GCC 的版本信息。
常用的 gcc 指令
GCC 最基本的用法是:gcc [options] [filenames],其中 options 是所需的参数,filenames 是文件名。
在使用 GCC 编译 C 程序时,根据不同的需求可以选择多种编译方式。以下是三种常见的编译方法及其优缺点(以源文件 inform.c 为例):
1. 默认编译法
gcc inform.c
- 结果:生成默认名为 a.out(在 Linux 和 Unix 系统中)或 a.exe(在 Windows 系统中) 的可执行文件。
- 缺点:文件名缺乏描述性,容易被后续编译操作覆盖,因此不推荐用于实际项目中。
2. 分步编译法
预处理: gcc -E inform.c -o inform.i
编译: gcc -S inform.i -o inform.s
汇编: gcc -c inform.s -o inform.o
链接: gcc inform.o -o inform
- 优点:可以深入了解编译过程中的每个阶段。
- 缺点:步骤繁琐,实际开发中较少采用,更适合学习和调试。
3. 一步到位法
gcc inform.c -o inform
- 结果:直接编译并生成指定名称的可执行文件 inform。
- 优点:简洁高效,文件名直观易懂,不会被其他编译操作覆盖,是日常开发中的首选方法。
推荐做法
对于大多数开发者来说,第三种 “一步到位法” 是最为便捷且实用的选择。它不仅简化了编译流程,还确保了生成的可执行文件具有明确的命名,便于管理和维护。而对于那些希望深入理解编译过程的人来说,第二种“分步编译法”提供了宝贵的学习机会,但在实际项目中并不常见。
扩展:
如果你想深入学习 GCC 指令及其在 VS Code 中调试 C 程序的编译过程,熟练掌握 GCC 指令的使用和终端操作,建议参考我的这篇博客。文章中提供了详细的讲解和实践指导,帮助你更好地理解和运用这些工具。直达链接如下:详细了解 GCC 指令及 VS Code 中的 C 程序调试
指定 C 标准进行编译
gcc 和 clang 命令都可以通过运行时选项来选择不同的 C 标准进行编译。以下是使用 c89/c90、c99 和 c11 等标准的示例:
1. 使用 C89/C90 标准编译
gcc -std=c89 inform.c -o inform
或者
gcc -std=c90 inform.c -o inform
这两条命令都是使用 ANSI C 或 ISO C90 标准(也称为 C89)编译 inform.c 文件,并生成指定名为 inform 的可执行文件。
2. 使用 C99 标准编译
gcc -std=c99 inform.c -o inform
这条命令使用 ISO C99 标准编译 inform.c 文件,并生成指定名为 inform 的可执行文件。
3. 使用 C11 标准编译
gcc -std=c11 inform.c -o inform
这条命令使用 ISO C11 标准编译 inform.c 文件,并生成指定名为 inform 的可执行文件。
注意事项
- -o 选项:用于指定输出文件的名称。如果不指定,默认生成的可执行文件名为 a.out(在 Windows 上为 a.exe)。
- 其他标准选项:
- -std=gnu89 或 -std=gnu90:使用 GNU 扩展的 C89/C90 标准。
- -std=gnu99:使用 GNU 扩展的 C99 标准。
- -std=gnu11:使用 GNU 扩展的 C11 标准。
这些命令可以帮助开发者根据项目需求选择合适的 C 标准版本进行编译,确保代码的兼容性和正确性。
1.9.5 Linux 系统
Linux 是一个开源、流行、类似于 UNIX 的操作系统,可在不同平台(包括 PC 和 Mac)上运行。
在 Linux 中准备 C 程序与在 UNIX 系统中几乎一样,不同的是要使用 GNU 提供的 GCC 公共域 C 编译器。编译命令类似于:
gcc inform.c
注意,在安装 Linux 时,可选择是否安装 GCC。如果之前没有安装 GCC,则必须安装。通常,安装过程会将 cc 作为 gcc 的别名,因此可以在命令行中使用 cc 来代替 gcc。
欲详细了解 GCC 和最新发布的版本,请访问 http://www.gnu.org/software/gcc/index.html。
1.9.6 PC 的命令行编译器
C 编译器不是标准 Windows 软件包的一部分,因此需要从别处获取并安装 C 编译器。可以从互联网免费下载 Cygwin 和 MinGW,这样便可在 PC 上通过命令行使用 GCC 编译器。
- Cygwin 在自己的视窗运行,模仿 Linux 命令行环境,有一行命令提示。
- MinGW 在 Windows 的命令提示模式中运行。这和 GCC 的最新版本一样,支持 C99 和 C11 最新的一些功能。
- Borland 的 C++ 编译器 5.5 也可以免费下载,支持 C90。
源代码文件应该是文本文件,不是字处理器文件(字处理器文件包含许多额外的信息,如字体和格式等)。因此,要使用文本编辑器(如,Windows Notepad)来编辑源代码。如果使用字处理器,要以文本模式另存文件。源代码文件的扩展名应该是 .c。一些字处理器会为文本文件自动添加 .txt 扩展名。如果出现这种情况,要更改文件名,把 txt 替换成 c。
通常,C 编译器生成的中间目标代码文件的扩展名是 .obj(也可能是其他扩展名)。与 UNIX 编译器不同,这些编译器在完成编译后通常不会删除这些中间文件。有些编译器生成带 .asm 扩展名的汇编语言文件,而有些编译器则使用自己特有的格式。
一些编译器在编译后会自动运行链接器,另一些要求用户手动运行链接器。在可执行文件中链接的结果是,在原始的源代码基本名后面加上 .exe 扩展名。例如,编译和链接 concrete.c 源代码文件,生成的是 concrete.exe 文件。可以在命令行输入基本名来运行该程序,也可以使用完整的文件名(基本名.扩展名)来运行该程序:
1.9.7 集成开发环境(Windows)
许多供应商(包括微软、Embarcadero、Digital Mars)都提供 Windows下的集成开发环境,或称为 IDE (目前,大多数 IDE 都是 C 和 C++ 结合的编译器)。可以免费下载的 IDE 有 Microsoft Visual Studio Express 和 Pelles C。利用集成开发环境可以快速开发 C 程序。关键是,这些 IDE 都内置了用于编写 C 程序的编辑器。这类集成开发环境都提供了各种菜单(如,命名、保存源代码文件、编译程序、运行程序等),用户不用离开 IDE 就能顺利编写、编译和运行程序。如果编译器发现错误,会返回编辑器中,标出有错误的行号并简单描述情况。
初次接触 Windows IDE 可能会望而生畏,因为它提供了多种目标(target),即运行程序的多种环境。例如,IDE 提供了 32 位 Windows 程序、64 位 Windows 程序、动态链接库文件(DLL)等。许多目标都涉及 Windows 图形界面。要管理这些(及其他)选择,通常要先创建一个项目(project),以便稍后在其中添加待使用的源代码文件名。不同的产品具体步骤不同。一般而言,首先使用【文件】菜单或【项目】菜单创建一个项目。选择正确的项目形式非常重要。本书中的例子都是一般示例,针对在简单的命令行环境中运行而设计。
Windows IDE 提供多种选择以满足用户的不同需求。例如,Microsoft Visual Studio 提供【Win32 控制台应用程序】选项。对于其他系统,查找一个诸如【DOSEXE】、【Console】或【Character Mode】的可执行选项。选择这些模式后,将在一个类控制台窗口中运行可执行程序。选择好正确的项目类型后,使用 IDE 的菜单打开一个新的源代码文件。对于大多数产品而言,使用【文件】菜单就能完成。你可能需要其他步骤将源文件添加到项目中。
通常,Windows IDE 既可处理 C 也可处理 C++,因此要指定待处理的程序是 C 还是 C++。有些产品用项目类型来区分两者,有些产品(如,Microsoft Visual C++)用 .c 文件扩展名来指明使用 C 而不是 C++。当然,大多数 C 程序也可以作为 C++ 程序运行。欲了解 C 和 C++ 的区别,请参阅参考资料 IX。
你可能会遇到一个问题:在程序执行完毕后,执行程序的窗口立即消失。如果不希望出现这种情况,可以让程序暂停,直到按下 Enter 键,窗口才消失。要实现这种效果,可以在程序的最后(return 这行代码之前)添加下面一行代码:
getchar();
该行读取一次键的按下,所以程序在用户按下 Enter 键之前会暂停。有时根据程序的需要,可能还需要一个击键等待。这种情况下,必须用两次 getchar():
getchar();
getchar();
例如,程序在最后提示用户输入体重。用户键入体重后,按下 Enter 键以输入数据。程序将读取体重,第 1 个getchar() 读取 Enter 键,第 2 个 getchar() 会导致程序暂停,直至用户再次按下 Enter 键。如果你现在不知所云,没关系,在学完 C 输出后就会明白。到时,我们会提醒读者使用这种方法。
虽然许多 IDE 在使用上大体一致,但是细节上有所不同。就一个产品的系列而言,不同版本也是如此。要经过一段时间的实践,才会熟悉编译器的工作方式。必要时,还需阅读使用手册或网上教程。
Microsoft Visual Studio 和 C标准
在 Windows 软件开发中,Microsoft Visual Studio 及其免费版本 Microsoft Visual Studio Express 都久负盛名,它们与 C 标准的关系也很重要。然而,微软鼓励程序员从 C 转向 C++ 和 C#。虽然 Visual Studio 支持 C89/90,但是到目前为止,它只选择性地支持那些在 C++ 新特性中能找到的 C 标准(如, long long 类型)。而且,自 2012 版本起,Visual Studio 不再把 C 作为项目类型的选项。尽管如此,本书中的绝大多数程序仍可用 Visual Studio 来编译。在新建项目时,选择 C++ 选项,然后选择【Win32 控制台应用程序】,在应用设置中选择【空项目】。几乎所有的 C 程序都能与 C++ 程序兼容。所以,本书中的绝大多数 C 程序都可作为 C++ 程序运行。或者,在选择 C++ 选项后,将默认的源文件扩展名 .cpp 替换成 .c,编译器便会使用 C 语言的规则代替 C++。
开发工具的选择
C 语言的开发工具有很多选择,包括 Visual Studio、Code::Blocks、CLion、VS Code 以及在线编译工具等。博主在这里特别推荐 VS Code,它不仅轻量高效,还拥有丰富的插件生态系统,能够极大地提升编码效率。本专栏主要使用 VS Code 作为集成开发环境(IDE)来编写代码。
关于 VS Code 的安装与配置,我在这篇博客中进行了详细的描述,从环境搭建到常用插件推荐,应有尽有。如果你对如何顺利配置 VS Code 以进行 C 语言开发感兴趣,欢迎查阅我的博客文章,获取更多实用技巧和建议。文章直达链接:C 语言开发工具的选择,MinGW 的安装与配置,VS Code 的安装与配置、插件推荐
1.9.8 Windows / Linux
许多 Linux 发行版都可以安装在 Windows 系统中,以创建双系统。一些存储器会为 Linux 系统预留空间,以便可以启动 Windows 或 Linux。可以在 Windows 系统中运行 Linux 程序,或在 Linux 系统中运行 Windows 程序。不能通过 Windows 系统访问 Linux 文件,但是可以通过 Linux 系统访问 Windows 文档。
在 Windows 或 macOS 上使用 Linux 系统的方法
如果你想在 Windows 或 macOS 电脑上运行 Linux 系统,可以通过创建虚拟机来实现。推荐使用 VMware Workstation Pro 或 VirtualBox 等虚拟化软件,它们允许你在一个安全的环境中安装和运行多个操作系统。
创建虚拟机并安装 Linux
- 下载并安装虚拟化软件:选择并安装适合你操作系统的虚拟化工具,如 VMware Workstation Pro 或 VirtualBox。
- 创建新虚拟机:按照向导步骤创建新的虚拟机,并为它分配适当的资源(如 CPU、内存等)。
- 安装 Linux 发行版:选择你喜欢的 Linux 发行版(如 Ubuntu、CentOS 等),并通过 ISO 文件安装到虚拟机中。
提高工作效率的工具
- 为了更高效地管理和操作虚拟机中的 Linux 环境,你可以配合使用以下工具:
- FinalShell 或 Xshell:这些终端模拟器支持 SSH 连接,方便远程管理 Linux 系统,并提供丰富的配置选项和便捷的功能。
- 文件共享:通过 VMware 的“共享文件夹”功能或 VirtualBox 的“双向拖放”特性,可以在主机与虚拟机之间轻松共享文件。
- 其他工具:还可以考虑使用 WinSCP 或 FileZilla 等 FTP/SFTP 客户端进行文件传输,或者使用 Docker 实现容器化开发环境。
通过虚拟机的方式,你可以在现有的 Windows 或 macOS 系统上无缝集成 Linux 环境,享受多操作系统带来的灵活性和便利性。对于有兴趣深入探索 Linux 或进行跨平台开发的用户来说,这是一个非常实用的选择。
1.9.9 Macintosh 中的 C
目前,苹果免费提供 Xcode 开发系统下载(过去,它有时免费,有时付费)。它允许用户选择不同的编程语言,包括 C 语言。
Xcode 凭借可处理多种编程语言的能力,可用于多平台,开发超大型的项目。但是,首先要学会如何编写简单的 C 程序。在 Xcode 4.6 中,通过【File】菜单选择【New Project】,然后选择【OS X Application Command Line Tool】,接着输入产品名并选择 C 类型。Xcode 使用 Clang 或 GCC C 编译器来编译 C 代码,它以前默认使用 GCC,但是现在默认使用 Clang。可以设置选择使用哪一个编译器和哪一套 C 标准(因为许可方面的事宜,Xcode 中 Clang 的版本比 GCC 的版本要新)。
UNIX 系统内置 Mac OS X,终端工具打开的窗口是让用户在 UNIX 命令行环境中运行程序。苹果在标准软件包中不提供命令行编译器,但是,如果下载了 Xcode,还可以下载可选的命令行工具,这样就可以使用 clang 和 gcc 命令在命令行模式中编译。
提示:
由于博主使用的是 Windows 操作系统,对于 macOS 上的 C 语言编译器安装与调试,这里就不班门弄斧啦!不过,Mac 用户们别担心,Xcode 和 Homebrew 可是你们的好朋友哦。如果你对如何在 Mac 上安装和配置 GCC 或 Clang 编译器感兴趣,建议去百度或 Google 上搜索一下,那里有大把的教程等着你发现。相信你会轻松找到适合自己的方法,开启愉快的编程之旅!