单片机驱动数码管显示设计C语言.ppt
第一章 C语言程序设计初步,1.1程序与程序设计语言,1.1.1 常量一提起计算机,人们就会联想到键盘、显示器和主机。其实,应用要早得多、并一直流传至今的计算机工具是算盘(见图1.1)。那么,现代电子计算机与算盘的最大区别在哪里呢?关键在于现代计算机可以自动完成计算过程,而算盘进行的计算过程是在人的拨动下才能进行。,图1.1 算盘,那么,为什么现代计算机可以自动完成计算过程呢?这 首先要从程序说起。程序实际上是一个非常普通的概念:按照一定的顺序安排的工作步骤。可以说,做任何事情都有相应的程序。做的事情不同,要求的效果不同,程序就不同。例如,用同样的原料,采用不同的程序,会做出不同的菜肴来。一种工具能够自动工作,一是要有记忆功能,能够记住程序;二是具有按照程序控制相关部件操作的能力。如果能让算盘记住做某种计算的口诀和计算的数据,并且有能按照口诀控制算珠自动运动的机制,则只要发出开始执行的命令,算盘就会自动完成计算。可惜这样的机制并没有在算盘中实现。但是,却有另外一种机器却在这方面向前推进了一步。这就是明朝末年宋应星在其天工开物中记载的中国古代提花机(见图1.2)。,图1.2 中国古代的提花机,中国提花机大约出现于西汉末年(公元前)。它采用用丝线结成的“花本”(花版)控制经线起落,以织成要求的图样。这是最早的程序控制思想。后来,提花机沿着丝绸之路传到欧洲,历经改进,1805年法国人Joseph Jacquard制造成功用穿孔卡片(见图1.3)控制连杆(横针),用有孔和无孔进一步控制经线起落的提花机。,图1.3 穿孔卡片,穿孔卡片把程序控制技术向前推进了一步。这一技术被一位英国数学家Charles Babbage(见图1.4)引入到了计算机中机,用有孔和无孔的组合来表示数据和程序。,图1.4 英国数学家Charles Babbage,18世纪末,法国数学界调集大批数学家,组成了人工手算的流水线,经过长期艰苦奋斗,终于完成了17卷数学用表的编制,但是,手工计算出的数据出现了大量错误。这件事情强烈刺激了Babbage。1812年20岁的Babbage开始计算机的研制工作,他要把函数表的复杂算式转化为差分运算,用简单的加法代替平方运算,快速编制不同函数的数学用表,并将这种机器称为“差分机”。经过十年的努力,终于于1822年完成了第一台差分机,可以处理3个不同的5位数,计算精度达到6位小数。1833年他又开始投身于一种“会分析的机器”分析机的研制中。他把机器设计成三个部分,一是用来储存数据信息的“仓库(The Store)”,二是进行数据运算处理的“工场(The mill)”,三是使用穿孔卡片来输入程序并用穿孔卡片输出数据。这台机器虽然没有制造成功,但它的工作原理程序存储控制为今天的计算机奠定了基础:(1)任何工具的工作,都是由程序控制的;(2)只有工具具有了记忆程序的功能,并具有了按照程序进行自我控制的功能,该工具才能自动工作。,1.1.2 计算机程序设计语言程序要需要用某种形式(语言)来描述。例如,用算盘进行计算,程序是用口诀描述的,珠算的语言是口诀。现代计算机的程序则是用计算机程序设计语言来描述的。从计算机诞生到今天,程序设计语言也在伴着计算机技术的进步不断升级换代。1.机器语言一种CPU的指令系统,也称该CPU的机器语言,它是该CPU可以识别的一组由0和1序列构成的指令码。下面是某CPU指令系统中的两条指令:1 0 0 0 0 0 0 0(进行一次加法运算)1 0 0 1 0 0 0 0(进行一次减法运算)用机器语言编程序,就是从所使用的CPU的指令系统中挑选合适的指令,组成一个指令系列。这种程序虽然可以被机器直接理解和执行,却由于它们不直观,难记、难认、难理解、不易查错,只能被少数专业人员掌握,同时编写程序的效率很低,质量难以保证。这种繁重的手工方式与高速、自动工作的计算机极不相称。这种方式仅使用于计算机出现的初期(使用穿孔纸带的时期)的编程(用有孔、无孔,分别代表1、0),现在已经不再使用,2.汇编语言为减轻人们在编程中的劳动强度,20世纪50年代中期人们开始用一些“助记符号”来代替0,1码编程。如前面的两条机器指令可以写为A+B A或ADD A,BA-B 个样 A或SUB A,B这种用助记符号描述的指令系统,称为符号语言或汇编语言。用汇编语言编程,程序的生产效率及质量都有所提高。但是汇编语言指令是机器不能直接识别、理解和执行的。用它编写的程序经检查无误后,要先翻译成机器语言程序才能被机器理解、执行。这个翻译转换过程称为“代真”。代真后得到的机器语言程序称为目标程序(object program),代真以前的程序,称为源程序(source program)。由于汇编语言指令与机器语言指令基本上具有一一对应的关系,所以汇编语言源程序的代真可以由汇编系统以查表的方式进行。汇编语言与机器语言,都是依CPU的不同而异,它们都称为面向机器的语言。用面向机器的语言编程,可以编出效率极高的程序。但是程序员用它们编程时,不仅要考虑解题思路,还要熟悉机器的内部结构,并且要“手工”地进行存储器分配。这种编程的劳动强度仍然很大,给计算机的普及推广造成很大的障碍。,2.高级语言汇编语言和机器语言是面向机器的,不同类型的计算机所用的汇编语言和机器语言是不同的。1954年出现的FORTRAN语言以及随后相继出现的其它高级语言,开始使用接近人类自然语言的、但又消除了自然语言中的二义性的语言来描述程序。这些高级语言使人们开始摆脱进行程序设计必须先熟悉机器的桎梏,把精力集中于解题思路和方法上。第一种高级语言是1954年问世的FORTRAN语言。此后不久,不同风格、不同用途、不同规模、不同版本的面向过程的高级语言便风涌而起。据统计,全世界已有2500种以上的计算机语言,其中使用较多的有近百种。图1.5为几种广泛流行的高级语言的发展变迁情况。,图1.5 几种广泛流行的高级语言的发展变迁情况,1.1.3 高级语言程序的开发过程一般来说,程序开发的一般过程有如图1.6所示几个步骤。,图1.6 高级语言程序的开发过程,1.分析一般来说,一个具体的问题要涉及许许多多的方面,这是问题的复杂性所在。为了便于求解,往往要忽略一些次要方面。这种通过忽略次要方面,而找出解题规律,就称为建立模型。2.建立模型,表现模型表现模型就是用一种符号-语言系统来描述模型。一般来说,模型的表现会随着对问题抽象程度的加深和细化,不断由领域特色向计算机可解释、执行靠近,中间也可能采用一些其他的符号系统,如流程图等,直到最后用一种计算机程序设计语言描述出来。3.源程序的编辑源程序的编辑就是在某种字处理环境下,用具体的程序设计语言书写并修改的过程。为此就要掌握一种计算机程序设计语言。还要应用一种专用程序编辑器或通用的文字编辑器进行。,4.程序的编译(或解释)与链接写出一个高级语言程序后,并不是就可以立即拿来执行。要让机器直接执行,还要将它翻译成由机器可以直接辨认并可以执行的机器语言程序。为区别它们,把用高级语言写的程序(文件)称为源程序(文件),把机器可以直接辨认并执行的程序(文件)称为可执行程序(文件)。这一过程一般分为两步:第1步:在程序编辑过程中输入到源文件中的是一些字符码,但是机器可以直接处理的是0、1信息。为此,首先要将源程序文件翻译成0、1码表示的信息,并用相应的文件保存。这种保存0、1码信息的文件称为目标程序文件。由源文件翻译成目标文件的过程称为编译。在编译过程中,还要对源程序中的语法和逻辑结构进行检查。编译任务是由称做编译器(compiler)的软件完成的。目标程序文件还不能被执行,它们只是一些目标程序模块。,第2步:将目标程序模块以及程序所需的系统中固有的目标程序模块(如执行输入输出操作的模块)链接成一个完整的程序。经正确链接所生成的文件才是可执行文件。完成链接过程的软件称为链接器(linker)。图1.7为编译和链接过程的示意图。程序在编译、链接过程中,也可能发现错误。这时要重新进入编辑器进行编辑。,图1.7 编译和链接过程的示意图,5.程序的测试与调试经编译、链接的程序文件,生成可执行文件,就可以让计算机执行了。但是,并不是就可以得到预期的结果而交付用户使用了,因为程序仍然会存在某些错误。因此,每一个人编写出一个程序后,在正式交付使用前,总要试通一下。“试通”就是试运行程序,也就是对程序进行测试。测试是以程序通过编译、没有语法和链接上的错误为前提,目的是找出程序中可能存在的错误并加以改正。因此,应该测试程序在不同情况下运行的情况,输入不同的数据可以检测出程序在不同情况下运行的情况。测试的数据应是以“程序是会有错误的”为前提精心设计出来的,而不是随心所欲地乱凑而成的。它不仅应含有被测程序的输入数据,而且还应包括程序执行它们后预期的结果。每次测试都要把实际的结果与预期的结果相比较,以观察程序是否出错。,6.编写程序文档经过了问题分析、设计、程序编码、测试后,程序开发的工作基本上结束了。但是,这时还不能交付使用。因为,随着程序规模的增大和日益复杂化,程序的使用和运行也越来越不那么直接,用户要运行程序,还需要知道许多信息,如:程序的功能需要输入的数据类型、格式和取值范围需要使用的文件数量、名称、内容以及存放位置等程序运行需要的软、硬件环境程序的装入、启动方法以及交互方式等。为此,程序开发者需要向用户提供这些资料称为程序使用说明书或用户文档。需要说明的是,在许多软件中,这些内容已经部分或全部地以“readme”或“help”的形式提供。目前,程序文档已经成为软件开发产品的必要部分。文档在程序使用和维护中的重要性也改变了软件的概念,使之由早期的“软件是计算机程序的总称”演化为“软件是计算机的程序连同计算机化的文档的总称。”,7.程序的维护程序交付用户使用之后,并不是万事大吉了。由于多种原因,还可能要对程序进行修改。交付之后对程序的修改称为程序的维护。维护程序的原因主要有:原来的程序没有完全满足用户要求;用户要求的改变;程序中遗留有错误,在运行中被发现。程序的维护可以由开发者进行,也可能是由别人进行。为能便于程序的维护,开发者应当提供必要的技术资料,并且要保证程序的可读性好能让人看懂。,1.2 C语言及其标准,1.2.1 C语言的出现 C语言是目前程序设计领域中最有影响力的一种程 序设计语言。可是,它却是“漫不经心”地开发出来的。20世纪60年代,Bell实验室的Ken Thompson(见图1.8)着手开发后来对计算机产生了巨大影响的UNIX操作系统。为了描述UNIX,Thompson首先将当时的一种专门用来描述系统程序的BCPL语言改进为他称为B的语言。1970年Thompson发表了用汇编语言和B语言写成的PDP-7上实现UNIX的初版。,1971年,Dennis Ritchie(见图1.8)开始协助Thompson开发UNIX。他对B语言做了进一步的充实和完善,加入数据类型和新的句法,于1972年推出了一种新型程序设计语言C语言(取BCPL的第2个字母)。为了使UNIX操作系统推广,1977年Dennis M.Ritchie 发表了不依赖于具体机器系统的C语言编译文本可移植的C语言编译程序。于是,C语言是借助UNIX操作系统的翅膀而起飞的,UNIX操作系统也由于C而得已快速移植落地生根,两者相辅相承,成就了软件开发史上历时30年的时代。,图1.8 Thompson(左)和Ritchie(中)于1999 年接受当时美国总统克林顿授予的国家技术勋章,1971年,Dennis Ritchie(见图1.8)开始协助Thompson开发UNIX。他对B语言做了进一步的充实和完善,加入数据类型和新的句法,于1972年推出了一种新型程序设计语言C语言(取BCPL的第2个字母)。为了使UNIX操作系统推广,1977年Dennis M.Ritchie 发表了不依赖于具体机器系统的C语言编译文本可移植的C语言编译程序。于是,C语言是借助UNIX操作系统的翅膀而起飞的,UNIX操作系统也由于C而得已快速移植落地生根,两者相辅相承,成就了软件开发史上历时30年的时代。,图1.8 Thompson(左)和Ritchie(中)于1999 年接受当时美国总统克林顿授予的国家技术勋章,1978年Brian W.Kernighian和Dennis M.Ritchie出版了名著The C Programming Language,从而使C语言成为目前世界上流行最广泛的高级程序设计语言。以后,又有多种程序设计语言在C语言的基础上产生,如C+、Visual C+、Java、C#等。,1.2.2 C语言的标准C语言的灵活性、丰富性、可移植性很快得到了普遍的承认,接着适合于各种不同操作系统(UNIX,MS-DOS,CP/M-80,86等)和不同机种(字长为8bit32bit)的C语言编译系统相继出现。1982年美国国家标准局(ANSI)语言标准化委员会成立了一个委员会开始着手进行C语言的标准化工作,并于1983年公布了第一个C语言标准草案(83 ANSI C)。1989年,ASNI又发布了一个完整的C语言标准ANSI X3.159-1989,通常称做“ANSI C”,简称“C89”。1990年,国际标准组织ISO/JEC JTC1/SC22/WG14采纳了C89,做了少编辑性修改后,以国际标准ISO/IEC 9899:1990发布,通常称其为“C90”,它同C89基本相同。1995年,WG14对C89做了两处技术修订和一个扩充。人们将其称为“C89增补1”或“C95”。同时,WG14开始着手对C标准做全面修订,并于1999年完成获得通过,形成正式的C语言标准,命名为ISO/IEC 9899:1999,简称“C99”。本书将基于C99介绍C 语言程序设计的基本方法。目前各厂家所提供的所有C编译系统都还未实现C99所建议的功能。为了读者能实际运行C程序,本书所介绍的程序都是符合ASNI C标准并能在大多数C编译系统通过和运行的程序。但在文字叙述中,会介绍C99所增加的新功能,以使读者今后能顺利地过渡到用C99编程。,1.3 C语言概要,1.3.1 函数任何一部机器都是用部件组装而成的。计算机程序和机器一样,也是由一些部件构建起来的。C语言程序部件是函数。也就是说,设计C语言程序就是设计它的构成函数。下面举例说明C语言程序中的函数是什么样的。例1.1 一个输出一串字符的C程序。,/*文件名:ex010101.c*/#include int main(void)printf(Programming is fun.);/*输出一串字符*/return 0;/*向操作系统返回一个数字0*/,这是一个非常简单的C语言程序,它的执行结果是显示一行字符:Programming is fun.说明:(1)这里 是一个函数。这个函数的名字为“main”。这个名字是专用的,表示这个函数是“主函数”。所谓主函数,就是执行这个程序时,由操作系统直接调用的函数。每一个C语言程序必须也只能有一个主函数。,int main(void),(2)函数名后面的圆括号用于表示参数。一般说来,用函数进行计算,需要给定参数。但是广义的计算也可以没有参数而只执行一个过程。在C语言程序中,参数部分写为“void”,表示该函数没有参数,只执行一个过程。“void”可以省写,如程序第一行可写为:int main()在许多教材和程序中,可以常常见到这种形式的主函数首行。但是,C标准建议写上void,使含义清晰。在本书的程序中都是写成main(void)形式的。(3)再后面的一对花括号中的部分称为函数体,用来表明该函数的功能是如何实现的。通常,函数体用一些语句表述。C语言规定语句必须用分号结束。先分析下面的语句:printf(Programming is fun.);它的功能是调用编译系统提供的函数库中的一个函数printf(),来输出后面的一串字符。函数printf的使用比较复杂,后面将陆续介绍。,(4)函数名前面的“int”表明函数的返回值是一个整数。有的操作系统(如Unix)要求在执行一个程序后应向系统返回一个整数值,如程序正常执行和结束,应返回0,否则返回一个非0值。因此,需要将main函数指定为int(整型),同时在函数体的最后写一返回语句:return 0;它的功能是向调用者(操作系统)返回0值,表示主函数正常结束(也就是程序正常结束)。此语句必须写在函数体的最后一行才有意义,因为只要执行到这条语句,就表达程序正常结束,向操作系统返回一个0,如果程序未执行到这个返回语句就非正常结束了,就不会向操作系统返回0。操作系统会据此作出相应的处理。,有的操作系统(如DOS,Windows)并无程序必须返回整数的要求,因此,可以不指定main函数为整型。这时可在main函数的前面加上void,如 void main(void)或 void main()表示main函数是无类型的,不返回任何类型的值。显然在main函数的最后也不必写返回语句“return 0;”。读者可以在其他教材或程序中看到这种形式的main函数。以上两种用法都是是合法的、有效的,编程者可以根据情况决定。为了使程序具有一般性,采用以下形式 int main(void)并在函数体最后有“return 0;”语句。,(5)程序最前面的#include 是一种在程序编译之前要处理的内容,称为编译预处理命令。编译预处理命令还有一些,它们都用“#”开头,并且不用分号结束,所以不是C语言的语句。这里的编译预处理命令称为文件包含命令,它的作用是在编译之前把程序中需要使用关于系统定义的函数printf()的一些信息文件stdio.h包含进来。用“.h”作为后缀的文件称为头文件。(6)“/*/”中的文字用于做一些说明注释,让读程序的人容易读懂。例如,注释/*文件名:ex1_01.c*/是告诉读程序的人,这个程序的源代码用文件ex1_01保存。而其他两个注释是对其左面两条语句功能的说明。上面的程序只由一个函数组成(在主函数中又调用了库函数printf()。在例1.2中将介绍由两个函数组成的程序。,例1.2 计算两个整数(2,3)相加的结果,/*文件名:ex010201.c*/#include int add(int,int);/*声明后面将要使用函数add()*/int main(void)int s;/*声明后面使用的变量s是整型的*/s=add(2,3);/*调用add()进行计算,并用s接收*/printf(“The sum is:%d”,s);/*输出s的值*/return 0;int add(int a,int b)/*函数add()的定义*/int sum;/*定义一个整数sum,用于存放和*/sum=a+b;/*将a和b求和,并把结果送sum*/return sum;/*返回sum的值到调用者*/,说明(1)图1.9表明了该程序的执行过程。为了清晰,仅列出了执行语句。,图1.9 程序ex1_02的执行过程,经过编译、链接后的C语言程序就成为一个可执行文件。例如,程序的ex1_02的默认可执行文件名为“ex1_02.exe”。若要执行这个程序,只要在操作系统的命令执行环境中打入这个文件名,系统就会开始执行这个程序。对于C语言程序而言,首先从调用主函数开始。在主函数main中,第一个语句是 s=add(2,3);但是,这个语句的执行要分如下步骤才能完成。调用函数add(),同时将数据2和3分别传送给函数add()中的变量a和b;使用表达式a+b进行加法计算。将和用“=”)送给函数add()中的变量sun中。注意“=”是赋值操作符,不是等号。C语言中的等号是“=”。用return语句将sum的值返回的函数add()的调用处。将函数add()的返回值送给主函数中的变量s。,执行函数printf(),输出下面的内容:The sun is 5这个语句的执行也需要如下多个步骤才能实现:圆括号中的引号中的“The sum is:”要求原样输出。圆括号中的引号中的“%”表示后面的字符“d”是一个格式字符,要求将双引号后面的表达式的值,按照整型数据输出。函数printf()将流程返回到调用处。printf()也有返回值(成功返回输出的字符个数;失败时,返回一个负整数),但是一般不用。执行main()中的返回语句return,用“0”向操作系统送回“平安”信号。,(2)变量及其类型本例中的s和sum都称为变量。变量是程序中被命名的数据实体,并且它的值是可以改变的。同时,为了便于计算与存储,C语言中程序中所使用的每个数据都被规范化了。这种数据的规范称为数据类型。本例中使用语句 int s;和 int sum;的作用就是声明了两个变量s和sum名字和类型(用“int”表明它们是整型数据)。变量在使用之前都要先行声明。,(3)函数的声明 本例中的 int add(int,int);称为函数声明。函数声明的作用是让编译器知道该函数的原型(包括返回类型、参数个数和类型,以便对调用语句进行语法检查。如果定义在调用前,从定义可以直接获得这些信息,就可以不写声明;如果调用在定义之前,则需要一个原型声明说明这些信息。对于编译系统提供的库函数,它们的定义不在程序中,因此需要给出相应的原型声明。为了方便使用,系统把某些类型的库函数的原型声明写在某个头文件中,程序员只要把要求的头文件用文件包含语句写在程序中函数调用之前,就等于把原型声明写在了函数调用之前。这就是使用函数printf(),必须在其前写一条#include 的原因。,(4)关于printf()函数的参数 printf()函数的参数有两部分:前面的用双引号引起的部分称为“控制串”。控制串由一些字符组成,这些字符可以分成两类:第一类字符可以直接显示出来,第二类字符作为格式说明符使用。或者说,除了格式说明符之外的字符,都是可以直接显示的。格式说明符是由“%”开头,后面跟着的是格式码。本例中的“d”就是格式码,它后面输出的数据按照带符号十进制输出。其他格式说明符将陆续介绍。,(5)关于赋值运算 在C语言中,符号“=”称为赋值运算符,它的作用是把后面(右面)的值,送到其前(左面)的变量(左值)中。一定不要将其当作是等号。在C语言中,等号是“=”。例1.3 计算一个数的正弦值的C语言程序。,/*文件名:ex010301.c*/#include include int main(void)float x;/*定义x为实型变量*/x=sin(0.19199);/*调用sin函数*/printf(%fn,x);/*调用printf函数,输出x的值*/,程序的执行结果如下:,0.190813,说明:(1)“float x;”是声明:x是一个实型变量。(2)“x=sin(0.19199)”可执行一次函数调用,求出0.19199弧度的正弦值,并赋给实型变量x。sin()是一个库函数,math.h是其要求的头文件。(3)printf()中的格式说明符“%f”,指定一个实型格式输出(前面介绍的%d是整型数据格式符)。通常输出的数据在小数后有6位数字;小数点前的数字位数不指定,根据实际值的位数输出。格式说明符的类型要与后面要输出的数据类型相一致。(4)printf()中的“n”称为转义字符序列,前面加了反斜杆后,“n”不再作为字符,而是作为一条换行命令使用。转义字符还有一些,以后陆续介绍。,从这一小节可以得出如下结论:C语言程序是由函数组成的。设计C语言程序时,一个必须设计的函数是主函数。C语言的执行是从系统调用主函数开始的。主函数的部分功能也可以通过其他子函数补充实现。子函数应当首选从函数库中的函数;函数库中没有时。可以考虑自行设计。使用库函数时,要用文件包含命令将需要的头文件包含到程序中调用该库函数之前。,1.3.2 C语言的标准由前一小节中的例子可以看出,在C语言程序中,函数下面的组成单位是语句。在C99中,基本的语句有表达式语句、流程控制语句和块语句。1.表达式语句C语言程序的具体计算过程是由表达式完成的。表达式是由运算符(如上述+,=等)、变量(如上述s,sum,a,b,x等)和常量(如上述2,3,0.19199等)组成。前面使用过的 s=add(2,3)sum=a+b x=sin(0.19199)都是表达式。表达式加上语句结束符(分号)就构成表达式语句。学习C语言程序设计,必须掌握正确地使用变量、常量和运算符的表示方法和使用规则。变量和常量的使用涉及它们的数据类型、表示(命名)规则等,后面要专门介绍。,C语言中的运算符种类很多,正确地使用这些运算符,有三点需要注意:它们的含义。特别要区分一个运算符符号在C语言中和在普通数学中的意义的不同。如“=”。优先级,即在一个表达式中存在多个运算符时,进行运算的先后顺序。结合性,即在一个表达式中有多个优先级别相同的运算符时,先进行哪个运算符的运算。例如,在表达式2*3/5(在C语言中,“*”为乘运算符。“/”为除运算符)中,先进行除呢,还是先进行乘。在这个表达式中,好像对运算结果没有影响,但有时是有影响的。关于这些问题,后面将专门介绍。,2.流程控制语句一般说来,程序中的语句是按照书写顺序执行的。但是,有些情况下,需要改变默认的执行顺序,例如像图1.10(a)那样要从两个或多个语句中挑选一个语句执行,或者像图1.10(b)那样要重复执行某一个语句或语句块。前者称为选择控制,后者称为重复控制。,(a)选择结构(b)重复结构 图1.10 两种基本的流程控制结构,下面给出两个实例。例1.4 由键盘输入两个数,输出其中的大数。本例中函数max2()的执行过程如图1.11所示。,图1.11 函数max2()的执行过程,程序如下:,/*文件名:ex010401.c*/#include float max2(float x,float y)float max;if(x=y)/*选择判断*/max=x;/*条件满足进行的运算*/else max=y;/*条件不符进行的运算*/printf(“The max is:%f”,max);int main(void)float a,b;printf(“Input two real numbers:n”);/*输入提示*/scanf(“%f%f”,说明:(1)本例的函数max2()中有一个选择结构,条件是“x=y”。满足该条件,则执行运算max=x;不满足,则执行运算max=y。这样,就在max中保存了x和y中的大者。(2)在主函数中,函数scanf(“%f%f”,&a,&b)的功能是从键盘上输入两个实数,分别存放到地址&a和&b中。地址&a和&b是变量a和b地址,“&”是一个运算符。用于计算其后面变量的地址。(3)在函数scanf(“%f%f”,&a,&b)中,“%f”表示要输入的数据是实型数据。也就是说,格式说明符的类型,要与输入数据的类型一致。另外,键入的两个数据之间应当以空格、制表符(按Tab键)或回车分隔。程序运行时的情形如下:,Input two real numbers:1.235 2.345The max is:2.345,例1.5 求的值。程序如下:,/*文件名:ex010501.c*/#include int sigma(int n)int i=0,sum=0;while(i=n)i+;sum=sum+i;renturn sum;int main(void)int m,total;scanf(“%d”,说明:(1)声明,int i=0,sum=0;,有两个功能:一是定义了两个整型变量;二是对两个整型变量设定了初值。这称为变量的初始化。变量在没有初始化时并且也没有执行赋值操作之前,其值是不确定的。为了避免使用这些不确定的值,应当尽量在变量定义的同时对其进行初始化。一般说来,存放和的变量的初始值应当为0,而存放积的变量的初始值应当为1。,(2)在本例中,函数sigma()用来计算。计算的方法 是:先设置sum的初值为0、i的初值为0。用表达式“i+”将自增1,相当于执行操作:i=i+1。每执行一次i的自增1,执行一次sum=sum+i。用流程图表示如图1.12所示。,图1.12 函数sigma()的执行过程,除了上述两种控制语句外,C语言还提供了其他一些控制语句,以后会专门介绍。,3.块语句 块语句也称为复合语句,就是用一对花括号将一组语句括起来。在一个块语句中可以包括若干声明和若干语句。在例1.8的函数sigma()中,while下面的用花括号括起的两个语句,就组成一个块语句。块语句在语法上相当于一条语句。因此,当语法上需要一个语句,而一个语句又不能满足需要时,就必须使用块语句。,1.3.3 名字与声明1.标识符与关键字 标识符也称为名称。变量的名字、函数的名字、文件的名字等,都是关键字。在C语言程序中,所使用的标识符应当符合如下的词法规则:(1)标识符是大小写字母、数字和下划线组成的序列,但不能以数字开头。例如,下面是合法的C标识符:a A Ab _Ax _aX A_x abcd 但是下列不是合法的C标识符:5_A(数字打头)A-3(含非法字符)(2)C语言区别同一字母的大小写,如abc与abC被看作识不同的名标识符。(3)C89要求C编译器能识别的标识符长度为不少于31个有效字符,C99要求C编译器能识别的标识符长度为不少于63个有效字符。一个标识符中超过了这个长度的字符可能会不被辨认。例如在符合C99的系统中,当两个标识符的前63个字符都相同时,不管后面的有效字符是否相同,都可能被当作同一个标识符。,(4)普通标识符不能使用对于系统有特殊意义的名称。这些对系统有特殊意义的名称称为关键字。表1.1为C99关键字。,表1.1 C99关键字,在一个程序中,往往要使用大量的名字。大量的名字的使用,可能会造成名字的冲突和使用错误。为此,除了上述词法规则外,人们还总结了在程序中使用“好”名字的一些原则:(1)尽量做到“见名知义”,以增加程序的可读性。(2)尽量避免使用容易混淆的字符,例如 0(数字)-O(大写字母)-o(小写字母)1(数字)-I(大写字母)-i(小写字母)2(数字)-Z(大写字母)-z(小写字母)(3)名字不要太短,一般函数名尽量使用动宾结构,如PrintCalendar、IsPrime等。(4)许多国外的Windows程序员还采用匈牙利人Charles Simonyi提出的将类型作为变量名前缀的命名方法通常称为匈牙利命名法。表1.2为部分常用匈牙利前缀。,表1.2 部分常用匈牙利前缀,2.声明 在程序中,有许多东西是需要系统为其开辟存储空间的,例如变量、函数类型定义等。它们都有自己的名字,并且要在内存中独立存储,为此可以将它们称做程序实体。那么,如何建立它们的名字与实体之间的关联呢?这就是声明的作用。声明也称为说明,它的作用非常重要,包括如下一些:告诉编译器,一个名字是与哪个实体联系,不能张冠李戴。告诉编译器,也要程序员明白这个实体的类型。告诉编译器,这个实体什么时候建立?在什么范围内可以使用?前面已经使用过了变量和函数的声明。所以,例1.2中的 int s;就是建立变量名s与它的实体之间的关联。在一个语句块中关于声明的进一步用法,后面还要陆续介绍。目前要牢记的是,在使用一个程序实体之前,一定要让编译器知道该程序实体的属性。在C99之前,对变量和函数的声明不作为语句(尽管它们也是用分号结尾),它们必须出现在C语句的前面(声明的位置必须集中写在语句之前)。C99改变了这一做法,它吸取了C+的做法,声明不必集中放在执语句之前,可以出现在程序中的任意行。这样,C语句就有执行语句和非执行语句之分。声明是非执行语句,表达式语句和流程控制语句是执行语句。,1.3.4 变量及其赋值1.变量 变量(variable)是一种程序实体。它具有一个值,并且这个值是可以通过程序操作改变的。2.变量的赋值运算 在C语言中,符号“=”称为赋值运算符,它连接了左右两个操作数(即运算量):右操作数也称右值,可以是一个表达式,左操作数也称左值(lvalue),只能是变量。赋值操作的过程是把右操作数的值先转换成左操作数(变量)的类型,并把该值存放到左操作数(变量)中。例如,int a;a=2.6;printf(“%d”,a);,结果为,2,这是因为计算机在执行上述语句时,首先将2.6舍去小数部分截尾(truncation)成整型,赋值给变量a。,应当注意,赋值运算符是“=”。这个符号不是等号。例如,int a=2,b=3;a=a+b;,的操作是把表达式a+b的值(2+3)送到(赋值给)变量a。即经上述操作后,变量的a的值由2变为5。图1.13表明这一操作过程:先计算a+b的值,然后把这个结果送到变量a中。于是,变量a的值由2变为5。,图1.13 a=a+b的操作过程,赋值运算符具有“自右至左”的结合性,例如,int a=0,b=0,c=0;a=b=c=5+3;,相当于,int a,b,c;a=(b=(c=(5+3));,即先计算把5+3的值,得8,赋值给变量c;再把变量c的值(8)赋值给变量b;最后把变量b的值(8)赋值给变量a。图1.14表明这一操作过程。执行的结果,a、b、c三个变量中的值均为8。也就是说,从一个变量向另一个变量赋值后,原来变量中的值并不会消失或改变。所以,赋值操作相当于拷贝,而不是移动。,(c)执行操作b=c=5+3后变量a、b、c的值,(b)执行操作c=5+3后变量a、b、c的值,(a)操作前变量a、b、c的值,(d)执行操作a=b=c=5+3后变量a、b、c的值,图1.14 a=b=c=5+3的操作过程,1.3.5 算术运算算术运算是一切计算的基础,也是大家都认为非常熟悉的。但是对于高级程序设计语言尤其是C语言中的算术运算符,还需要有一个再学习的过程。C语言中的算术运算符与普通数学中的算术运算符有如下一些不同。运算符符号有所不同。种类有所不同。结合性可能会破坏交换率。1.C语言的基本算术运算符 表1.3为C语言中的基本算术运算符及其说明。,表1.3 C语言中的基本算术运算符及其说明,说明:(1)这几个算术运算符的运算对象有两个,所以也称为双目算术运算符。(2)这几个算术运算符的优先级别为:*、/、%高于+、-。并且它们都比赋值运算符的优先级别高。所以,在一个表达式中有赋值运算符,也有算术运算符时,不使用圆括号,可以先进行算术运算,后进行赋值运算。(3)这几个算术运算符均为“自左至右”。(4)需要特别注意的是整数除和模运算的结果都是整 数。例1.6 分析下面的程序的执行结果。,/*文件名:ex010601.c*/#include int main(void)printf(300*2/3=%dn,300*2/3);printf(2/3*300=%dn,2/3*300);return 0;,观察上面的程序,按照交换率,似乎它们的计算结果应该相同。但是,非常遗憾!结果完全不同:,300*2/3=2002/3*300=0,原因在于算术运算符具有自左至右的结合性,即对于第1个表达式语句,执行的顺序为:300*2=600,600/3=200 对于第2个表达式语句,执行的顺序则为:2/3=0(注意是整数相除),0*300=0 因此,使用整数除,应当特别小心。,2.自反算术赋值运算符 前面介绍过这样的赋值表达式:a=a+b它的作用是将变量a的值加上变量b的值,再送回到变量a中。或者说是将变量a的值增加一个变量b的值。这样类似的运算很多。为此C语言为这种运算提供了一种简洁形式:a+=b这样,就可以用一个复合运算符代替原来的两个运算符。这种复合运算符称为自反算术赋值运算符。除自反加以外,还有下列一些:-=(自反减赋值)*=(自反乘赋值)/=(自反除赋值)%=(自反模赋值)自反算术赋值的结合方向与赋值运算符一样,为自右向左。另外,它的优先级别相当低,与赋值是同一级别。例如表达式语句:,c=b*=a+2;,相当于如下两个表达式语句:,b=b*(a+2);c=b;,3.自加和自减运算 自反算术赋值运算中有两种更特殊的情况,即:i=i+1 即 i+=1 和 i=i-1