21天学通C语言(第7版)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-19 12:52:26

点击下载

作者:[美]布Bradley Jones Peter Aitken Dean Miller

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

21天学通C语言(第7版)

21天学通C语言(第7版)试读:

前言

从书名便可看出,通过学习本书,你可以自学C程序设计语言。在众多语言(如C++、JAVA和C#)中,C仍然是学习程序设计语言的首选。第1课中将详细介绍其中的原因。选择C作为程序设计语言是明智之举。

与市面上其他C语言的书籍相比,本书的讲解逻辑更清晰,初学者更容易理解。之前的6个版本一直在畅销书排行榜上遥遥领先,广受读者赞誉!本书为读者量身定制,每天只需花一小时便可学完一课内容。读者不需要有任何编程经验,当然,如果有其他语言的基础(如BASIC),学起来会更快。本书的重点是介绍C语言,不会指定计算机和编译器。无论你的计算机使用的是Windows系统、Mac OS系统还是UNIX系统,都可以学习C语言。本书特色

本书包含一些特殊栏,有助于启发读者学习。语法框教你如何使用C语言中特殊的概念,提供精炼的示例和详尽的解释。可以查看下面的示例,预览语法框的概要。(你尚未学习第1课,不理解其中的内容没关系。)语 法#include printf( 格式字符串[,参数,...] );

printf()函数接受一系列的参数。每个参数都在格式字符串中有相应的转换说明。printf()将格式的信息打印在标准输出设备上(通常是显示屏)。使用printf()函数时,必须包含标准输入/输出头文件stdio.h。

格式字符串必不可少,而参数是可选的。每个参数都必须有转换说明。格式字符串中可包含转义序列。以下所示的程序示例中调用了prin转换说明tf(),并打印输出。

程序示例1

输入#include int main( void ){ printf( "This is an example of something printed!");}

输出This is an example of something printed!

程序示例2

输入printf( "This prints a character, %c\na number, %d\na floating point,%f", 'z', 123, 456.789 );

输出This prints a character, za number, 123a floating point, 456.789

本书的另一大特色是DO/DON’T栏,其中指出读者应该做什么,不应该做什么。DODON’T阅读本节余下内请勿跳过随堂测验和课后练习。如容,将解释本课末尾的果你可以完成本课的课后研习,说明你课后研习部分。已准备好学习新的内容。

另外,本书还包含大量的提示、注意和警告栏。提示提供使用C语言的一些捷径和技巧。注意提供其他具体的细节,帮助读者更好地理解C语言的概念。警告提醒读者避免一些潜在的问题。

本书用大量的程序示例解释C语言的特性和概念,可以把这些示例用在自己的程序中。每个程序都分成3部分(输入、输出和分析)来讨论,并分别以黑体字和标出。

每课末尾的答疑部分包含本课相关内容的常见问题与回答。另外,每课还设有课后研习,包含小测验和练习题。小测验用于检测读者对本课概念的掌握程度。如果要核对或查看答案,请翻阅附录D。

只阅读本书是学不会C语言的,如果你想成为一个程序员,就必须写程序。解答小测验中的问题是很好地练习,还应该尝试解答每一道练习题。写代码是学习C语言的最佳途径。

排错练习最有益。在C语言中,bug是程序的错误。排错练习中的代码示例中包含一些常见错误(bug)。你要找出这些错误并修正它们。如果遇到任何困难,可以查阅附录D。

随着进一步学习本书,有些练习的答案会很长,有些练习会有多种答案。因此,后面的课程可能不会提供所有练习的答案。本书使用的约定

本书使用不同的字体,不仅帮助读者在阅读过程中区别C代码和常规英文,而且有助于读者识别重要的概念。本书中出现的C代码一律使用等宽字体(如,monospace)。在程序示例的输入和输出中,用户键入的内容均使用加粗等宽字体(如,blod monospace)。占位符(术语,代表代码中你实际键入的内容)用斜体等宽字体(如,①②italic monospace)。新术语和重要术语用斜体(如italic)标出。

① 译者注:译文采用仿宋体(如,仿宋体)。

② 译者注:译文采用楷体及中英并陈(如,函数(function))。第1部分C语言基础第1课初识C语言

欢迎学习本课程!本课将是你成为C程序员高手之路的开始。本课将介绍以下内容:● 在众多程序设计语言中,为什么C语言是首选● 程序开发周期中的步骤● 如何编写、编译和运行第1个C程序● 编译器和链接器生成的错误消息1.1 C语言发展简史

读者一定非常好奇C语言的起源及其名字的由来。1972年,丹尼斯·里奇(Dennis Ritchie)在贝尔电话实验室发明了C语言。发明该语言的初衷是为了设计UNIX操作系统(现在用于许多计算机上)。从一开始,C语言就致力于为程序员排忧解难。

C语言因功能强大和灵活,很快便广泛应用于贝尔实验室以外的各领域。世界各地的程序员都开始用它来编写程序。不久,各个组织开始使用自己版本的C语言,但是各种版本C语言的实现存在细微差别,这让程序员头疼不已。为解决这一问题,美国国家标准协会(ANSI)于1983年成立了一个委员会,制定了C语言的标准定义,即ANSI C标准。当前的C编译器(极少例外)都支持这个标准。注意

C语言极少改动,最新的改动是2011年的ANSI C11标准。该标准在C语言中添加了一些新特性(本书已涵盖)。但是,旧的编译器可能不支持目前的最新标准。

为什么叫C语言?之所以叫C语言,是因为它的前辈是B语言。B语言由贝尔实验室(Bell Labs)的肯·汤普逊(Ken Thompson)发明。至于为什么叫B语言就不言而喻了。1.2 为何要使用C语言

如今,在计算机程序设计领域,可供选择的高级语言很多,如C、Perl、BASIC、Java、PHP和C#。这些语言都能很好地完成程序设计任务,但是基于以下几个原因,使得C语言仍是众多计算机专业人士的首选。● C语言功能强大、十分灵活。你完全想不到C语言能帮你完成什

么工作。语言本身不会给你带来任何限制。C语言可用于各种操

作系统、文字处理软件、图形、电子表格、甚至是其他语言的编

译器。● C语言非常流行,是众多专业程序员的首选。因此,市面上很容

易找到各种C语言的编译器和辅助工具。● C是一门可移植语言。可移植意味着在一台计算机系统(如,

IBM PC)中写出的C程序,无需修改或少量修改便可在其他操作

系统(如,DEC VAX系统)中运行。除此之外,在微软Windows

操作系统中编写的程序,也可移到Linux系统的机器中(无需修

改或少量修改)。ANSI标准进一步改善了C语言的可移植性,为

C编译器添加了一系列规则。● C语言短小精悍,只包含少量关键字(keyword)。C语言以关键

字为基础构建语言的功能,C语言编程离不开关键字。你可能认

为一门语言中关键字(有时也称为保留字(reserved word))越多

功能越强大,其实并非如此。● C语言是模块化的。C代码可以(且应该)以例程形式编写,这

些例程称为函数(function)。函数可复用于其他程序或应用。

通过传递信息给函数,可以创建有用且能重复使用的代码。

正是由于上述特性,使得C语言成为程序设计语言的首选。那C++怎样?你可能在别处了解过C++和面向对象程序设计(object-oriented programming)技术。C与C++有何不同?是否应该自学C++?

别担心!C++是C的超集,这意味着C++中不仅包含了C的所有内容,还添加了面向对象程序设计的新特性。如果你继续学习C++,会发现C中所学的知识几乎都可应用于C++。学习C语言,不仅学了一门现在功能最强大、最流行的程序设计语言,而且还为学习面向对象程序设计做好了准备。

另一门备受关注的语言是Java。Java和C++一样,也是基于C语言发展起来的。如果将来学习Java,你会发现在C语言中学过的所有知识点都用得上。

最新的一门语言是C#(发音为“See-Sharp”)。与C++和Java类似,C#也是一门源自C的面向对象语言。同样,在C语言中学到的内容可以直接用于C#程序设计。注意

许多学过C语言的人,后来都会选择学习C++、Java或C#。先学习C语言,再学其他语言会轻松许多。1.3 准备编程

解决问题之前肯定要经过一些步骤。首先,必须定义问题。如果不知道问题是什么,根本不可能找到解决方案!知道问题所在,才能设法修正。如果你想到一个解决方案,通常会去实施它。之后,你必须知道问题是否解决了。这样解决问题的逻辑可以应用到各个领域,包括程序设计。

用C语言创建一个程序(或者说,用任意语言编写的计算机程序)时,应该遵循以下步骤的顺序。

1.确定程序的目标。

2.确定在编程时所使用的方法。

3.创建程序解决问题。

4.运行程序查看结果。

目标(步骤1)可能是编写一个文字处理软件或数据库程序。简单的目标可以是在屏幕上显示你的姓名。没有目标就不可能写出程序,因此,如果要写程序就已经准备好第一步了。

接下来,第2步要确定你编写的程序中所使用的方法。你是否需要计算机程序来解决问题?要记录什么信息?程序中要使用什么公式?在这一步中,应该明确自己需要了解什么知识以及实施方案的具体步骤。例如,假设你要写一个计算圆面积的程序。第1步已经完成,因为目标明确:计算圆的面积。第2步,分析如何确定圆的面积。例2如,假设用户提供圆的半径。了解这些以后,便可通过公式πr计算得出结果。现在,可以继续第3步和第4步,进入程序开发周期。1.4 程序开发周期

程序开发周期的步骤是:第1步,使用编辑器创建磁盘文件保存源代码;第2步,编译源代码创建目标文件;第3步,链接已编译的代码创建可执行文件;第4步,运行程序,看看程序是否能按原计划运行。1.4.1 创建源代码

源代码(source code)是一系列语句或命令,指导计算机执行特定的任务。如前所述,程序开发周期的第1步是在编辑器中输入源代码。例如,下面这行C源代码:printf("Hello,Mom!")

这行语句命令计算机在屏幕上显示消息Hello,Mom!(到目前为止,不用管如何执行语句)。1.4.2 使用编辑器

绝大多数C程序都是在集成开发环境(IDE)中进行编译的,集成开发环境能让你在它的编辑器中输入程序并编译程序,同时还能调试和创建应用(这些概念今后会学到)。你完全不必使用额外的编辑器。尽管如此,绝大多数计算机系统仍包含可用作编辑器的程序。如果使用Linux或UNIX系统,可以使用ed、ex、edit或vi编辑器。如果使用微软Windows系统,可以使用Notepad或WordPad。

大部分文字处理软件都使用特殊的代码格式化文档,在其他程序中无法正确读取这些代码。美国信息交换标准码(ASCII)几乎为所有的程序(包括C)指定了一个标准文本。许多文字处理软件(如WordPerfect、Microsoft Word、WordPad和WordStar)都能以ASCII形式保存源文件(将源文件作为文本文件而不是文档文件保存)。如果要以ASCII文件保存一个文字处理软件的文件,在保存时必须选择ASCII选项或文本选项。

如果不想用上述编辑器,也可以购买其他的编辑器。市面上有许多专门为输入源代码而设计的软件包(包括商业的和共享软件)。

在保存文件之前必须先给文件命名。文件名应描述该程序的用途。另外,保存C程序的源文件时,文件的扩展名是.c。尽管可以为源文件取任意文件名和扩展名,但是.c是公认较合适的扩展名。1.4.3 编译源代码

虽然你能明白C源代码(至少在学习本书后你会明白),但是计算机不明白。计算机只能理解数字或二进制(binary)的机器语言(machine language)指令。在计算机运行C程序之前,必须先将源代码翻译成机器语言。这个翻译过程属于程序开发的第2步,由编译器(compiler)来执行(编译器也是一个程序)。编译器将源代码作为输入,生成磁盘文件,其中包含源代码相应的机器指令。编译器创建的机器语言指令被称为目标代码(object code),包含目标代码的文件称为目标文件(object file)。注意

本书涵盖ANSI C标准,这意味着只要编译器遵循ANSI标准,你可以使用任意编译器进行编译。然而,并非所有的编译器都支持该标准。当前的C语言标准名为ISO/IEC 9899:2011。为简单起见,本书在提到该标准时均用C11代替复杂的标准名。

使用图形集成开发环境来编译程序非常简单。在大多数图形环境中,通过选择“编译”图标或相应的菜单选项即可编译程序清单。在编译代码后,选择“运行”图标或相应的菜单选项来执行程序。在使用编译器之前,应先查看编译器的用户手册,了解编译器编译和运行程序的规范。Code::Blocks就是一个图形开发环境,有各种操作系统的版本,可免费下载。除此之外,还有许多适用于多平台的其他图形开发环境,读者可依自己喜好免费下载或购买。

编译完成后,生成一个目标文件。如果查看编译目录或文件夹中的文件列表,会发现一个与源文件同名的文件,但是扩展名是.obj(不是.c)。扩展名为.obj的文件是目标文件,供链接器使用。在Linux和UNIX系统中,编译器生成的目标文件扩展名是.o,不是.obj。1.4.4 链接以创建可执行文件

在运行程序之前,还要完成一步。函数库是ANSI C语言定义的一部分,其中包含了预定义函数的目标代码(已编译过的代码)。预定义函数(predefined function)包含已编写好的C代码,在编译器的软件包中可随时使用。

1.4.1节中使用的printf()函数就是一个库函数(library function)。库函数用于执行一些经常需要完成的任务(如,在屏幕上显示信息、从磁盘文件中读取数据等)。如果在程序中使用了这些函数(几乎所有的程序都需要使用库函数),必须将编译源文件时生成的目标文件与库函数的目标文件合并,创建最终的可执行程序(可执行的意思是,可以在计算机上运行或执行程序)。上述过程称为链接(link),执行这一过程的程序称为链接器(linker)。图1.1演示了从源代码到目标代码,再到可执行程序的整个过程。图1.1 C源代码由编译器转换成目标代码,然后由链接器生成可执行文件1.4.5 完成开发周期

程序经过编译和链接将创建一个可执行文件,点击“运行”按钮即可运行该文件(假设在集成开发环境中进行操作)。如果运行程序后得到的结果与预期不符,则需要回到第1步。必须找出问题所在,并更正源代码。一旦改动了源代码,就要重新编译和重新链接程序,以创建更正后的可执行文件。重复这个过程直至程序的执行情况与预期相符。

关于编译和链接还需注意一点:虽然本书将编译和链接分成两个独立的步骤来讲解,但是大多数编译器都将其合为一个步骤。通常,图形开发环境会提供一个选项,让用户设置是分开完成编译和链接还是一步完成。无论以哪种方式完成编译和链接,都要明白:即使只使用一条命令来完成编译和链接,它们仍是两个独立的行为。

C程序的开发周期如下。

步骤1 使用编辑器编写源代码。传统上,C源代码文件的扩展名是.c(如myprog.c、database.c)。

步骤2 使用编译器编译程序。如果编译器在程序中未发现错误,将生成一个目标文件。编译器生成的文件与源文件同名,其扩展名是.obj或.o(如,myprog.c被编译为myprog.obj或myprog.o)。如果编译器发现错误会报错。必须返回步骤1,在源代码中更正错误。

步骤3 使用链接器链接程序。如果不出错,链接器将在磁盘文件中生成一个与目标文件同名的可执行程序,扩展名是.exe(如,链接myprog.obj后创建了myprog.exe)。

步骤4 执行程序。应该测试程序,判断它是否如期正常运行。如果不是,返回步骤1在源代码中进行修改。图1.2演示了程序开发的步骤。除最简单的程序外,在完成程序前,几乎所有的程序都要按顺序执行这些步骤多次。即使是经验丰富的程序员也不可能一蹴而就,写出没有任何错误的程序!因为要多次重复编辑-编译-链接-测试环节,所以必须熟悉你所使用的工具:编辑器、编译器和链接器。图1.2 C程序开发涉及的步骤1.5 第1个C程序

是否迫不及待想要创建自己的第1个C程序了?下面的程序清单1.1是一个小程序,帮助读者熟悉自己的编译器。现在你也许尚未理解程序中的每个细节,不用担心,先照着程序清单的内容,在编译器中体验编写、编译和运行C程序的过程。

该示例的文件名是hello.c,它将在屏幕上显示Hello, World!。这是介绍C程序设计的经典示例,适合读者学习。hello.c的源代码在程序清单1.1中。键入该程序清单时,不要包含最左侧的行号和冒号。这些行号是为了方便描述和分析程序。程序清单1.1 hello.c1:    #include 2:    3:    int main(void)4:    {5:      printf("Hello, World!\n");6:      return 0;7:    }

务必按照软件提供的安装说明安装编译器。无论你使用的是Linux、UNIX、MAC OS、Windows还是其他操作系统,确保你理解如何使用所选的编辑器和编译器。准备好编辑器和编译器后,按以下几个步骤输入、编译并执行hello.c。1.5.1 输入并编译hello.c

按以下步骤输入和编译hello.c程序。

1.在需要储存C程序的目录中打开编辑器。如前所述,你可以使用任意文本编辑器,但是集成开发环境(IDE)大多数都自带C编译器,方便用户在IDE中完成输入、编译和链接程序。请查阅编译器用户手册,了解你所使用的编译器是否有IDE。

2.对照程序清单1.1,使用键盘键入hello.c源代码。换行时,请按下Enter键。注意

不要输入程序清单中左侧的行号和冒号。在程序清单中显示它们是为了方便描述和分析程序。

3.保存源代码,命名为hello.c。

4.查看目录或文件夹中列出的文件(应看到列出的文件中有hello.c),确认hello.c是否储存在磁盘上。

5.编译并链接hello.c。执行编译器用户手册中指定的命令,会显示一条消息,描述是否有错误或警告。

6.查看编译器的消息。如果编译器给出的消息显示没有任何错误或警告,那么一切运行正常。如果键入的程序有误,编译器会捕获错误并显示错误消息。例如,如果将printf拼写成prntf,编译器会生成一条类似的消息:Error: undefined symbols:_prntf in hello.c (hello.OBJ)

7.如果有错误的消息,请返回步骤2。打开编辑器中的hello.c文件,仔细对比程序清单1.1中的代码,修正错误并返回步骤3继续。

8.现在,第1个C程序应该已经编译完成,准备运行。如果此时查看目录中列出的所有hello文件(不包括扩展名),会发现以下文件:

hello.c       编辑器创建的源文件

hello.obj或hello.o  包含hello.c的目标代码

hello.exe      编译和链接hello.c时创建的可执行程序①

9.只需输入hello,便可执行(execute)或运行hello.exe。屏幕上将显示消息Hello, World!

恭喜!你已经输入、编译并运行了第1个C程序。虽然hello.c这个程序相当简单,也没什么实际用途。但是,它是你编程生涯的起点。实际上,现在大多数专家级C语言程序员都是从编译hello.c开始学习的。1.5.2 编译错误

如果编译器在编译源代码时,发现某些内容无法编译,会生成编译错误。拼写错误、字母排错等各种问题都将导致编译器停止工作。幸运地是,现在的编译器不止是停止工作,还会告诉用户问题出在哪里!为用户在源代码中查找并更正错误提供了极大的方便。

下面,为了解释这点,在前面的hello.c程序中故意加入错误。如果你刚才运行了程序清单1.1,那么现在你的计算机磁盘中应该有一份hello.c的备份。使用编辑器,将光标移至调用printf()(第5行)的末尾,删除末尾的分号。此时,hello.c应该如程序清单1.2所示:程序清单1.2 hello2.c - 错误的hello.c1:    #include 2:    3:    int main(void)4:    {5:      printf("Hello, World!\n")6:      return 0;7:    }

接下来,保存该文件。然后在编译器中输入命令。由于你将程序改错了,编译器将无法完成编译。而且,编译器显示一条类似的消息:hello.c(6) : Error: ';' expected

可以分三部分查看这行消息:hello.c          错误所在文件的名称(6)            错误所在行的编号Error: ';' expected    对错误的描述

这行消息包含的信息很多,描述了hello.c的第6行缺少分号。但是,实际上是第5行缺少分号,这与实际情况不符。你一定很纳闷,明明是第5行末尾缺少分号,为何编译器报告第6行出错?出现这种情况的原因是,C编译器会忽略行与行之间的间隔,虽然分号属于printf()语句,但也可置于下一行(这样写容易引起混淆,是不好的编程习惯)。编译器执行到第6行的return语句后,才确定遗漏了分号。因此,编译器报告第6行出错。

这是C编译器在报错方面不可否认的事实。虽然编译器在检测和定位错误时很智能,但是它不是爱因斯坦。你必须灵活运用C语言的知识,解读编译器的消息,确定编译器报错的实际位置。通常都可以在编译器报告的出错代码行中找到错误,但如果找不到,应该查看上一行。刚开始,你会认为查找错误很困难,但很快就能得心应手。注意

报告的错误依编译器而异。绝大多数情况下,错误消息都有助于找到问题所在。

在结束本课之前,再来看一个编译错误的示例。在编辑器中再次载入hello.c,并按以下步骤改动。

1.在第5行加上分号。

2.删除第5行中Hello前面的双引号。

保存该文件至磁盘并再次编译该程序。这次,编译器应该生成类似如下的错误消息:hello.c(5) : Error: undefined identifier 'Hello'hello.c(7) : Lexical error: unterminated stringLexical error: unterminated stringLexical error: unterminated stringFatal error: premature end of source file

第1条错误消息正确地指出了错误,定位在第5行的Hello单词。错误消息undefined identifier的意思是,编译器不知道Hello单词用来做什么,因为它没有放在双引号内。但是,其他4条错误消息是怎么回事?这说明在C编译器中,一个错误有时会引发多条错误消息(现在,不用关心它们的含义)。1.5.3 链接器错误消息

链接器错误相对少见,通常是由误写C语言的库函数所致。在这种情况下,编译器会生成Error: undefined symbols:(后面接错误名称,名称前面有一条下划线)的错误消息。更正拼写错误后,便可解决问题。1.6 小 结

学完本课,你应该自信地认为选择C语言作为程序设计语言是明智之举。C语言融功能强大、流行和可移植性于一体。这些因素加上C语言与其他面向对象语言(如,C++、Java和C#)的密切关系,让C语言从众多程序设计语言中脱颖而出。

本课讲解了编写C程序涉及的步骤,这一过程称为程序开发。读者要熟悉编辑—编译—链接—测试环节和每一步用到的工具。

程序开发免不了出现错误。C编译器可以检测源代码中的错误并显示错误消息,对错误进行定位和描述。利用这些信息,可以在源代码中更正错误。但是请记住,编译器指出的错误不一定完全准确。有时,你要用C语言的知识,查出导致错误的真正原因。1.7 答 疑

问:如果要将自己编写的程序提供给他人,应提供哪些文件?

答:C语言的优点之一是,它是一门编译语言。这意味着对源代码进行编译后,将获得一个可执行程序(即,独立程序)。你只需要将可执行程序hello.exe提供给对方,对方便可运行hello程序。不需要再提供源代码hello.c或目标文件hello.obj,对方也不需要使用C编译器。但是,获得可执行程序(你提供的)的人要和你一样使用同类操作系统的机器,如PC、Macintosh、Linux等。

问:创建可执行文件后,是否还要保留源代码(.c)或目标代码(.obj)?

答:应该保留源代码,如果删除源代码,将来就无法再修改该程序。目标代码的情况有所不同,需要保留目标代码的原因不在本书讨论范围之内。就现在而言,获得可执行文件后,便可删除目标文件。如果需要目标文件,重新编译源文件即可。大多数集成开发环境(IDE)除创建源文件(.c)、目标文件(.obj)和可执行文件(.exe)外,还会创建其他文件。只要保留了源文件(.c),就能重新创建其他文件。

问:是否必须使用编译器自带编辑器?

答:完全不必。你可以使用任意编辑器,只要该编辑器以文本形式保存源代码。如果编译器自带编辑器,应该尝试使用它。如果你更喜欢其他编辑器,也没关系。虽然我所使用的所有编译器都自带编辑器,但是我还是另外购买了编辑器。不过,现在编译器自带的编辑器也越来越好用,有的能自动格式化C代码,有的用不同的颜色标出源文件中各部分的代码,更方便查找错误。

问:如果只有C++编译器没有C编译器,怎么办?

答:本课前面介绍过,C++是C的超集。这意味着可以使用C++编译器编译C程序。

问:是否可以忽略警告消息?

答:有些警告消息会影响程序的运行,但有些不会。如果编译器报错,就说明程序中有不对的地方。绝大多数编译器都允许用户设置警告等级。通过设置警告等级,可以让编译器只显示最严重的警告或所有警告(包括最细微的警告)。一些编译器甚至提供各种不同的中等警告。要查看每条警告并判断是否需要修正。程序最好没有任何警告和错误(如果有错误,编译器不会创建可执行文件)。1.8 课后研习

课后研习包含小测验和练习题。小测验帮助读者理解和巩固本课所学概念,练习题有助于读者将理论知识与实践相结合。在继续学习下一课之前,应尽量理解小测验和练习题的内容。答案参见附录D。1.8.1 小测验

1.为什么C语言是编程的首选语言?请列出3个原因。

2.编译器的用途是什么?

3.程序开发周期有哪些步骤?

4.如果用编译器编译program1.c程序,需要输入什么命令?

5.你的编译器是将编译和链接一步完成,还是需要输入各自的命令?

6.你所使用的C源文件的扩展名是什么?

7.FILENAME.TXT是否是C源文件的有效文件名?

8.如果执行一个已编译的程序没有按预期运行,怎么办?

9.什么是机器语言?

10.链接器有什么用途?1.8.2 练习题

1.用文本编辑器查看程序清单1.1创建的目标文件。目标文件看上去是否与源文件很相像?(在退出编辑器时不要保存该文件)

2.输入下面的程序并编译它。该程序完成了什么任务?(不要输入左侧的行号和冒号)1:  #include 2:3:  int radius, area;4:5:  int main( void )6:  {7:    printf( "Enter radius (i.e. 10): " );8:    scanf( "%d", &radius );9:    area = (int) (3.14159 * radius * radius);10:    printf( "\n\nArea = %d\n", area );11:    return 0;12: }

3.输入并编译下面的程序。该程序完成了什么任务?1:#include 2:3:int x, y;4:5:  int main( void )6:  {7:    for ( x = 0; x < 10; x++, printf( "\n" ) )8:      for ( y = 0; y < 10; y++ )9:        printf( "X" );10:11:    return 0;12:  }

4.排错:下面的程序中存在问题,在编辑器中输入该程序并编译。哪些行导致产生错误消息?1:  #include 2:3:  int main( void );4:  {5:    printf( "Keep looking!" );6:    printf( "You\'ll find it!\n" );7:    return 0;8:  }

5.排错:下面的程序中存在一个问题。在编辑器中输入该程序并编译。哪些行导致了错误消息?1:  #include 2:3:  int main( void )4:  {5:    printf( "This is a program with a " );6:    do_it( "problem!");7:    return 0;8:  }

6.在练习题3中作如下修改。重新编译并运行该程序。程序现在完成什么任务?9: printf( "%c", 1 );

① 译者注:如果使用IDE,只需点击相应的“运行”按钮。具体操作请查阅你所使用的IDE用户手册。第2课C程序的组成部分

每个C程序都由多个部分组成。本书绝大多数篇幅都在解释各种程序的组成部分以及如何使用它们。为了帮助读者掌握C程序的概况,首先介绍一个完整(但简短)的C程序,并识别其中的每个部分。本课将介绍以下内容:● 简短C程序的组成部分● 每个程序组成部分的用途● 如何编译并运行程序示例2.1 简短的C程序

程序清单2.1列出了bigyear.c的源代码,这是一个简单的程序。该程序接受用户从键盘输入的出生年份,并计算此人指定年龄的年份。现在,还不用了解程序的各种细节和工作原理。关键是要熟悉C程序的各个部分,以便更好地理解本书后面所示的程序清单。

在查看程序示例之前,要知道什么是函数,因为函数是C语言程序设计的核心。函数(function)是一段执行某项任务的程序代码。要指定函数的名称,在程序中通过引用函数名,可以执行函数中的代码。程序还能将信息(被称为参数(argument))发送给函数,而函数也可以将信息返回。C语言有两种类型的函数:库函数(library function)和用户自定义函数(user-defined function),前者是C编译器软件包的一部分,后者由程序员创建。你将在本书中学到这两种类型函数的相关内容。

注意,程序清单2.1和本书后面所列的所有程序清单中的行号都不是程序的一部分。把它们显示在程序清单中,只是为了方便描述和分析,在键入程序时千万不要将它们也一同输入。

输入程序清单2.1 bigyear.c - 计算某人在指定年数后的年份1:  /* 该程序计算某人在经过指定年数后的年份。*/2:  #include 3:  #define TARGET_AGE 884:  5:  int year1, year2;6:  7:  int calcYear(int year1);8:  9:  int main(void)10:  {11:    // 询问用户的出生年份12:    printf("What year was the subject born? ");13:    printf("Enter as a 4-digit year (YYYY): ");14:    scanf(" %d", &year1);15:  16:    // 计算指定年数后的年份,并显示该年份17:    year2 = calcYear(year1);18:  19:    printf("Someone born in %d will be %d in %d.",20:        year1, TARGET_AGE, year2);21:  22:    return 0;23:  }24:  25:  /* 该函数计算将来年份 */26:  int calcYear(int year1)27:  {28:    return (year1 + TARGET_AGE);29:  }

输出What year was the subject born? 1963Someone born in 1963 will be 88 in 2051.2.2 程序的组成部分

接下来,将逐行分析上面的程序示例。我们为程序清单中的每一行都添加了行号,以方便读者定位和查找正在分析和讨论的部分。2.2.1 main()函数

main()函数位于程序清单2.1的第9~23行。在每个可执行的C程序中,main()函数必不可少。在最简单的情况下,main()函数由函数名main、其后的一对圆括号(其中包含void)和一对花括号({})组成。在大多数编译器中,省略圆括号中的void并不影响程序的运行。但是,ANSI标准规定,应该在main后的圆括号中写上void,以表示没有给main函数发送任何消息。

花括号内的语句组成了程序的主体。在一般情况下,程序从main()的第1条语句开始执行,到main()的最后一条语句结束。根据ANSI标准,main()中不能缺少return语句(第22行)。2.2.2 #include和#define指令

#include指令和#define指令分别位于程序清单2.1的第2行和第3行。#include指令命令C编译器,在编译时将包含文件的内容添加进程序中。包含文件(include file)是独立的磁盘文件,内含程序或编译器要使用的信息。这些包含文件(也称为头文件(header file))由编译器提供。一般情况下都不用修改这些文件中的内容,因此将其与源代码分离。所有包含文件的扩展名都是.h(如,stdio.h)。

使用#include指令,可以让编译器在编译过程中将指定的包含文件放入程序中。在程序清单2.1中,#include指令被解译为“添加stdio.h文件的内容”。几乎所有的C程序都要包含一个或多个包含文件。欲了解更多包含文件的相关内容,请参阅第22课。

#define指令命令C编译器,在整个程序中用赋给指定项的值替换指定项。如果用#define在程序的顶部设置变量,不仅整个程序都能使用该项,而且在需要时可以很方便地更改该项。只需修改#define一行,便可替换所有该项的值,省去了在程序中逐一查找修改的麻烦。例如,假设你编写了一个工资单程序,用这种特殊的方法设置医疗保险(即,用#define设置HEALTH_INSURANCE的值),在保险费率发生变化时,只需修改该程序顶部(或头文件中)HEALTH_INSURANCE的值即可。这比逐行查找相关代码再逐一修改保险费率要简单得多。我们将在第3课详细介绍#define指令。2.2.3 变量定义

变量定义位于程序清单2.1的第5行。变量是赋给内存中某个位置的名称,用于储存信息。在程序执行期间,程序使用变量储存各种不同类型的信息。在C语言中,必须先定义变量才能使用。变量定义告诉编译器变量的名称和待储存信息的类型。在上面的程序示例中,第5行int year1, year2;定义了两个变量——分别名为year1和year2,每个变量都储存一个整型值。第3课将详细介绍变量和变量定义的内容。2.2.4 函数原型

函数原型位于程序清单2.1的第7行。函数原型(function prototype)出现在使用函数之前,将程序中所用函数的名称和参数告知编译器。函数原型与函数定义(function definition)不同,函数定义包含组成函数的实际语句(函数定义在2.2.6节中详述)。2.2.5 程序语句

程序清单2.1的第12、13、14、17、19、20、22和28行都是程序语句。C程序的具体工作由它的语句来完成,如在屏幕上显示信息、读取键盘的输入、执行数学运算、调用函数、读取磁盘文件以及程序需要执行的其他操作。本书用大部分篇幅分析和讲解各种C语句。就现在而言,你只需记住:在源代码中C语句通常占一行,并以分号结尾。接下来,将详细讲解bigyear.c中的语句。(1) pri ntf()语句

printf()语句(第12、13、19和20行)是在屏幕上显示信息的库函数。printf()语句可以显示简单的文本消息(如12和13行所示),也可以显示带有一个或多个变量值的消息(如第19行和第20行所示)。(2) scanf()语句

scanf()语句(第14行)也是一个库函数。它读取从键盘输入的数据,并将数据赋给程序中的一个或多个变量。

程序中第17行的语句,调用calcYear()函数。也就是说,该语句执行calcYear()函数中包含的程序语句。此外,year1作为参数被发送给函数。执行完calcYear()中的语句后,calcYear()向程序返回一个值,该值被储存在year2变量中。(3) return语句

程序清单2.1中的第22行和第28行都是return语句。其中,第28行的return语句属于calcYear()函数,该函数计算一个人到指定年龄时的年份,通过将#define定义的TARGET_AGE加上变量year1,并将结果返回调用calcYear()的程序。第22行的return语句,在程序结束前将0这个值返回操作系统。2.2.6 函数定义

程序清单2.1中的函数定义在第26~29行。该程序中涉及了两种类型的函数(库函数和用户自定义函数)。printf()和scanf()函数是库函数,第26~29行的calcYear()函数是用户自定义函数。顾名思义,用户自定义函数由程序员在程序开发过程中编写。calcYear()函数将创建的TARGET_AGE与年份相加,并将结果(另一个不同的年份)返回调用该函数的程序。在第5课中,你将学到正确使用函数是养成良好的C程序设计习惯的关键。

这里要提醒读者注意,在真正的C程序中,可能不会用函数完成诸如计算两个数加法这样简单的任务。程序清单2.1这样做只是为了演示,方便读者理解函数。2.2.7 程序的注释

程序清单2.1中的第1、11、16和25行都是程序的注释。程序中以/*开始、以*/结尾的部分,或者以//开始的单独一行都称为注释(comment)。编译器会忽略所有的注释,无论你在注释中写任何内容,都不会影响程序的运行。第1种风格的注释可写成一行或多行(跨行)下面有3个示例:/* 该注释独占一行 */int a,b,c; /* 该注释占一行的一部分 *//* 该注释跨越多行 */

注释不能套嵌。把一条注释放入另一条注释中称为嵌套(nested)注释。大多数编译器都不允许下面这样的注释:/*/* 套嵌注释 */*/

然而,某些编译器也允许套嵌注释,虽然这看上去很不错,但是请不要这样做。因为C语言的优势之一是可移植性,使用嵌套注释这样的特性可能会影响代码的可移植性。除此之外,嵌套注释还可能导致一些难以发现的问题。

第2种风格的注释以双斜杠(//)开始,只用于单行注释。双斜杠告诉编译器忽略从双斜杠后面至本行结尾的内容。// 这一整行都是注释int x; // 注释开始于双斜杠

许多新手程序员都认为给程序加注释浪费时间,完全没必要加注释。这样想完全不对!在你写代码时,当然很清楚程序完成什么操作。然而,随着程序越来越大、越来越复杂,或者你要修改半年前编写的程序,就能体现注释的价值所在。现在就养成好习惯,用注释来说明程序设计的结构和操作。可以依自己喜好选择任意一种风格的注释。本书的程序中会用到这两种风格的注释。DODON’T不要给本身很清晰的语句在程序的源代码中添加必要的注释,添加不必要的注释。例特别是在你可能会不清楚的语句或函如,数附近,方便自己或他人今后修改。/* 下面的语句在屏幕中打印如何写好注释是门学问。词不达意或Hello World! */printf("Hello 晦涩难懂的注释起不到注释本身的作World!);用。过于冗长繁琐的注释可能导致写在你非常熟悉printf()函数注释的时间比编程还多。后,这条注释相当多余。2.2.8 使用花括号

程序清单2.1中的花括号位于第10、23、27和29行。使用花括号({})将组成每个C程序(包括main()函数)的代码行都括起来。用花括号括起来的一条或多条语句称为块(block)。学到本书后面的课程,你会发现C语言中的块有许多用途。2.2.9 运行程序

花时间输入、编译并运行bigyear.c(程序清单2.1)。不要放过任何一个练习使用编辑器和编译器的机会。回顾第1课中学过的步骤①。

1.确保编程的目录正确。

2.打开编辑器。

3.对照程序清单2.1正确输入bigyear.c源代码,但不要输入左侧的行号和冒号。

4.保存程序文件。

5.输入编译器相应的命令编译并链接该程序。如果未显示任何错误消息,便可点击C环境中相应的按钮运行程序。

6.如果出现错误消息,返回第2步并更正错误。2.2.10 补充说明

计算机运行快速且准确,但它的确只会“照本宣科”。计算机非常呆板、缺乏想象力,对最简单的拼写错误也无能为力。它只按照你输入的内容执行,完全无视这些内容的含义!

C语言的源代码也是如此。程序中一个简单的拼写错误会导致C编译器停止工作,甚至崩溃。幸运地是,虽然编译器尚未智能到可以纠正你的小错误(人人都会犯错!),但是,它能轻易地识别这些错误并报错(第1课中介绍了编译器如何报告错误消息和如何解译它们)。2.3 学以致用

介绍完程序的组成部分后,我们来查看各程序有何相似之处。请看程序清单2.2,看是否能识别程序的各个部分。

输入程序清单2.2 list_it.c:计算某人在指定年数后的年份1:  /* list_it.c - 该程序将显示整个程序的代码,包括行号! */2:  #include 3:  #include 4:  #define BUFF_SIZE 2565:  void display_usage(void);6:  int line;7:  8:  int main(int argc, char *argv[])9:  {10:    char buffer[BUFF_SIZE];11:    FILE *fp;12:  13:    if (argc < 2)14:    {15:      display_usage();16:      return 1;17:    }18:  19:    if ((fp = fopen(argv[1], "r")) == NULL)20:    {21:      fprintf(stderr, "Error opening file, %s!", argv[1]);22:      return(1);23:    }24:  25:    line = (1);26:  27:    while (fgets(buffer, BUFF_SIZE, fp) != NULL)28:      fprintf(stdout, "%4d:\t%s", line++, buffer);29:  30:    fclose(fp);31:    return 0;32:  }33:  34:  void display_usage(void)35:  {36:    fprintf(stderr, "\nProper Usage is: ");37:    fprintf(stderr, "\n\nlist_it filename.ext\n");38:  }

输出1:  /* list_it.c - 该程序将显示整个程序的代码,包括行号! */2:  #include 3:  #include 4:  #define BUFF_SIZE 2565:  void display_usage(void);6:  int line;7:  8:  int main(int argc, char *argv[])9:  {10:    char buffer[BUFF_SIZE];11:    FILE *fp;12:  13:    if (argc < 2)14:    {15:      display_usage();16:      return 1;17:    }18:  19:    if ((fp = fopen(argv[1], "r")) == NULL)20:    {21:      fprintf(stderr, "Error opening file, %s!", argv[1]);22:      return(1);23:    }24:  25:    line = (1);26:  27:    while (fgets(buffer, BUFF_SIZE, fp) != NULL)28:      fprintf(stdout, "%4d:\t%s", line++, buffer);29:  30:    fclose(fp);31:    return 0;32:  }33:  34:  void display_usage(void)35:  {36:    fprintf(stderr, "\nProper Usage is: ");37:    fprintf(stderr, "\n\nlist_it filename.ext\n");38:  }

分析

该程序把保存的所有代码内容显示在屏幕上,包括代码的行号。

查看程序清单2.2,分析该程序由哪几个部分组成。必不可少的main()函数位于第8~32行。第2~3行是#include指令。第4行是#define指令,将BUFF_SIZE定义为256。这样处理该值后,如果改变缓冲区大小,则只需修改一行,所有使用BUFF_SIZE的地方都会自动更②新。如果硬编码(hardcode)一个数字如256,在需要更改该值时,就必须逐一查找所有使用该值的代码,以确保更新了所有的相关内容。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载