《第2章用C语言表达程序》由会员分享,可在线阅读,更多相关《第2章用C语言表达程序(86页珍藏版)》请在金锄头文库上搜索。
1、第2C语言表达程序2.1 C程序的结构 C程序是由若干个函数组成其中有且只有一个主函数(main)。主函数是程序的入口,主函数调用其它函数完成任务,相当于把一个大任务分成若干小任务逐块完成。一个程序的整体思路是:“输入-处理-输出”的模式。在程序中需要用到像变量一类的标识符,必须先定义后使用。程序中通常需要用到输入输出函数,比如键盘输入、屏幕输出,因此,希望每一个程序的第一行都加上 #include 2.1.1 简单结构 【例2-1】输入两个整数,输出其和。【分析】 输入:用键盘输入两个数存入变量a,b;用函数scanf实现。 处理:直接用加法运算a+b,将结果存入变量c;“存入”用C语言的赋
2、值操作实现。 输出:用函数 printf 向屏幕输出。【说明】 #include 相当于把系统中的文件stdio.h的内容插入到程序中。 程序中使用到标准输入函数scanf和标准输出函数printf,在使用它们之前必须先声明,而文件stdio.h中的主要内容就是对标准输入输出函数调用格式的说明(称函数声明),所以通常放在程序的开头,称*.h(head)的文件为头文件。这一行叫做“包含标准输入输出头文件”。 C语言规定,所有标识符都必须先定义(即规定标识符的名称和其他属性),后使用。如变量名、符号常量名、函数名。 标识符的名字必须是以字母或下划线开头的字母、数字、下划线串。不能以数字开头,也不能
3、含有其它符号,如小数点、空格、逗号等。apple-pric、3W、都不是一个合法的标识符,下面几种是合法的:apple_price、ApplePrice、ApplePrice2、W3。 标识符尽量取成有意义的名字,以便阅读程序方便。如用max表示最大、min表示最小、ApplePrice表示苹果的价格等等。现在流行的匈牙利取名法,比如fApplePrice表示苹果的价格是浮点数(实数),不但具有实际名字的意义,而且还具有程序设计的意义。 C语言区别标识符的大小写字母。如max与Max是两个不同的标识符。 一对花括号表示了一段语义的开始与结束。 上面这个简单程序表达了清晰的“输入-处理-输出”的
4、工作流程。 程序中给出适当的注释,可以使阅读程序更容易。/* */ 方便阅读和备忘。 / 单行注释。【例2-2】输入两个整数,输出它们的平方和。【分析】 输入:用函数scanf实现键盘输入两个整数,存入变量a,b; 处理:计算a2+b2,在C语言中用a*a+b*b表达,将计算结果存于变量square中; 输出:把第步的计算结果square输出,用函数printf实现。 【说明】 本例多用了一个printf,目的是在程序运行时屏幕上出现需要输入数据的提示。 变量名square比变量名c更有意义,增加了程序的可读性。 本程序仍用“输入-处理-输出”的思维模式,初学者切记此法。2.1.2 简单函数构
5、造与调用一个问题可以分解为更容易解决的小问题,把每一个小问题构造成一个函数,使程序结构更清晰。另外,有一些问题可能会反复用到某一个小问题,写程序时只需要构造一个函数而反复使用。【例2-3】圆的周长与面积。【分析】 输入:用函数scanf实现键盘输入圆的半径r。 处理:用求圆的周长和面积的公式,计算周长c、面积s。 输出:把上面的计算结果c和s用函数printf向屏幕输出。圆的半径决定了圆的性质,因此,只要输入圆的半径,其周长和面积就容易计算了。其中圆周率只能取近似值。【说明】 如果程序运行时,用户输入的半径是一个负数,计算还有意义吗? 为了避免这样的情况发生需要在程序中做“判断”,后面例题将讲
6、到。 这里用到实数类型float,对应的输入输出函数中也有变化。对比前面例2-1用到的整数类型int及其输入输出格式符号(%d)的区别。输入输出格式控制字符对应输入输出数据的类型,在后面章节中再细讲。#include void main( ) float r, c, s; /定义变量r为半径,c为周长, /s为面积,它们都为实数 scanf(%f, &r); / 输入圆的半径 c = 2 * 3.1415926 * r; s = 3.1415926 * r * r; printf(周长为:%f, 面积为:%fn, c, s); 在某些问题中需要反复计算不同半径的周长和面积,可以把这两个计算专门
7、拿出来 #include float c(float r) return 2*3.1415926*r;float s(float r) return 3.1415926*r*r;void main( ) / 主函数float r ; /* 定义变量r为半径 */scanf(%f, &r); / 输入圆的半径printf(周长为:%f, 面积为:%fn, c(r), s(r) ); 上面这个程序把计算周长和面积单独定义成函数c和函数s,这两个函数只要有一个半径给它,它就能返回一个值(用return语句返回函数值). 函数定义的一般格式为函数值类型 函数名(参数表) /函数头 /函数体语句;函数参
8、数函数值图2.2 函数接口设计函数的参数可能有多个,但函数返回值最多只有一个。如果函数不需要返回值,则函数名前用空类型(如上面程序的主函数用的void类型);如果函数需要返回值,则函数名前需明确返回值的类型,在函数中用return实现返回值; 函数头部非常重要,它说明了一个函数的“入”和“出”,至于函数是如何实现的,调用者不关心。上面程序中周长是如何求出来的,主函数并不关心,这样,主函数在一个更高的层次上处理问题,结构就更清晰了。 建议把主函数放到程序最后面,养成这样的习惯既遵守了“先定义后使用”的规则,又容易找到。 子函数中的参数是形式参数(形参只是说明需要一个这种类型的数据,函数才能完成任
9、务。在执行该函数前形参不占用存储单元,也没有实际的值),无所谓用什么标识符。因此注意子函数中的r和主函数里的变量r不是同一个东西。把子函数的参数名改一下,上机试试结果如何 ? 系统提供给我们的函数,如printf等等,也是这样构造的。 可以看出,子函数与主函数的结构是完全一样的。主函数也可以使用参数,主函数由操作系统调用,因此,其参数也是由操作系统传给主函数。如果主函数有返回值,当然也是返回给操作系统。 定义函数的头部非常重要,如“float c(float r)”有三部分:函数的返回值类型、函数名、参数表。定义了函数之后,调用这个函数就如同调用库函数一样。2.2 常量、变量与赋值 直接写在程
10、序代码中的数据的值是常量。不同数据类型的主要差别是取值范围的不同,其实质是每个数据占用的内存空间大小不同。无论哪种数据类型,占用的空间都是有限的。2.2.1 预处理命令【例2-4】球的表面积与体积。#define PI 3.1415926PI可以看成是一个常量标识符2.2.2 变量与内存所谓变量,是与常量相对应的,类似数学知识中的变元。计算机领域里用“内存单元”理解。 一个字节(byte)的内存有8个二进制位(bit),能表示的无符号数为028-1,即0255共256个无符号数。如果把这个字节里的数看成有符号整数,则表示范围为128127,也是256个数C语言规定的数据类型(比如前面用到的in
11、t和float),实际上是告诉编译程序为变量分配多少字节的内存空间,不同大小的内存能表示数的范围也不一样。使用变量就是使用内存。内存里的数据是可以改变的,因此变量的值也是可以改变的。但注意,尽管变量的值可以改变,在某一时刻一个变量只能存一个值,新值覆盖旧值。5x10y10x10y图2.4 执行“x=y”后,x原来的值被覆盖了,y的值不变变量的值也是可以复制的,如“x=y”表示把变量y的值存入变量x中,但y的值不变,这就是复制,如图2.4所示。给变量赋值,实际上就是改变相应内存里的值。如“c=a+b”,就是把a+b的结果放到变量c中去,当然,原来c里的值就被新值所覆盖,如图2.5所示。图2.5
12、执行“c=a+b”后,a、b、c变量的变化5a10b20c5x10y15c+从变量的实质可以看出,赋值运算符的左边必须有存放数据的地方,一般都是变量 【例2-6】交换两个变量的值。【分析】交换两个变量的值的PAD图如图2.6所示,最左边的竖线表示程序执行的流程,矩形框表示执行步骤。由PAD图可以看出各步骤从上往下执行的顺序 图2.6 “交换两数值”的PAD图a=2 t=aa=bb=t b=3输出a,b输出a,b【说明】注意到,一行可以写多个语句,每个语句后面必须有分号。用图来表示内存单元数据的交换过程更直观(如图2.7)。在执行“a = 2; b=3;”后如图2.7(a)。注意此时变量t单元里
13、的值是内存原来的“残留物”,但不知道它的值具体是多少,我们也不关心。图2.7 变量a和b的值交换过程2a3bt(a) 执行“a = 2; b=3;”后2a3b2t(b) 执行“t=a;”后3a3b2t(c) 执行“a=b;”后3a2b2t(d) 执行“b=t;”后 计算机处理数据是一个一个地处理,如图2.7,没有别的捷径,但计算机执行速度非常快。 交换一对变量的值需要三个赋值语句,在应用程序中经常需要用到“交换”操作,一定理解并记熟。 就本题而言,交换两个变量的值,还可以用下面三句实现,可以节省一个变量,请读者琢磨:a = a+b; b=a-b; a=a-b;2.3 表达式表达式用运算符和括号
14、将运算对象连接起来的、符合C语言语法规则的式子。【例2-7】温度的转换:华氏温度转换成摄氏温度。2.3.1 运算符【说明】 阅读教学参考书 * % 关系运算符 = = != = 逻辑运算符 ! & |= 条件运算符: ? :位运算:按位与(&)、或( | )、异或( )、左移()运算都是双目运算(两个操作数), 按位取反()是单目运算。 2.3.2 使用库函数【例2-9】求解一元二次方程的根【分析】一元二次方程ax2+bx+c=0实际上是由系数a,b,c决定的,因此,给出了a,b,c的值就等同于确定了一个一元二次方程。求解一元二次方程的根可用求根公式,只要有了a,b,c的值,不难计算该公式。判
15、别式=b24ac确定了根的多种情况:两个相同的实根、两个不同的实根、一对共轭复数根。须把判别的步骤写在程序中(下面算法第4步)。粗略算法: 步1:输入a,b,c (确定方程) 步2:如果a为0,则输出“不是一元二次方程”,转5 步3:求=b24ac 步4:如果=0,则有相同的两个相等的实根: ; 否则如果0,则有两个不同的实根: ; 否则有一对共轭复数根: 步5:结束 图2.8 “求解一元二次方程的根”的PAD图a=0 =0 输入a,b,c 0 不是一元二次方程两个相等的实根两个实根共轭复根 =b24ac2.3.3 赋值表达式与变量的自增自减x=1+2赋值表达式赋值表达式的值是赋值以后左边变量
16、的值。比如赋值表达式“x=1+2”的值是3赋值语句就是赋值表达式后面加分号。 自增和自减x = x +1写成变量x自增:+x或x+;x = x - 1写成变量x自减:- - x或x - -;自增和自减也是赋值,是变量自己增1或减1后送回自己,因此,自增自减操作只能用在可以赋值的变量上。自增和自减操作只是为了提高程序运行的速度,其实完全可以用赋值来表示。+x是先增后用,即先把x的值增加1以后再把x拿来运算,y=+x; 相当于执行 x=x+1;y=x; x+是先用后增,即先把x的值用作运算以后再把x增加1,y=x+; 相当于执行 y=x; x=x+1; 单独使用自增的语句, +x与x+效果是相同的
17、自减也是一样的道理x = x+5,C语言中可以写成x+=5- =、*=、/=、变量在原来的基础上修改自己的值注意:复合赋值运算符的运算规则与赋值运算符一样:先对右边的表达式求值,再做(复合)赋值运算。复合赋值运算2.4 基本输入输出所有高级语言系统都将输入输出处理成子程序放到函数库中,程序设计者只需调用而不需过多掌握硬件性能。 2.4.1 格式输入输出scanf(按格式扫描scan format)和printf(按指定格式打印print format )格式是指函数中用到的类似%d、%f等以“%”引导的格式字符,是告诉程序以什么样的格式输入或输出。 scanf告诉程序需要输入什么类型的数据,并
18、在键盘输入结束之后敲入回车键 除了规定的格式字符以外的字符,在程序运行时必须原样输入 scanf(%d,as%d, &x, &y)在程序运行时输入:123,as456,则变量x得到123,变量y得到456注意:变量是以变量的地址给出,如&x就是变量x的地址,也就是要求明确指明得到的数据放到内存的什么地方去printf 占用屏幕字符宽度。%d是表示把对应表达式的值以整数形式输出。%3d表示用3个字符的宽度输出。如果数据多于3位(含符号位),则按实际需要使用宽度(宽度是指占用屏幕上的字符个数);如果数据不足3位,则前面用空格补够3个字符宽度(右对齐)。%-4d表示:如果数据不够4位,则在数据后面用
19、空格补够4个字符宽度(左对齐)。%05d表示:如果数据不够5位,则前面用0补够5个字符宽度。讨论:%-06d有意义吗?小数位。%f表示把对应表达式的值以float(浮点)类型输出,%lf(l是字母)输出double型数据。%5.2f表示输出的浮点数总宽度占5个位(含小数点的位置),其中小数部分占2位。如果需要的总宽度超过5,则按实际需要使用宽度。讨论:有%6.3d这样的格式吗?%.0f的格式正确吗?用整数类型计算阶乘15!超界,而用double输出带小数不恰当,用%.0lf(l是字母)输出没有小数部分.【例2-10】用各种格式输出结果。【分析】本例展示输出格式的使用。#include void
20、 main( ) int x=210; double y=1234.5678; printf(十进制输出:%d, %lfn,x,y); printf(加前导0的输出:%05d, %09.2lfn,x,y); printf(十六进制输出(大写字母):%Xn, x); printf(十六进制输出(小写字母):%xn, x); printf(八进制输出:%on, x);【说明】 有些编译器对输出格式%f和%lf不加区别; 八进制输出格式只能用小写字母o; 输入输出格式字符都是以“%”引导的。如果需要在屏幕上看见一个“%”,则在程序中须用“%”。2.4.2 字符输入输出特指一个字符的输入输出字符在计算
21、机里是用代码表示的ASCII 每个字节代表一个字符的编码 汉字用两个字节编码 字符输入函数getchar( ) :键盘输入一个字符,回车以后返回输入的字符给程序;getch( ):在键盘上按一个键,立即将按键的字符返回给程序,但不把这个字符显示在屏幕上。注意:函数getch不是标准库函数,需要conio.h预处理说明。conio.h是控制台(console)输入输出头文件。字符输出函数putchar( ):其参数就是待输出字符的代码【例2-11】键盘输入字符,输出其后面的字符。如输入A 则输出B .【分析】 用一个变量去接收键盘的字符,再把这个变量的值增1然后输出。我们用getch()取键盘字
22、符。【程序】 【说明】 字符类型char取值是-128127,本例也可以用无符号字符型unsigned char。 特殊字符:用引导符 给出一个字符的表示方法,如 n 表示换行字符,其ASCII码是10。注意,字符常量用单引号。符号 用于特殊字符的引导符,如果想在屏幕上看见一个 ,须在程序中用连续两个符号 ,用单引号的 代表字符 。 本例中+c与c+是不同的。 字符的运算实际是用对应的ASCII值,如c=A,变量c的值就是65,+c即c的值变为66,此时用putchar(c)实际输出ASCII值为66的字符(B)。可以理解为A与65相等。 如果接收的字符没有别的用途,可不用变量暂存,但不能用成
23、+getch()。因为自增或自减是赋值运算,只针对变量操作。#include #include void main()putchar(getch()+1);putchar(n);2.5 基本数据类型数据类型的用途: 明确变量的取值范围; 明确变量占用内存空间大小; 编译程序检查变量在各种情形下的使用是否合法。2.5.1 数据在内存中的存储类型标识符VC占用字节数取值范围字符型char1-128 +127无符号字符型unsigned char10255 (ASCII表附录) 短整型short int (简写short)2-32768+32767无符号短整型unsigned short20 655
24、35整型int4-231+231-1无符号整型unsigned int(简写unsigned)40 232-1 (4294967295)长整型long int (简写long)4-231+231-1无符号长整型unsigned long40232-1浮点型float4(3.410-383.41038),有效数字7位双精度型double8(1.710-3081.710308),有效数字15位长双精度型long double10(3.410-49323.4104932),有效数字18位2.5.2 变量的地址、用指针存取数据变量其实就是一片内存空间。一片内存空间实际上由两个条件决定:一是这片空间的起
25、始地址(首地址),二是这片空间的长度(字节数)。 每个存储单元都有一个编号,这个编号就是内存地址。每个变量可能不止占用一个单元,如double型变量占用8个单元,如图2.9. 图2.9 double类型变量占用的空间与首地址变量a首地址&a第一个条件,需要取得变量的首地址,比如变量a的首地址用&a得到(&a到底是多少,我们一般不关心其具体值),在前面使用格式输入函数scanf已经用到过&a;第二个条件由变量的数据类型决定占用空间大小。 地址有一个特别的名称叫指针;专门用于存地址的变量就叫指针变量。例如定义一个指向double数据的指针变量p:double *p指针变量p的值就是某一片内存的首地
26、址,而这片内存空间的长度由类型double决定。如果赋值p=&a,则p的值是变量a的首地址,如图2.10.图2.10 指针变量的值是内存地址&a变量a指针变量p用变量p取得变量a的数据用*p,即*p就是a的值。但注意:指针变量必须指向一个确定的地址才有意义(如上面用了“p=&a”),否则,如果p的值是未知区域的地址,向这片空间存值是危险的,甚至会使得系统崩溃。指针概念是C语言的精华,通过指针直接使用内存的方式使设计程序非常灵活而效率非常高,请读者多理解。更重要的是,指针概念可以打开程序设计的思路。 【例2-12】用指针存取变量的值。【分析】如果用指针变量p存取简单变量a的值,可用p=&a得到变
27、量a的地址,再用*p存或取a的值。【程序】【说明】 变量定义“int a=5, *p”后,变量a有了空间而且赋了初值5(如图2.11(a),但变量p的值不确定(尽管p也有值,但我们不知道它的值到底是多少,而且可能每次运行p的值都不一样)。 语句“p = &a”就明确了p的值,就是a的地址(如图2.11(b)。从此,只要不改变 p的值,p指向的地方就是变量a的空间,所以“*p = 6”就等价于“a=6”。 图2.11 变量与指针的关系p5a (b) 执行“p=&a”后5a (a) 执行“int a=5”后 a的首地址到底是多少呢?一般情况下,我们并不关心变量的具体地址。但经过编译链接以后,程序运
28、行时操作系统会分配给变量一个恰当的空间,也就有了地址。可由printf(%p, &a)得到变量a的地址,或 “p = &a”后,由printf(%p,p)得到p的值,即变量a的地址。 指针变量p也是变量,当然p也有地址,p的地址就是指向指针的指针,因为p的空间里装的是地址(指针)。 简单理解,指针就是地址。2.5.3 表达式混合运算的数据类型转换表达式混合运算的数据类型转换:向占用空间大的数据类型转换;如果有占用空间相同的两种类型则向实数类型(浮点型、双精度型、长双精度型)转换,这样不至于丢掉小数部分。数据类型的转换有以下几种: 自然转换。当各种类型的数据混合使用在表达式中,最后的结果是其中占
29、用空间最多的那种数据类型,以保证精度不降低。如果有实数类型,最后结果是占用空间最多的实数类型。 赋值转换。不管赋值语句的右端是什么类型,最终以左边变量的类型为准。 强行转换。如(double) 5/9*(F-32)相当于5.0/9*(F-32),但(double) (5/9)*(F-32)相当于0.0*(F-32)。强行转换后结果的类型更加清晰明了。 本章小结 C程序的结构。C程序是由一些函数组成的,其中主函数是程序的入口. 变量与内存的关系。变量都有确定的内存,由首地址与长度决定了一片内存空间的用途,空间的大小由变量的类型决定。 变量的值可以被修改、也可以被复制。 运算符与表达式。算术运算符
30、:加(+)、减(-)、乘(*)、除(/)、取余数(%)。关系运算符:小于()、小于等于()、大于等于(=)、不等(!=)。关系表达式的结果:关系成立为1,关系不成立为0。逻辑运算符:与(&)、或(|)、非( ! )。逻辑值:逻辑表达式结果真为1、假为0;一般数据参加逻辑运算时,用非0表示真、用0表示假。逻辑运算的短路:一个逻辑表达式从左向右,当整个表达式的值已经能确定时就不再往下计算。例如:逻辑表达式“(max=a)10 | (min=b)10”确定为真了,就不再计算“(min=b)0”的真假,因此,就不可能把b的值赋给变量min,也就是说变量min并没有得到b的值。C语言的条件表达式的一般格
31、式为:逻辑表达式 ? 表达式1:表达式2条件表达式的取值:如果逻辑表达式为真,则取表达式1的值,否则取表达式2的值。 自增自减。对变量自己增1或减1,其实也是特殊的赋值。复合运算:+=、-=、*=、/=、%=、&=、|=、=、=。 语句:表达式后面添分号。空语句(只有分号)、复合语句(括号“”与“”之间的语句合成复合语句)。 算术表达式的类型转换。向占用空间最大的数据类型转换;如果占用空间相同的两种类型运算,则向实数类型(浮点型、双精度型、长双精度型)转换。 格式输入输出函数。scanf与printf函数需要格式字符确定输入或输出的数据格式。用%号引导格式字符。 字符输入输出函数。getchar, getch, putchar. 字符串的输入输出函数。gets, puts. 常用基本数据类型