
1.1 什么是计算机程序
有人以为计算机是“万能”的,会自动进行所有的工作,甚至觉得计算机神秘莫测。这是很多初学者的误解,其实,计算机的每一个 *** 作都是根据人们事先指定的指令进行的。例如用一条指令要求计算机进行一次加法运算,用另一条指令要求计算机将某一运算结果输出到显示屏。为了使计算机执行一系列的 *** 作,必须事先编好一条条指令,输入到计算机。
所谓程序,就是一组计算机能识别和执行的指令。每一条指令使计算机执行特定的 *** 作。只要让计算机执行这个程序,计算机就会“自动地"执行各条指令,有条不紊地进行工作。一个特定的指令序列,用来完成一定的功能。为了使计算机系统能实现各种功能,需要成千上万个程序。这些程序大多数是由计算机软件设计人员根据需要设计好的,作为计算机的软件系统的一部分提供给用户使用。此外,用户还可以根据自己的实际需要设计一些应用程序,例如学生成绩统计程序、财务管理程序、工程中的计算程序等。
总之,计算机的一切 *** 作都是由程序控制的,离开程序,计算机将一事无成。所以,计算机的本质是程序的机器,程序和指令是计算机系统中最基本的概念。只有懂得程序设计,才能真正了解计算机是怎样工作的,才能更深入地使用计算机。
1.2 什么是计算机语言
人和人之间的交流需要通过语言。中国人之间用中国话,英国人用英语、俄罗斯人用俄语,等等。人和计算机交流信息,也要解决语言问题。需要创造一种计算机和人都能识别的语言,这就是计算机语言。计算机语言经历了几个发展阶段:
机器语言 计算机工作基于二进制,从根本上说,计算机只能识别和接受由0和1组成的指令。在计算机发展的初期,一般计算机的指令长度为16,即以16个二进制数(0或1)组成一条指令,16个0和1可以组成各种排列组合。例如,用
1011011000000000
让计算机进行一次加法运算。人要使计算机知道和执行自己的意图,就要编写许多条由0和1组成的指令。然后要用纸带穿孔机以人工的方法在特制的黑色纸带上穿孔,在指定的位置上有孔代表1,无孔代表0。一个程序往往需要一卷长长的纸带。在需要运行此程序时就将此纸带装在光电输入机上,当光电输入机从纸带读入信息时,有孔处产生一个电脉冲,指令变成电信号,让计算机执行各种 *** 作。 这种计算机能直接识别和接受的二进制代码称为机器指令( machine instruction)。机器指令的集合就是该计算机的机器语言(machine language)。在语言的规则中规定各种指令的表示形式以及它的作用。
显然,机器语言与人们习惯用的语言差别太大,难学、难写、难记、难检查﹑难修改,难以推广使用。因此初期只有极少数的计算机专业人员会编写计算机程序。
符号语言 为了克服机器语言的上述缺点,人们创造出符号语言(symbolic language),它用一些英文字母和数字表示一个指令,例如用ADD代表“加”,SUB代表“减”,LD代表“传送”等。如上面介绍的那条机器指令可以改用符号指令代替:
ADD A,B (执行A十B=>A,将寄存器A中的数与寄存器B中的数相加,放到寄存器A中)
显然,计算机并不能直接识别和执行符号语言的指令,需要用一种称为汇编程序的软件,把符号语言的指令转换为机器指令。一般,一条符号语言的指令对应转换为一条机器指令。转换的过程称为“代真”或“汇编”,因此,符号语言又称为符号汇编语言(symbolic assembler language)或汇编语言(assembler language)。
虽然汇编语言比机器语言简单好记一些,但仍然难以普及,只在专业人员中使用。不同型号的计算机的机器语言和汇编语言是互不通用的。用甲机器的机器语言编写的程序在乙机器上不能使用。机器语言和汇编语言是完全依赖于具体机器特性的,是面向机器的语言。由于它“贴近”计算机,或者说离计算机"很近”,称为计算机低级语言(low level language)。
高级语言 为了克服低级语言的缺点,20 世纪50年代创造出了第一个计算机高级语言——FORTRAN语言。它很接近于人们习惯使用的自然语言和数学语言。程序中用到的语句和指令是用英文单词表示的,程序中所用的运算符和运算表达式和人们日常所用的数学式子差不多,很容易理解。程序运行的结果用英文和数字输出,十分方便。例如在FORTRAN语言程序中,想计算和输出3.5×6sin(π/3),只须写出下面这样一个语句:
PRINT * ,3.5 * 6 * SIN(3.1415926/3)
即可得到计算结果。显然这是很容易理解和使用的。
这种语言功能很强,且不依赖于具体机器,用它写出的程序对任何型号的计算机都适用(或只须作很少的修改),它与具体机器距离较远,故称为计算机高级语言( high level language)。
当然,计算机也是不能直接识别高级语言程序的,也要进行“翻译”。用一种称为编译程序的软件把用高级语言写的程序(称为源程序, source program)转换为机器指令的程序(称为目标程序,object program),然后让计算机执行机器指令程序,最后得到结果。高级语言的一个语句往往对应多条机器指令。
自从有了高级语言后,一般的科技人员、管理人员、大中学生以及广大计算机爰好者,都能较容易地学会用高级语言编写程序,指挥计算机进行工作,而完全不顾什么机器指令,也可以不必深人懂得计算机的内部结构和工作原理,就能得心应手地利用计算机进行各种工作,为计算机的推广普及创造了良好的条件,人们称高级语言的出现是计算机发展史上“惊人的成就”。
数十年来,全世界涌现了2500种以上高级语言,每种高级语言都有其特定的用途,其中应用比较广泛的有100多种,影响最大的有FORTRAN 和 ALGOL(适合数值计算)、BASIC和QBASIC(适合初学者的小型会话语言)、COBOL(适合商业管理)、Pascal(适合教学的结构程序设计语言)、PL/1(大型通用语言)、LISP和 PROLOG(人工智能语言)、C(系统描述语言)、C++(支持面向对象程序设计的大型语言)、Visual Basic(支持面向对象程序设计的语言)和Java(适于网络的语言)等。
高级语言经历了不同的发展阶段:
(1)非结构化的语言。初期的语言属于非结构化的语言,编程风格比较随意,只要符合语法规则即可,没有严格的规范要求,程序中的流程可以随意跳转。人们往往追求程序执行的效率而采用了许多“小技巧”,使程序变得难以阅读和维护。早期的BASIC、FORTRAN和ALGOL等都属于非结构化的语言。
(2)结构化语言。为了解决以上问题,提出了“结构化程序设计方法”,规定程序必须由具有良好特性的基本结构(顺序结构、分支结构,循环结构)构成,程序中的流程不允许随意跳转,程序总是由上而下顺序执行各个基本结构。这种程序结构清晰、易于编写、阅读和维护。QBASIC,FORTRAN 77和C语言等属于结构化的语言,这些语言的特点是支持结构化程序设计方法。
以上两种语言都是基于过程的语言,在编写程序时需要具体指定每一个过程的细节。在编写规模较小的程序时,还能得心应手,但在处理规模较大的程序时,就显得捉襟见肘、力不从心了。在实践的发展中,人们又提出了面向对象的程序设计方法。程序面对的不是过程的细节,而是一个个对象,对象是由数据以及对数据进行的 *** 作组成的。
(3) 面向对象的语言。近十多年来,在处理规模较大的问题时,开始使用面向对象的语言。C++,C#,Visual Basic和Java等语言是支持面向对象程序设计方法的语言。有关面向对象的程序设计方法和面向对象的语言在本书中不作详细介绍,有兴趣的可参考有关专门书籍(如作者编著的《C++面向对象程序设计》)。
进行程序设计,必须要用到计算机语言,人们根据任务的需要选择合适的语言,编写出程序,然后运行程序得到结果。
1.3 C语言的发展及其特点
本书是介绍怎样利用C语言作为工具进行程序设计的。为什么要选择C语言呢?这里有必要对C语言的发展和特点有一定的了解。
C语言是国际上广泛流行的计算机高级语言。
C语言的祖先是 BCPL语言。1967年英国剑桥大学的 Martin Richards推出了没有类型的BCPL(Basic Combined Programming Language)语言。1970年美国AT&T贝尔实验室的Ken Thompson 以 BCPL 语言为基础,设计出了很简单且很接近硬件的B语言(取BCPL的第一个字母)。但B语言过于简单,功能有限。1972—1973年间,美国贝尔实验室的D.M.Ritchie在B语言的基础上设计出了C语言。C语言既保持了BCPL 和B语言的优点(精练,接近硬件),又克服了它们的缺点(过于简单,无数据类型等),C语言的新特点主要表现在具有多种数据类型(如字符、数值、数组、结构体和指针等)。开发C语言的目的在于尽可能降低用它所写的软件对硬件平台的依赖程度,使之具有可移植性。
最初的C语言只是为描述和实现UNIX *** 作系统提供一种工作语言而设计的。1973年,Ken Thompson和 D.M.Ritchie合作把UNIX的90%以上用C语言改写,即UNIX第5版(原来的UNIX *** 作系统是1969年由美国的贝尔实验室的Ken Thompson和 D.M.Ritchie开发成功的,是用汇编语言编写的)。随着UNIX的日益广泛使用,C语言也迅速得到推广。1978年以后,C语言先后移植到大、中、小和微型计算机上。C语言便很快风靡全世界,成为世界上应用最广泛的程序设计高级语言。
以UNIX第7版中的C语言编译程序为基础,1978年,Brian W. Kernighan和 DennisM. Ritchie 合著了影响深远的名著The C Programming Language ,这本书中介绍的C语言成为后来广泛使用的C语言版本的基础,它是实际上第一个C语言标准。1983年,美国国家标准协会(ANSI)成立了一个委员会,根据C语言问世以来各种版本对C语言的发展和扩充,制定了第一个C语言标准草案(83 ANSI C)。ANSI C比原来的C有了很大的发展。Brian W.Kernighan 和 Dennis M.Ritchie 在 1988年修订了他们的经典著作The C Programming Language ,按照即将公布的ANSI C新标准重新写了该书。1989年,ANSI公布了一个完整的C语言标准—ANSI X3.159-1989(常称ANSI C或C89)。1990年,国际标准化组织ISO(International Standard Organization)接受C89作为国际标准ISO/IEC9899 : 1990,它和ANSI 的C89基本上是相同的。
1995年,ISO对C90做了一些修订,即“1995基准增补1(ISO/IEC 9899/AMD1 ∶ 1995)”。1999年,ISO又对C语言标准进行修订,在基本保留原来的C语言特征的基础上,针对应用的需要,增加了---些功能,尤其是C++中的一些功能,命名为ISO/IEC 9899 : 1999,2001年和2004年先后进行了两次技术修正,即 2001年的 TC1 和2004年的 TC2.ISO/IEC 9899 : 1999及其技术修正被称为C99,C99是C89(及1995基准增补1)的扩充。本书的叙述以C99标准为依据,为了与C89作比较,在本书的叙述中对C99新增加的功能作特别的说明。
应该注意到,目前由不同软件公司所提供的一些C语言编译系统并未完全实现C99建议的功能,它们多以C89为基础开发。读者应了解自己所使用的C语言编译系统的特点。初学者所用到的初步编程知识基本上在C89的范围内,因此,使用目前的C编译系统仍然可以满足对初学者的教学需要,在今后进行实际软件开发工作时,应注意使用能在更大程度上实现C99功能的编译系统。本书中所举的示例程序基本上都可以在目前所用的编译系统(如Visual C++ 6.0,Turbo C++ 3.o,GCC)上编译和运行。
C语言是一种用途广泛、功能强大、使用灵活的过程性(procedural)编程语言,既可用于编写应用软件,又能用于编写系统软件。因此C语言问世以后得到迅速推广。自20世纪90年代初,C语言在我国开始推广以来,学习和使用C语言的人越来越多,成了学习和使用人数最多的一种计算机语言,绝大多数理工科大学都开设了C语言程序设计课程。掌握C语言成为计算机开发人员的一项基本功。
C语言有以下一些主要特点。
(1)语言简洁、紧凑,使用方便、灵活。C语言一共只有37个关键字(见附录B)、9种控制语句,程序书写形式自由,主要用小写字母表示,压缩了一切不必要的成分。C语言程序比其他许多高级语言简练,源程序短,因此输入程序时工作量少。
实际上,C是一个很小的内核语言,只包括极少的与硬件有关的成分,C语言不直接提供输入和输出语句,有关文件 *** 作的语句和动态内存管理的语句等(这些 *** 作是由编译系统所提供的库函数来实现的),C的编译系统相当简洁。
(2)运算符丰富。C语言的运算符包含的范围很广泛,共有34种运算符(见附录C)。C语言把括号﹑赋值和强制类型转换等都作为运算符处理,从而使C语言的运算类型极其丰富﹐表达式类型多样化。灵活使用各种运算符可以实现在其他高级语言中难以实现的运算。
(3)数据类型丰富。C语言提供的数据类型包括:整型、浮点型、字符型、数组类型、指针类型、结构体类型和共用体类型等,C99又扩充了复数浮点类型,超长整型(long long)和布尔类型(bool)等。尤其是指针类型数据,使用十分灵活和多样化,能用来实现各种复杂的数据结构(如链表、树、栈等)的运算。
(4)具有结构化的控制语句(如if…else语句、while语句,do…while语句, switch语句和 for语句)。用函数作为程序的模块单位,便于实现程序的模块化。C语言是完全模块化和结构化的语言。
(5)语法限制不太严格,程序设计自由度大。例如,对数组下标越界不进行检查,由程序编写者自己保证程序的正确。对变量的类型使用比较灵活,例如,整型量与字符型数据以及逻辑型数据可以通用。一般的高级语言语法检查比较严,能检查出几乎所有的语法错误,而C语言允许程序编写者有较大的自由度,因此放宽了语法检查。程序员应当仔细检查程序,保证其正确,而不要过分依赖C语言编译程序查错。“限制”与“灵活”是一对矛盾。限制严格,就失去灵活性;而强调灵活,就必然放松限制。对于不熟练的人员,编一个正确的C语言程序可能会比编一个其他高级语言程序难一些。也就是说,对用C语言的人,要求更高一些。
(6)C语言允许直接访问物理地址,能进行位(bit) *** 作,能实现汇编语言的大部分功能,可以直接对硬件进行 *** 作。因此C语言既具有高级语言的功能,又具有低级语言的许多功能,可用来编写系统软件。C语言的这种双重性,使它既是成功的系统描述语言,又是通用的程序设计语言。
(7)用C语言编写的程序可移植性好。由于C的编译系统相当简洁,因此很容易移植到新的系统。而且C编译系统在新的系统上运行时,可以直接编译“标准链接库”中的大部分功能,不需要修改源代码,因为标准链接库是用可移植的C语言写的。因此,几乎在所有的计算机系统中都可以使用C语言。
(8)生成目标代码质量高,程序执行效率高。
C原来是专门为编写系统软件而设计的,许多大的软件都用C语言编写,这是因为C语言的可移植性好和硬件控制能力高,表达和运算能力强。许多以前只能用汇编语言处理的问题,后来可以改用C语言来处理了。目前C的主要用途之一是编写“嵌入式系统程序”。由于具有上述优点,使C语言应用面十分广泛,许多应用软件也用C语言编写。
对C语言以上的特点,待学完C语言以后再回顾一下,就会有比较深的体会。
1.4 最简单的C语言程序
为了使用C语言编写程序,必须了解C语言,并且能熟练地使用C语言。本书将由浅入深地介绍怎样阅读C语言程序和使用C语言编写程序。
1.4.1最简单的C语言程序举例
下面介绍几个最简单的C语言程序。
例1.1 要求在屏幕上输出以下一行信息。
This is a C program.
解题思路:在主函数中用printf函数原样输出以上文字。编写程序:
#define _CRT_SECURE_NO_WARNINGS #include//这是编译预处理命令 int main() //定义主函数 { //函数开始的标志 printf("This is a C program.n"); //输出所指定的一行信息 return 0; //函数执行完成时返回返回函数值0 } //函数结束的标志
运行结果:
This is a C program. E:TortoiseGit-2.8.0.0-64bitcs_-cC_2021_10_31DebugC_2021_10_31.exe (进程 16516)已退出,代码为 0。 要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口. . .
以上运行结果是在Visual Studio 2019环境下运行程序时屏幕上得到的显示。其中第1行是程序运行后输出的结果,后面几行是Visual Studio 2019系统在输出完运行结果后自动输出的一行信息。
程序分析:先看第2行,其中 main是函数的名字,表示“主函数”, main前面的int表示此函数的类型是int类型(整型)。在执行主函数后会得到一个值(即函数值),其值为整型。程序第5行“return 0;”的作用是:当main函数执行结束前将整数0作为函数值,返回到调用函数处0。每一个C语言程序都必须有一个main函数。函数体由花括号{}括起来。本例中主函数内有两个语句,程序第4行是一个输出语句,printf是C编译系统提供的函数库中的输出函数(详见第4章)。printf 函数中双撇号内的字符串"This is a C program."按原样输出。n是换行符,即在输出"This is a C program."后,显示屏上的光标位置移到下一行的开头。这个光标位置称为输出的当前位置,即下一个输出的字符出现在此位置上。每个语句最后都有一个分号,表示语句结束。
在使用函数库中的输入输出函数时,编译系统要求程序提供有关此函数的信息(例如对这些输入输出函数的声明和宏的定义、全局量的定义等),程序第1行“#include
①C99建议把main 函数指定为int型(整型),它要求函数带回一个整数值。在 main函数中,在执行的最后设置一个"return 0;"语句。当主函数正常结束时,得到的函数值为0,当执行 main 函数过程中出现异常或错误时,函数值为一个非0的整数。这个函数值是返回给调用main函数的 *** 作系统的。程序员可以利用 *** 作指令检查main 函数的返回值﹐从而判断 main函数是否已正常执行,并据此决定以后的 *** 作。如果在程序中不写“return 0;”语句,有的C编译系统会在目标程序中自动加上这一语句,因此。主函数正常结束时,也能使函数值为0。为使程序规范和可移植性,希望读者写的程序一律将main函数指定为int型,并在main函数的最后加--个“return 0;"语句。
的作用就是用来提供这些信息的。stdio. h是系统提供的一个文件名, stdio是“standardinput & output”的缩写,文件后缀.h的意思是头文件(header file),因为这些文件都是放在程序各文件模块的开头的。输入输出函数的相关信息已事先放在stdio. h文件中。现在,用#include指令把这些信息调人供使用。对编译预处理指令#include,在此读者可先不必深究,只要记住:在程序中如要用到标准函数库中的输入输出函数,应该在本文件模块的开头写上下面一行:
# include
在以上程序各行的右侧,如果有//,则表示从到本行结束是“注释”,用来对程序有关部分进行必要的说明。在写C程序时应当多用注释,以方便自己和别人理解程序各部分的作用。在程序进行预编译处理时将每个注释替换为一个空格,因此在编译时注释部分不产生目标代码,注释对运行不起作用。注释只是给人看的,而不是让计算机执行的。
说明:C语言允许用两种注释方式:
(1)以//开始的单行注释。如上介绍的注释。这种注释可以单独占一行,也可以出现在一行中其他内容的右侧。此种注释的范围从//开始,以换行符结束。也就是说这种注释不能跨行。如果注释内容一行内写不下,可以用多个单行注释,如下面两行是连续的注释行:
//如注释内容一行内写不下
//可以在下一行重新用“//”,然后继续写注释。
(2)以结束的块式注释。这种注释可以包含多行内容。它可以单独占一行(在行开头以结束),也可以包含多行。编译系统在发现一个,把二者间的内容作为注释。
但应注意的是在字符串中的//和n");
输出分别是:
/ / how do you do!
和
/ * how do you do! * /
注释可以用汉字或英文字符表示。
在C 89只允许用形式的注释,而C++则允许用//形式的注释,//注释被称为“C++风格”的注释。但许多C编译系统在C99之前就已支持这种方便的注释方法,C 99正式将//注释纳入C语言新标准。目前使用的一些编译系统(如Visual C++ 6.0,Turbo C++ 3.0,GCC)等都支持//单行注释。在本书的程序中,将利用//对程序的各部分作简要的说明。如果读者输入并运行这些程序,可不必包括注释部分。
例1.2 求两个整数之和。
解题思路:设置3个变量,a和b用来存放两个整数,sum用来存放和数。用赋值运算符“=”把相加的结果传送给sum。
编写程序:
#include//这是编译预处理指令 int main() //定义主函数 { //函数开始 int a, b, sum; //本行是程序的声明部分,定义a,b,sum为整型变量 a = 123; //对变量a赋值 b = 456; //对变量b赋值 sum = a + b; //进行a+b的运行,并把结果存放在变量中 printf("sum is %dn", sum); //输出结果 return 0; //使函数返回值为0 } //函数结束
运行结果:
sum is 579 E:TortoiseGit-2.8.0.0-64bitcs_-cC_2021_11_01DebugC_2021_11_01.exe (进程 15864)已退出,代码为 0。 要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口. . .
然后换行,程序执行结束。
程序分析:本程序的作用是求两个整数a和b之和。第4行是声明部分,定义a,b和sum为整型(int)变量。第5,6行是两个赋值语句,使a和b的值分别为123和456。第7行使sum的值为a与b之和。第8行输出结果,这个 printf函数圆括号内有两个参数,一个是双撇号中的内容sum is %dn,它是输出格式字符串,作用是输出用户希望输出的字符和输出的格式。其中 sum is是用户希望输出的字符(这和例1.1是一样的),%d是指定的输出格式,d表示用“十进制整数”形式输出。圆括号内第2个参数sum,表示要输出变量sum的值。在执行printf函数时,将sum变量的值(以十进制整数表示)取代双撤号中的%d。现在sum的值是579(即 123与456之和),所以在输出时,十进制整数579取代了%d(见图 1.1),n是换行符。
最后输出双撇号中的字符“sum is 579”,然后换行,程序执行结束。
由于本程序正常运行和结束,因此main函数的返回值应为0。现在并没有去检查和利用这个函数值,但是以后在某些时候会需要用到main函数值的。
例1.3 求两个整数中的较大者。
解题思路:用一个函数来实现求两个整数中的较大者。在主函数中调用此函数并输出结果。
编写程序:
# include//这是编译预处理指令 int main() //定义主函数 { //主函数体开始 int max(int x, int y); //对被调用函数max的声明 int a, b, c; //定义变量a,b,c scanf("%d,%d", &a, &b); //输入变量a和b的值 c = max(a, b); //调用max函数,将得到的值赋给c printf("max=%dn", c); //输出c的值 return 0; //返回函数值为0 } //主函数体结束 //求两个整数中的较大者的max函数 int max(int x, int y) { int z; //定义max函数。函数值为整型,形式参数x和y为整型 if (x > y)z = x; //若x>y成立,将y的值赋值给变量z else z = y; //否则(即x>y不成立),将y的值赋值给变量z return (z); //将z的值作为max函数值,返回到调用max函数的位置 }
运行结果:
8,5 max=8 E:TortoiseGit-2.8.0.0-64bitcs_-cC_2021_11_01DebugC_2021_11_01.exe (进程 1880)已退出,代码为 0。 要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。 按任意键关闭此窗口. . .
第1行输入8和5,赋给变量a和 b,第2行输出“大数为8”。
程序分析:本程序包括两个函数:①主函数main;②被调用的函数max。
max函数的作用是将x和y中较大者的值赋给变量z。第16行return语句将z的值作为max的函数值,返回给调用max函数的函数(即主函数main)。返回值是通过函数名max带回到main函数中去的(带回到程序第7行, main函数调用max函数处)。
程序第4行是对被调用函数max的声明(declaration)。为什么要作这个函数声明呢?因为在主函数中要调用max函数(主函数中第8行“c=max(a,b);”),而max 函数的定义却在main函数之后,对程序的编译是自上而下进行的,在对程序第7行进行编译时,编译系统无法知道max是什么,因而无法把它作为函数调用处理。为了使编译系统能识别max函数,就要在调用max函数之前用"“int max(int x,int y);”对max函数进行“声明”,所谓声明,通俗地说就是告诉编译系统max是什么,以及它的有关信息。有关函数的声明详见第7章。
程序第6行scanf是输入函数的名字(scanf和 printf都是C的标准输入输出函数)。该scanf函数的作用是输入变量a和 b的值。scanf后面圆括号中包括两部分内容:一是双撇号中的内容,它指定输入的数据按什么格式输人。“%d”的含义是十进制整数形式。二是输入的数据准备放到哪里,即赋给哪个变量。现在,scanf函数中指定的是a和b,在a和 b的前面各有一个&,在C语言中“&”是地址符,&a的含义是“变量a的地址”,&b是“变量b的地址”。执行scanf函数,从键盘读入两个整数,送到变量a和b的地址处,然后把这两个整数分别赋给变量a和 b。
程序第7行用max(a,b)调用max函数。在调用时将a和b作为max函数的参数(称为实际参数)的值分别传送给max函数中的参数x和y(称为形式参数),然后执行max函数的函数体(程序第12~17行),使max函数中的变量z得到一个值(即x和y中大者的值),return(z)的作用是把z的值作为max函数值带回到程序第7行=的右侧(主函数调用max函数的位置),取代max(a,b),然后把这个值赋给变量c。
第8行输出结果。在执行printf函数时,对双撇号括起来的max=%dn是这样处理的:将max=原样输出,%d由变量c的值取代之,n执行换行。
注意:本例程序中两个函数,都有return语句,请注意它们的异同。两个函数都定义为整型,都有函数值,都需要用return语句为函数指定返回值。但是main函数中的return语句指定的返回值一般为0,而max函数的返回值是max函数中求出的二数中的最大值z,只有通过return语句才能把求出的z值作为函数的值并返回调用它的位置上(即main函数,程序第7行)。不要以为在max函数中求出最大值z后就会自动地作为函数值返回调用处,必须用return语句指定将哪个值作为函数值。也不要不加分析地在所有函数的最后都写上“return 0;”。
本例用到了函数调用、实际参数和形式参数等概念,只作了简单的解释。读者如对此可能不大理解,可以先不予深究,在学到以后有关章节时自然迎刃而解。在本章介绍此例子,主要是使读者对C程序的组成和形式有一个初步的了解。
1.4.2 C语言程序的结构
通过以上几个程序例子,可以看到一个C语言程序的结构有以下特点:
(1)一个程序由一个或多个源程序文件组成。一个规模较小的程序,往往只包括一个源程序文件,如例1.1和例1.2是一个源程序文件中只有一个函数(main函数),例1.3中有两个函数,属于同一个源程序文件。在一个源程序文件中可以包括3个部分:
①预处理指令。如#include
②全局声明。即在函数之外进行的数据声明。例如可以把例1.2程序中的“int a,b,sum;”放到main函数的前面,这就是全局声明,在函数外面声明的变量称为全局变量。如果是在程序开头(定义函数之前)声明的变量,则在整个源积程序文件范围内有效。在函数中声明的变量是局部变量,只在函数范围内有效。关于全局变量和局部变量的概念和用法见本书第7章。在本章的例题中没有用全局声明,只有在函数中定义的局部变量。
③函数定义。如例1.1、例1.2和例1.3中的main函数和例1.3中的max函数,每个函数用来实现一定的功能。在调用这些函数时,会完成函数定义中指定的功能。
(2)函数是C程序的主要组成部分。程序的几乎全部工作都是由各个函数分别完成的,函数是C程序的基本单位,在设计良好的程序中,每个函数都用来实现一个或几个特定的功能。编写C程序的工作主要就是编写一个个函数。
一个C语言程序是由一个或多个函数组成的,其中必须包含一个main函数(且只能有一个main函数)。例1.1和例1.2中的程序只由一个main函数组成,例1.3程序由一个main函数和一个max函数组成,它们组成一个源程序文件,在进行编译时对整个源程序文件统一进行编译。
一个小程序只包含一个源程序文件,在一个源程序文件中包含若干个函数(其中有一个main函数)。当程序规模较大时,所包含的函数的数量较多,如果把所有的函数都放在同一个源程序文件中,则此文件显得太大,不便于编译和调试。为了便于调试和管理,可以使一个程序包含若干个源程序文件,每个源程序文件又包含若干个函数。一个源程序文件就是一个程序模块,即将一个程序分成若干个程序模块。
在进行编译时是以源程序文件为对象进行的。在分别对各源程序文件进行编译并得到相应的目标程序后,再将这些目标程序连接成为一个统一的二进制的可执行程序。
C语言的这种特点使得容易实现程序的模块化。
在程序中被调用的函数,可以是系统提供的库函数(例如 printf和 scanf函数),也可以是用户根据需要自己编制设计的函数(例如例1.3中的max函数)。C的函数库十分丰富,ANSIC建议提供一百多个标准库函数,不同的C编译系统除了提供标准库函数外,还增加了其他一些专门的函数,如Turbo C提供三百多个库函数。不同编译系统所提供的库函数个数和功能是不完全相同的。
(3)一个函数包括两个部分。
① 函数首部。即函数的第1行,包括函数名、函数类型、函数属性、函数参数(形式参数)名、参数类型。
例如,例1.3中的max函数的首部为
一个函数名后面必须跟一对圆括号,括号内写函数的参数名及其类型。如果函数没有参数,可以在括号中写void,也可以是空括号,如:
int main( void)
或
int main()
① 函数体。即函数首部下面的花括号内的部分。如果在一个函数中包括有多层花括号,则最外层的一对花括号是函数体的范围。
函数体一般包括以下两部分。
·声明部分。声明部分包括:定义在本函数中所用到的变量,如例1.3中在main函数中定义变量"int a,b,c;”;对本函数所调用函数进行声明,如例1.3中在main函数中对max函数的声明“int max(int x,int y); ”。
·执行部分。由若干个语句组成,指定在函数中所进行的 *** 作。
在某些情况下也可以没有声明部分(例如例1.1),甚至可以既无声明部分也无执行部分。如:
void dump ()
{}
它是一个空函数,什么也不做,但这是合法的。
(4)程序总是从main函数开始执行的,而不论main函数在整个程序中的位置如何(main函数可以放在程序最前头,也可以放在程序最后,或在一些函数之前、另一些函数之后)。
(5)程序中对计算机的 *** 作是由函数中的C语句完成的。如赋值、输入输出数据的 *** 作都是由相应的C语句实现的。C程序书写格式是比较自由的。一行内可以写几个语句,一个语句可以分写在多行上,但为清晰起见,习惯上每行只写一个语句。
(6)在每个数据声明和语句的最后必须有一个分号。分号是C语句的必要组成部分。如
c=a+b;
其中的分号是不可缺少的。
(7)C语言本身不提供输入输出语句。输入和输出的 *** 作是由库函数scanf 和 printf等函数来完成的。C对输人输出实行“函数化”。由于输人输出 *** 作涉及具体的计算机设备,把输入输出 *** 作用库函数实现,就可以使C语言本身的规模较小,编译程序简单,很容易在各种机器上实现,程序具有可移植性。
(8)程序应当包含注释。一个好的,有使用价值的源程序都应当加上必要的注释,以增加程序的可读性。
1.5 运行C程序的步骤与方法
在1.4节中看到的用C语言编写的程序是源程序。计算机不能直接识别和执行用高级语言写的指令,必须用编译程序(也称编译器)把C源程序翻译成二进制形式的目标程序,然后再将该目标程序与系统的函数库以及其他目标程序连接起来,形成可执行的目标程序。
在编好一个C源程序后,怎样上机进行编译和运行呢?一般要经过以下几个步骤:
(1)上机输入和编辑源程序。通过键盘向计算机输入程序,如发现有错误,要及时改正。最后将此源程序以文件形式存放在自己指定的文件夹内(如果不特别指定,一般存放在用户当前目录下),文件用.c作为后缀,生成源程序文件,如f.c。
(2)对源程序进行编译,先用C编译系统提供的“预处理器”(又称“预处理程序"或“预编译器")对程序中的预处理指令进行编译预处理。例如,对于#include
编译的作用首先是对源程序进行检查,判定它有无语法方面的错误,如有,则发出“出错信息”,告诉编程人员认真检查改正。修改程序后重新进行编译,如有错,再发出“出错信息”。如此反复进行,直到没有语法错误为止。这时,编译程序自动把源程序转换为二进制形式的目标程序(在Visual C++中后缀为.obj,如f.obj)。如果不特别指定,此目标程序一般也存放在用户当前目录下,此时源文件没有消失。
在用编译系统对源程序进行编译时,自动包括了预编译和正式编译两个阶段,一气呵成。用户不必分别发出二次指令。
(3)进行连接处理。经过编译所得到的二进制目标文件(后缀为.obj)还不能供计算机直接执行。前面已说明:一个程序可能包含若干个源程序文件,而编译是以源程序文件为对象的,一次编译只能得到与一个源程序文件相对应的目标文件(也称目标模块),它只是整个程序的一部分。必须把所有的编译后得到的目标模块连接装配起来,再与函数库相连接成一个整体,生成一个可供计算机执行的目标程序,称为可执行程序(executive program),在 Visual C+++中其后缀为.exe,如f.exe。
即使一个程序只包含一个源程序文件,编译后得到的目标程序也不能直接运行,也要经过连接阶段,因为要与函数库进行连接,才能生成可执行程序。
以上连接的工作是由一个称为“连接编辑程序(linkage editor)”的软件来实现的。
(4)运行可执行程序,得到运行结果。
以上过程如图1.2所示。其中实线表示 *** 作流程,虚线表示文件的输入输出。例如,编辑后得到一个源程序文件f.c,然后在进行编译时再将源程序文件f.c输入,经过编译得到目标程序文件f.obj,再将所有目标模块输入计算机,与系统提供的库函数等进行连接,得到可执行的目标程序f.exe,最后把f.exe输入计算机,并使之运行,得到结果。
一个程序从编写到运行成功,并不是一次成功的,往往要经过多次反复。编写好的程序并不一定能保证正确无误,除了用人工方式检查外,还须借助编译系统来检查有无语法错误。从图1.2中可以看到:如果在编译过程中发现错误,应当重新检查源程序,找出问题,修改源程序并重新编译,直到无错为止。有时编译过程未发现错误,能生成可执行程序,但是运行的结果不正确。一般情况下,这不是语法方面的错误,而可能是程序逻辑方面的错误,例如计算公式不正确、赋值不正确等,应当返回检查源程序,并改正错误。
为了编译、连接和运行C程序,必须要有相应的编译系统。目前使用的很多C编译系统都是集成环境(IDE)的,把程序的编辑、编译、连接和运行等 *** 作全部集中在一个界面上进行,功能丰富,使用方便,直观易用。
写出源程序后可以用任何一种编译系统对程序进行编译和连接工作,只要用户感到方便、有效即可。20世纪90年代,Turbo C2.0用得比较多,但Turbo C2.0是用于DOS环境的,在进入Turbo C集成环境后,不能用鼠标进行 *** 作,主要通过键盘选择菜单,不大方便。有的人改用Turbo C++ 3.0,它具有方便、直观和易用的界面,虽然它也是DOS环境下的集成环境,但可以用鼠标 *** 作菜单,因此在Windows环境下使用也很方便。近来,不少人用Visual C++对C程序进行编译。Visual C++6.0既可以对C++程序进行编译,也可以对C程序进行编译。本书的程序是用Visual C++集成环境进行编译的(本人在上传这本书时使用的是Visual Studio 2019,Visual C++6.0已经过时),熟悉它以后也会有利于今后进一步学习C++语言。
不应当只会使用一种编译系统,无论用哪一种编译系统,都应当能举一反三,在需要时会用其他编译系统进行工作。
在与本书配套出版的《C程序设计(第四版)学习辅导》中,详细介绍了常用的C编译工具的使用方法,可供读者上机调试程序时参考。
1.6 程序设计的任务
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)