01_C认识

认识C

1. C语言的起源

​ 1972年,贝尔实验室的丹尼斯·里奇(Dennis Ritch)和肯·汤普逊(Ken Thompson)在开发UNIX操作系统时设计了C语言。然而,C语言不完全是里 奇突发奇想而来,他是在B语言(汤普逊发明)的基础上进行设计。C 语言设计的初衷是将其作为程序员使用的 一种编程工具,因此,其主要目标是成为有用的语言

2. C原因特点

设计特性, 高效性, 可移植性, 强大而又灵活, 面向程序员

2.1 涉及特性

​ C是一门流行的语言,融合了计算机科学理论和实践的控制特性。C语 言的设计理念让用户能轻松地完成自顶向下的规划、结构化编程和模块化设 计。因此,用C语言编写的程序更易懂、更可靠

2.2 高效性

​ C是高效的语言。在设计上,它充分利用了当前计算机的优势,因此 C 程序相对更紧凑,而且运行速度很快。实际上,C 语言具有通常是汇编语言 才具有的微调控制能力(汇编语言是为特殊的中央处理单元设计的一系列内 部指令,使用助记符来表示;不同的 CPU 系列使用不同的汇编语言),可 以根据具体情况微调程序以获得最大运行速度或最有效地使用内存。

2.3 可移植性

​ C是可移植的语言。这意味着,在一种系统中编写的 C程序稍作修改或 不修改就能在其他系统运行。如需修改,也只需简单更改主程序头文件中的 少许项即可。
​ 由于C语言与UNIX关系密切,UNIX系统通常会将C编译器作为软件包的 一部分。安装Linux时,通常也会安装C编译器。供个人计算机使用的C编译 器很多,运行各种版本的Windows和Macintosh(即, Mac)的PC都能找到 合适的C编译器。因此,无论是使用家庭计算机、专业工作站,还是大型 机,都能找到针对特定系统的C编译器。

2.5 强大而又灵活

​ C语言功能强大且灵活;例如,功能 强大且灵活的UNIX操作系统,大部分是用C语言写的,其他语言(如, FORTRAN、Perl、Python、Pascal、LISP、Logo、BASIC)的许多编译器和 解释器都是用C语言编写的。因此,在UNIX机上使用FORTRAN时,最终是 由C程序生成最后的可执行程序。C程序可以用于解决物理学和工程学的问 题,甚至可用于制作电影的动画特效。

2.6 面向程序员

​ C 语言是为了满足程序员的需求而设计的,程序员利用 C 可以访问硬 件、操控内存中的位。C 语言有丰富的运算符,能让程序员简洁地表达自己 的意图。C没有Pascal严谨,但是却比C++的限制多。这样的灵活性既是优点 也是缺点。

​ 优点是,许多任务用C来处理都非常简洁(如,转换数据的格 式);

​ 缺点是,你可能会犯一些莫名其妙的错误,这些错误不可能在其他语 言中出现。C 语言在提供更多自由的同时,也让使用者承担了更大的责任。
​ 另外,大多数C实现都有一个大型的库,包含众多有用的C函数。这些 函数用于处理程序员经常需要解决的问题。

3. 计算机工作原理

	CPU 的工作非常简单,至少从以下简短的描述中看是这样。它从内存 中获取并执行一条指令,然后再从内存中获取并执行下一条指令,诸如此类 (一个吉赫兹的CPU一秒钟能重复这样的操作大约十亿次,因此,CPU 能以 惊人的速度从事枯燥的工作)。CPU 有自己的小工作区——由若干个寄存 器组成,每个寄存器都可以储存一个数字。一个寄存器储存下一条指令的内 存地址,CPU 使用该地址来获取和更新下一条指令。在获取指令后,CPU在 另一个寄存器中储存该指令,并更新第1个寄存器储存下一条指令的地址。 CPU能理解的指令有限(这些指令的集合叫作指令集)。而且,这些指令相 当具体,其中的许多指令都是用于请求计算机把一个数字从一个位置移动到 另一个位置。例如,从内存移动到寄存器。

​ 一,储存在计算机中的所有内容都是数 字。计算机以数字形式储存数字和字符(如,在文本文档中使用的字母)。 每个字符都有一个数字码。计算机载入寄存器的指令也以数字形式储存,指 令集中的每条指令都有一个数字码。

​ 二,计算机程序最终必须以数字指令 码(即,机器语言)来表示。

​ 简而言之,计算机的工作原理是:如果希望计算机做某些事,就必须为 其提供特殊的指令列表(程序),确切地告诉计算机要做的事以及如何做。你必须用计算机能直接明白的语言(机器语言)创建程序。这是一项繁琐、 乏味、费力的任务。计算机要完成诸如两数相加这样简单的事,就得分成类 似以下几个步骤。
​ 1.从内存位置2000上把一个数字拷贝到寄存器1。
​ 2.从内存位置2004上把另一个数字拷贝到寄存器2。
​ 3.把寄存器2中的内容与寄存器1中的内容相加,把结果储存在寄存器1中。
​ 4.把寄存器1中的内容拷贝到内存位置2008。
而你要做的是,必须用数字码来表示以上的每个步骤!

4. 编译器

​ 在计算机看来,高级指令就是一堆无法理解的无用数 据。编译器在这里派上了用场。编译器是把高级语言程序翻译成计算机能理 解的机器语言指令集的程序。程序员进行高级思维活动,而编译器则负责处 理冗长乏味的细节工作。

​ 编译器还有一个优势。一般而言,不同CPU制造商使用的指令系统和编 码格式不同。例如,用Intel Core i7 (英特尔酷睿i7)CPU编写的机器语言程 序对于ARM Cortex-A57 CPU而言什么都不是。但是,可以找到与特定类型 CPU匹配的编译器。因此,使用合适的编译器或编译器集,便可把一种高级 语言程序转换成供各种不同类型 CPU 使用的机器语言程序。一旦解决了一 个编程问题,便可让编译器集翻译成不同 CPU 使用的机器语言。

​ 简而言之,高级语言(如C、Java、Pascal)以更抽象的方式描述行 为,不受限于特定CPU或指令集。而且,高级语言简单易学,用高级语言编 程比用机器语言编程容易得多

5. 语言标准

​ 目前,有许多C实现可用。在理想情况下,编写C程序时,假设该程序 中未使用机器特定的编程技术,那么它的运行情况在任何实现中都应该相 同。要在实践中做到这一点,不同的实现要遵循同一个标准。
​ C语言发展之初,并没有所谓的C标准。1987年,布莱恩·柯林汉(Brian Kernighan)和丹尼斯·里奇(Dennis Ritchie)合著的The C Programming Language(《C语言程序设计》)第1版是公认的C标准,通常称之为K&R C 或经典C。特别是,该书中的附录中的“C语言参考手册”已成为实现C的指导 标准。例如,编译器都声称提供完整的K&R实现。虽然这本书中的附录定 义了C语言,但却没有定义C库。与大多数语言不同的是,C语言比其他语言 更依赖库,因此需要一个标准库。实际上,由于缺乏官方标准,UNIX实现 提供的库已成为了标准库。

5.1 第一个ANSI/ISO C标准

​ 随着C的不断发展,越来越广泛地应用于更多系统中,C社区意识到需 要一个更全面、更新颖、更严格的标准。鉴于此,美国国家标准协会 (ANSI)于 1983 年组建了一个委员会(X3J11),开发了一套新标准,并 于1989年正式公布。该标准(ANSI C)定义了C语言和C标准库。国际标准 化组织于1990年采用了这套C标准(ISO C)。ISO C和ANSI C是完全相同的 标准。ANSI/ISO标准的最终版本通常叫作C89(因为ANSI于1989年批准该标 准)或C90(因为ISO于1990年批准该标准)。另外,由于ANSI先公布C标 准,因此业界人士通常使用ANSI C。
在该委员会制定的指导原则中,最有趣的可能是:保持 C的精神。委员 会在表述这一精神时列出了以下几点:

  • 信任程序员;

  • 不要妨碍程序员做需要做的事;

  • 保持语言精练简单;

  • 只提供一种方法执行一项操作;

  • 让程序运行更快,即使不能保证其可移植性。

  • 在最后一点上,标准委员会的用意是:作为实现,应该针对目标计算机 来定义最合适的某特定操作,而不是强加一个抽象、统一的定义。在学习C 语言过程中,许多方面都反映了这一哲学思想。

5.2 C99标准

​ 1994年,ANSI/ISO联合委员会(C9X委员会)开始修订C标准,最终发 布了C99标准。该委员会遵循了最初C90标准的原则,包括保持语言的精练 简单。委员会的用意不是在C语言中添加新特性,而是为了达到新的目标。 第1个目标是,支持国际化编程。例如,提供多种方法处理国际字符集。

​ 第2 个目标是,“调整现有实践致力于解决明显的缺陷”。因此,在遇到需要将C 移至64位处理器时,委员会根据现实生活中处理问题的经验来添加标准。

​ 第 3个目标是,为适应科学和工程项目中的关键数值计算,提高C的适应性, 让C比FORTRAN更有竞争力。
​ 这3点(国际化、弥补缺陷和提高计算的实用性)是主要的修订目标。 在其他方面的改变则更为保守,例如,尽量与C90、C++兼容,让语言在概 念上保持简单。用委员会的话说:“委员会很满意让C++成为大型、功能 强大的语言”。
​ C99的修订保留了C语言的精髓,C仍是一门简洁高效的语言。本书指出 了许多C99修改的地方。虽然该标准已发布了很长时间,但并非所有的编译 器都完全实现C99的所有改动。因此,你可能发现C99的一些改动在自己的 系统中不可用,或者只有改变编译器的设置才可用。

5.3 C11标准

维护标准任重道远。标准委员会在2007年承诺C标准的下一个版本是C1X,2011年终于发布了C11标准。此次,委员会提出了一些新的指导原 则。出于对当前编程安全的担忧,不那么强调“信任程序员”目标了。而且, 供应商并未像对C90那样很好地接受和支持C99。这使得C99的一些特性成为 C11的可选项。因为委员会认为,不应要求服务小型机市场的供应商支持其 目标环境中用不到的特性。另外需要强调的是,修订标准的原因不是因为原 标准不能用,而是需要跟进新的技术。例如,新标准添加了可选项支持当前 使用多处理器的计算机。

5.4 C语言七步骤

在这里插入图片描述

5.4.1 第一步: 定义程序目标

​ 在动手写程序之前,要在脑中有清晰的思路。想要程序去做什么首先自 己要明确自己想做什么,思考你的程序需要哪些信息,要进行哪些计算和控 制,以及程序应该要报告什么信息。在这一步骤中,不涉及具体的计算机语 言,应该用一般术语来描述问题

5.4.2 第二步: 设计程序

​ 对程序应该完成什么任务有概念性的认识后,就应该考虑如何用程序来 完成它。例如,用户界面应该是怎样的?如何组织程序?目标用户是谁?准 备花多长时间来完成这个程序?
​ 除此之外,还要决定在程序(还可能是辅助文件)中如何表示数据,以 及用什么方法处理数据。学习C语言之初,遇到的问题都很简单,没什么可 选的。但是,随着要处理的情况越来越复杂,需要决策和考虑的方面也越来 越多。通常,选择一个合适的方式表示信息可以更容易地设计程序和处理数 据。
​ 再次强调,应该用一般术语来描述问题,而不是用具体的代码。但是, 你的某些决策可能取决于语言的特性。例如,在数据表示方面,C的程序员 就比Pascal的程序员有更多选择。

5.4.3 第三步: 编写代码

​ 在程序有了清晰的设计后,就可以通过编写代码来实现它了。也就是说,将你的设计构思转变为 C 语言。这里是你真正需要使用 C 知识的地方。你可以在纸上勾画你的想法,但最终必须将代码输入计算机。一般来说,需要使用文本编辑器来创建一种称为源代码的文件,该文件包含你的程序设计的的 C 实现形式。

5.4.4 第四步:编译

​ 接下来的这一步是编译源代码。编译的细节取决于编程的环境。编译器是把源代码转换成可执行代码的程序。可执行代码 是用计算机的机器语言表示的代码。这种语言由数字码表示的指令组成。不同的计算机使用不同的机器语言方案。C 编译器负责把C代码翻 译成特定的机器语言。此外,C编译器还将源代码与C库(库中包含大量的 标准函数供用户使用,如printf()和scanf())的代码合并成最终的程序(更精 确地说,应该是由一个被称为链接器的程序来链接库函数,但是在大多数系 统中,编译器运行链接器)。其结果是,生成一个用户可以运行的可执行文 件,其中包含着计算机能理解的代码。

​ 编译器还会检查C语言程序是否有效。如果C编译器发现错误,就不生 成可执行文件并报错。理解特定编译器报告的错误或警告信息是程序员要掌 握的另一项技能。

5.4.5 运行程序

​ 传统上,可执行文件是可运行的程序。在常见环境(包括Windows命令 提示符模式、UNIX终端模式和Linux终端模式)中运行程序要输入可执行文 件的文件名,而其他环境可能要运行命令(如,在VAX中的VMS[2])或一 些其他机制。例如,在Windows和Macintosh提供的集成开发环境(IDE) 中,用户可以在IDE中通过选择菜单中的选项或按下特殊键来编辑和执行C 程序。最终生成的程序可通过单击或双击文件名或图标直接在操作系统中运 行。

5.4.6 第六步: 测试和调试程序

​ 程序能运行是个好迹象,但有时也可能会出现运行错误。接下来,应该 检查程序是否按照你所设计的思路运行。你会发现你的程序中有一些错误, 计算机行话叫作bug。查找并修复程序错误的过程叫调试。学习的过程中不 可避免会犯错,学习编程也是如此。因此,当你把所学的知识应用于编程 时,最好为自己会犯错做好心理准备。随着你越来越老练,你所写的程序中 的错误也会越来越不易察觉。
​ 将来犯错的机会很多。你可能会犯基本的设计错误,可能错误地实现了 一个好想法,可能忽视了输入检查导致程序瘫痪,可能会把圆括号放错地 方,可能误用 C语言或打错字,等等。把你将来犯错的地方列出来,这份错 误列表应该会很长。
看到这里你可能会有些绝望,但是情况没那么糟。现在的编译器会捕获 许多错误,而且自己也可以找到编译器未发现的错误

5.4.7 第七步: 维护代码和修改代码

​ 创建完程序后,你发现程序有错,或者想扩展程序的用途,这时就要修改程序。如果在编写程序时清楚地做了注释并采用了合理 的设计方案,这些事情都很简单。

5.4.8 说明

​ 编程并非像描述那样是一个线性的过程。有时,要在不同的步骤之间往 复。例如,在写代码时发现之前的设计不切实际,或者想到了一个更好的解 决方案,或者等程序运行后,想改变原来的设计思路。对程序做文字注释为 今后的修改提供了方便。
​ 刚开始学习时,编写的程序非常简单,完全可以 在脑中构思好整个过程。即使写错了,也很容易发现。但是,随着编写的程 序越来越庞大、越来越复杂,动脑不动手可不行,而且程序中隐藏的错误也 越来越难找。最终,那些跳过前两个步骤的人往往浪费了更多的时间,因为 他们写出的程序难看、缺乏条理、让人难以理解。要编写的程序越大越复 杂,事先定义和设计程序环节的工作量就越大。
​ 磨刀不误砍柴工,应该养成先规划再动手编写代码的好习惯,用纸和笔 记录下程序的目标和设计框架。这样在编写代码的过程中会更加得心应手、 条理清晰

6. 编译机制

​ 生成程序的具体过程因计算机环境而异。C是可移植性语言,因此可以 在许多环境中使用,包括UNIX、Linux、MS-DOS(一些人仍在使用)、 Windows和Macintosh OS。有些产品会随着时间的推移发生演变或被取代, 首先,来看看许多C环境(包括上面提到的5种环境)共有的一些方 面。虽然不必详细了解计算机内部如何运行C程序,但是,了解一下编程机 制不仅能丰富编程相关的背景知识,还有助于理解为何要经过一些特殊的步 骤才能得到C程序。

​ 用C语言编写程序时,编写的内容被储存在文本文件中,该文件被称为 源代码文件(source code file)。大部分C系统,包括之前提到的,都要求文 件名以.c结尾(如,wordcount.c和budget.c)。在文件名中,点号(.)前面 的部分称为基本名(basename),点号后面的部分称为扩展名 (extension)。因此,budget是基本名,c是扩展名。基本名与扩展名的组合 (budget.c)就是文件名。文件名应该满足特定计算机操作系统的特殊要 求。例如,MS-DOS是IBM PC及其兼容机的操作系统,比较老旧,它要求基 本名不能超过8个字符。因此,刚才提到的文件名wordcount.c就是无效的 DOS文件名。有些UNIX系统限制整个文件名(包括扩展名)不超过14个字 符,而有些UNIX系统则允许使用更长的文件名,最多255个字符。Linux、 Windows和Macintosh OS都允许使用长文件名。

7. 目标代码文件,可执行文件和库

​ C编程的基本策略是,用程序把源代码文件转换为可执行文件(其中包含可直接运行的机器语言代码)。典型的C实现通过编译和链接两个步骤来 完成这一过程。**编译器把源代码转换成中间代码,链接器把中间代码和其他 代码合并,生成可执行文件。**C 使用这种分而治之的方法方便对程序进行模 块化,可以独立编译单独的模块,稍后再用链接器合并已编译的模块。通过 这种方式,如果只更改某个模块,不必因此重新编译其他模块。另外,链接 器还将你编写的程序和预编译的库代码合并。

​ 中间文件有多种形式。我们在这里描述的是最普遍的一种形式,即把源代码转换为机器语言代码,并把结果放在目标代码文件(或简称目标文件) 中(这里假设源代码只有一个文件)。虽然目标文件中包含机器语言代码, 但是并不能直接运行该文件。因为目标文件中储存的是编译器翻译的源代 码,这还不是一个完整的程序。
​ 目标代码文件缺失启动代码(startup code)。启动代码充当着程序和操 作系统之间的接口。例如,可以在MS Windows或Linux系统下运行IBM PC兼 容机。这两种情况所使用的硬件相同,所以目标代码相同,但是Windows和 Linux所需的启动代码不同,因为这些系统处理程序的方式不同。
​ 目标代码还缺少库函数。几乎所有的C程序都要使用C标准库中的函 数。例如,concrete.c中就使用了 printf()函数。目标代码文件并不包含该函数的代码,它只包含了使用 printf()函数的指令。printf()函数真正的代码储存 在另一个被称为库的文件中。库文件中有许多函数的目标代码。
链接器的作用是,把你编写的目标代码、系统的标准启动代码和库代码 这 3 部分合并成一个文件,即可执行文件。对于库代码,链接器只会把程序 中要用到的库函数代码提取出来
image

​ 目标文件和可执行文件都由机器语言指令组成的。然而,目 标文件中只包含编译器为你编写的代码翻译的机器语言代码,可执行文件中 还包含你编写的程序中使用的库函数和启动代码的机器代码。

​ 在有些系统中,必须分别运行编译程序和链接程序,而在另一些系统 中,编译器会自动启动链接器,用户只需给出编译命令即可

8. UNIX系统

​ 由于C语言因UNIX系统而生,也因此而流行,所以我们从UNIX系统开始

1.在UNIX系统上编辑

​ UNIX, C没有自己的编辑器,但是可以使用通用的UNIX编辑器,如 emacs、jove、vi或X Window System文本编辑器。
​ 作为程序员,要负责输入正确的程序和为储存该程序的文件起一个合适 的文件名。如前所述,文件名应该以.c结尾。注意,UNIX区分大小写。创建一个cc inform.c源文件,源文件是整个编译过 程的开始,不是结束。

2.在UNIX上编译

​ 虽然在我们看来,程序完美无缺,但是对计算机而言,这是一堆乱码。 计算机不明白#include 和printf是什么(也许你现在也不明白,但是学到后面 就会明白,而计算机却不会)。如前所述,我们需要编译器将我们编写的代 码(源代码)翻译成计算机能看懂的代码(机器代码)。最后生成的可执行 文件中包含计算机要完成任务所需的所有机器代码。
​ 以前,UNIX C编译器要调用语言定义的cc命令。但是,它没有跟上标 准发展的脚步,已经退出了历史舞台。但是,UNIX系统提供的C编译器通常 来自一些其他源,然后以cc命令作为编译器的别名。因此,虽然在不同的系 统中会调用不同的编译器,但用户仍可以继续使用相同的命令。
编译inform.c,要输入以下命令: cc inform.c
​ 几秒钟后,会返回 UNIX 的提示,告诉用户任务已完成。如果程序编写 错误,你可能会看到警告或错误消息,但我们先假设编写的程序完全正确 (如果编译器报告void的错误,说明你的系统未更新成ANSI C编译器,只需 删除void即可)。如果使用ls命令列出文件,会发现有一个a.out文件(见图 1.5)。该文件是包含已翻译(或已编译)程序的可执行文件。要运行该文 件,只需输入:a.out
在这里插入图片描述

​ 如果要储存可执行文件(a.out),应该把它重命名。否则,该文件会被 下一次编译程序时生成的新a.out文件替换。
如何处理目标代码?C 编译器会创建一个与源代码基本名相同的目标代 码文件,但是其扩展名是.o。在该例中,目标代码文件是 inform.o。然而, 却找不到这个文件,因为一旦链接器生成了完整的可执行程序,就会将其删 除。如果原始程序有多个源代码文件,则保留目标代码文件

9. GNU编译器集合和LLVM项目

​ 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的别名。
​ LLVM项目成为cc的另一个替代品。该项目是与编译器相关的开源软件集合,始于伊利诺伊大学的2000份研究项目。它的 Clang编译器处理 C代码,可以通过 clang调用。有多种版本供不同的平台使用,包括Linux。2012 年,Clang成为FreeBSD的默认C编译器。Clang也对最新的C标准支持得很 好。
​ GNU和LLVM都可以使用-v选项来显示版本信息,因此各系统都使用cc 别名来代替gcc或clang命令。以下组合:cc -v
​ 显示你所使用的编译器及其版本。
​ gcc和clang命令都可以根据不同的版本选择运行时选项来调用不同C标准。
​ gcc -std=c99 inform.c[3]
​ gcc -std=c1x inform.c
​ gcc -std=c11 inform.c

​ 第1行调用C99标准,第2行调用GCC接受C11之前的草案标准,第3行调 用GCC接受的C11标准版本。Clang编译器在这一点上用法与GCC相同

10. Linux系统

Linux是一个开源、流行、类似于UNIX的操作系统,可在不同平台(包 括PC和Mac)上运行。在Linux中准备C程序与在UNIX系统中几乎一样,不 同的是要使用GNU提供的GCC公共域C编译器。编译命令类似于:
gcc inform.c
注意,在安装Linux时,可选择是否安装GCC。如果之前没有安装 GCC,则必须安装。通常,安装过程会将cc作为gcc的别名,因此可以在命 令行中使用cc来代替gcc。最新版本GCC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值