概述
起源
1972 年, 贝尔实验室 的 丹尼斯·里奇 (Dennis Ritch)和 肯·汤普逊 (Ken Thompson) 在开发UNIX操作系统时设计了 C 语言
。
然而,C语言不完全是里奇突发奇想而来,他是在 B语言
(汤普逊发明)的基础上进行设计。至于B语言的起源,那是另一个故事。
C 语言设计的初衷是将其作为程序员使用的一种编程工具,因此,其主要目标是成为有用的语言。
虽然绝大多数语言都以实用为目标,但是通常也会考虑其他方面。例如, Pascal
的主要目标是为更好地学习编程原理提供扎实的基础:而 BASC
的主要目标是开发出类似英文的语言,让不熟悉计算机的学生轻松学习编程。
这些目标固然很重要,但是随着计算机的迅猛发展,它们已经不是主流语言。
然而,最初为程序员设计开发的 C 语言,现在已成为首选的编程语言之一。
特性
设计特性
C 是一门流行的语言,融合了计算机科学理论和实践的控制特性。
C 语言的设计理念让用户能轻松地完成 自顶向下 的规划、 结构化 编程和 模块化 设计。因此,用 C 语言编写的程序更易懂、更可靠。
高效性
C 是高效的语言。在设计上,它充分利用了当前计算机的优势,因此C程序相对更紧凑,而且运行速度很快。
实际上,C 语言具有通常是汇编语言才具有的 微调控制能力 (汇编语言是为特殊的中央处理单元设计的一系列内部指令,使用助记符来表示:不同的 CU 系列使用不同的汇编语言),可以根据具体情况微调程序以获得最大运行速度或最有效地使用内存。
简单来说,C 语言可以 直接操纵内存 ,这使得它有无与伦比的速度。
可移植性
C 是可移植的语言。这意味着,在一种系统中编写的C程序稍作修改或不修改就能在其他系统运行。如需修改,也只需简单更改主程序头文件中的少许项即可。
大部分语言都希望成为可移植语言,但是,如果经历过把 IBM PC BASIC 程序转换成苹果 BASIC (两者是近亲),或者在 UNIX 系统中运行IBM大型机的 FORTRAN 程序的人都知道,移植是最麻烦的事。
C 语言是可移植方面的佼佼者,几乎所有的操作系统都可以找到相对应的 C 编译器,
编译器
高级编程语言以多种方式简化了编程工作。
在计算机看来,高级指令就是一堆无法理解的无用数据。
编译器在这里派上了用场,编译器是把高级语言程序翻译成计算机能理解的机器语言指令集的程序。程序员进行高级思维活动,而编译器则负责处理冗长乏味的细节工作。
编译器还有一个优势。一般而言,不同 CPU 制造商使用的指令系统和编码格式不同。例如用Intel Core i7(英特尔酷睿i7) CPU编写的机器语言程序对于ARM Cortex-A57CPU而言什么都不是。
但是,可以找到与特定类型 CPU 匹配的编译器。
因此,使用合适的编译器或编译器集,便可把一种高级语言程序转换成供各种不同类型CPU使用的机器语言程序。一旦解决了一个编程问题,便可让编译器集翻译成不同 CPU 使用的机器语言。
简而言之,高级语言以更抽象的方式描述行为,不受限于特定 CPU 或指令集。
语言标准
随着 C 的不新发展,越来越广泛地应用于更多系统中,C 社区意识到需要一个更全面、更新颖、更严格的标准。
鉴于此, 美国国家标准协会 (ANSI)于 1983 年组建了一个委员会(X3J11),开发了一套新标准,并于 1989 年正式公布。
该标准(ANSI C)定义了 C 语言和 C 标准库。国际标准化组织于 1990 年采用了这套 C 标准(ISOC)。ISOC 和 ANSI C是 完全相同 的标准。ANSI/ISO
标准的最终版本通常叫作 C89
(因为ANSI于1989年批准该标准)或 C90
(因为IS0于1990年批准该标准)。
另外,由于 ANSI 先公布 C 标准,因此业界人士通常使用 ANSI C。在该委员会制定的指导原则中,最有趣的可能是:保持C的精神。委员会在表述这一精神时列出了以下几点:
信任程序员。
不要妨碍程序员做需要做的事。
保持语言精练简单。
只提供一种方法执行一项操作。
让程序运行更快,即使不能保证其可移植性。
在最后一点上,标准委员会的用意是:作为实现,应该针对目标计算机来定义最合适的某特定操作,而不是强加一个抽象、统一的定义 。在学习 C 语言过程中,许多方面都反映了这一哲学思想。
TIP
在第一个 C 标准诞生前,已经有非常多的 C 程序,它们并没有遵守 C 标准。
为了让这些古老程序可以正常运行,即使 不完全 使用 C 标准来编写 C 程序,依然可以运行。
即便如此,在开发过程中,请遵守 C 标准。
目标代码文件、可执行文件和库
C 编程的基本策略是,用程序把源代码文件转换为可执行文件(其中包含可直接运行的机器语言代码)。
典型的 C 实现通过 编译
和 链接
两个步骤来完成这一过程。
编译器
把源代码转换成 中间代码 , 链接器
把中间代码和其他代码合并,生成 可执行文件 。
C 使用这种 分而治之 的方法方便对程序进行模块化,可以独立编译单独的模块,稍后再用链接器合并已编译的模块。
通过这种方式,如果只更改某个模块,不必因此重新编译其他模块。另外,链接器还将你编写的程序和预编译的库代码合并。
中间文件有多种形式,我们在这里描述的是最普遍的一种形式,即把源代码转换为 机器语言 代码,并把结果放在目标代码文件(或简称目标文件)中(这里假设源代码只有一个文件)。
虽然目标文件中包含机器语言代码,但是并不能直接运行该文件,因为目标文件中储存的是编译器翻译的源代码,这还不是一个完整的程序。
启动代码
目标代码文件缺失 启动代码
(startup code),启动代码充当着程序和操作系统之间的接口。例如,可以在 MS Windows 或 Linux 系统下运行 IBM PC 兼容机,这两种情况所使用的硬件相同,所以目标代码相同,。
但是 Windows 和 Linux 所需的启动代码不同,因为这些系统处理程序的方式不同。
因此在不同的平台运行 C 程序,需要不同的启动代码。
库代码
目标代码还缺少 库函数 。几乎所有的 C 程序都要使用 C 标准库中的函数。例如 printf()
函数,这些函数不需要我们来写,但我们需要把别人实现好的代码放进来。
目标代码文件并不包含该函数的代码,它只包含了使用 printf()
函数的指令。 printf()
函数真正的代码储存在另一个被称为 **库 ** 的文件中。
库文件中有许多函数的目标代码。链接器的作用是,把你编写的 目标代码 、系统的 标准启动代码 和 库代码 这 3 部分合并成一个文件,即可执行文件。
对于库代码,链接器只会把程序中要用到的库函数代码提取出来。