第一章 C语言程序设计初步
一、主要知识点
(一) 程序设计语言的发展
1、机器语言 2、汇编语言 3、面向过程的语言 4、面向对象的程序设计语言
(二) C程序设计语言的执行过程
1、编辑 2、编译 3、连接 4、执行 (三) 用库函数组装C程序 例1:计算2.1715的正弦值。 #include “math.h” main( ) {
float a; /*定义a为实型变量*/ a=sin(2.1715); /*调用sin函数*/
printf(“%f\\n”,a); /*调用printf函数,输出a的值*/ }
程序详解:
1、在本例中用到的sin函数称为数学函数,它是为了用户使用方便,由一批厂家开发编写的函数,并不是C语言的一部分。在使用数学函数时,往往要用到函数执行时所需的一些信息(例如宏定义),这些信息包含在“math.h”中。因此在程序开头用#include “math.h”将有关的标头文件包括到程序中。
2、一个C语言源程序文件由一个或多个函数组成,C语言的基本组成单位是函数。一个完整的C语言程序有且只有一个称为主函数的main函数,程序总是从main函数开始执行,调用其它函数后再回到main函数,在main函数中结束整个程序的运行。
3、main是函数名称,没有参数可以不写,但圆括号不能省略,main()后面有一对花括号,花括号内的部分称为函数体,main函数可以在程序的任意位置。
4、C规定每个语句以分号(;)结束,分号是语句不可缺少的组成部分,每
行中可以写多条语句。
C语言程序设计详解 2
5、/*与*/之间为注释信息,对程序运行结果不发生影响,也不被编译,注释说明可以放在语句的任意位置。
6、float a;此语句是把a定义为一个实型变量。
7、C语言本身没有输入、输出语句,本例使用prinft函数输出数据。prinft函数的括号内包括两部分内容:双引号内的部分、双引号外的部分。双引号内的部分是“格式字符串”,用于指定输出双引号外的变量的打印格式。此例中双引号中有一个”%f”,它是输出一个保留小数点后6位数字的格式字符,小数点前的位数不指定。
二、经典例题详解
例2:一个C程序的执行是从____。
(A)本程序的main函数开始,到main函数结束
(B)本程序文件的第一个函数开始,到本程序文件的最后一个函数结束 (C)本程序的main函数开始,到本程序文件的最后一个函数结束 (D)本程序文件的第一个函数开始,到本程序main函数结束 答案:C
详解:一个C语言源程序文件由一个或多个函数组成。一个完整的C语
言程序有且只有一个称为主函数的main函数,程序总是从main函数开始执行,调用其它函数后再回到main函数,在main函数中结束整个程序的运行。
例3、以下叙述不正确的是____。
(A)一个C源程序可由一个或多个函数组成 (B)一个C源程序必须包含一个main函数 (C)C程序的基本组成单位是函数
(D)在C程序中,注释说明只能位于一条语句后面 答案:D
详解:/*与*/之间为注释信息,对程序运行结果不发生影响,也不被编译,
注释说明可以放在语句的任意位置。
三、习题 (一) 选择题
1、以下叙述正确的是____。
C语言程序设计详解 3
(A)在C程序中,main函数必须位于程序的最前面 (B)C程序的每行中只能写一条语句 (C)C语言本身没有输入输出语句
(D)在对一个C程序进行编译的过程中,可发现注释中的拼写错误 2、C语言规定:在一个源程序中,main函数的位置____。 (A)必须在最开始
(B)必须在系统调用库函数的后面 (C)可以任意 (D)必须在最后
3、一个C语言程序是由____。 (A)一个主程序和若干子程序组成 (B)函数组成 (C)若干过程组成 (D)若干子程序组成 (二) 填空题:
1、一个C源程序中至少包括一个________。
2、在一个C源程序中,注释部分两侧的分界符分别为________和
________。
第二章 数据描述与基本操作
一、主要知识点 (一) C的基本数据类型
整型 基本类型 字符型
实型(浮点型) 单精度型 枚举类型 双精度型
数据类型 构造类型
指针类型
空类型
C语言程序设计详解 4
(二) 常量和符号常量
1、常量定义:在程序运行过程中,其值不能被改变的量称为常量。常量常区分不同的类型,如12、0、-3为整型常量,’a’、’D’为字符常量。
2、符号常量:用一个标识符代表一个常量的,称为符号常量,即标识符形式的常量。常量不同于变量,它的值在作用域内不能改变,也不能再被赋值。
例1:已知商品的单价及数量求商品的总价值。 #define PRICE 30 main( ) {
int num,total; num=10;
total=num*PRICE; printf(“total=%d”,total); }
输出结果:total=300 程序详解:
1、程序中用#define命令行定义PRICE代表常量30,此后凡在此文件中出现的PRICE都代表30,可以和常量一样进行运算。
2、符号常量不同于变量,它的值在其作用域内不能改变,也不能再被赋值。如再用以下赋值语句给PRICE赋值:PRICE=40;是错误的。
(三) 变量
1、变量定义:其值可以改变的量称为变量。 2、标识符的命名规范
和其它高级语言一样,用来标识变量名、符号常量名、函数名、数组名、类型名、文件名的有效字符序列称为标识符,C语言中的标识符命名规范为:
①变量名只能由字母、数字和下划线三种字符组成,且第一个字符必须为字母或下划表。
②C语言中标识符的长度(字符个数)无统一规定,随系统而不同。许多系统(如IBM PC的MS C)取8个字符,假如程序中出现的变量名长度大于8个
C语言程序设计详解 5
字符,则只有前面8个字符有效,后面的不被识别。
③C语言有32个关键字(例如if、else、while)它们已有专门含义,不应用采用与它们同名的变量名。
④C语言将大写字母和小写字母认为是两个不同字。
例2:在下列符号中,可以选用哪些作变量名?哪些不可以?
a3B 3aB ∏ +a -b *x $ _b5_ if next_ day e_2 OK? Integer MAXNUMBER i*j
答案:_b5_ a3B next_ day e_2 MAXNUMBER可作变量名,其它的作变量名不可以。
详解:
①MAXNUMBER可作变量名。习惯上符号常量名用大写,变量名用小写以示区别,但大写字母作变量名并无错误。
②if、integer属于保留字,保留字不可作变量名。
③∏ +a -b *x $ OK? i*j不可作变量名,因为变量名只能由字母、数字和下划线三种字符组成。
④3aB不可作变量名,因为变量名的第一个字母必须为字母或下划线。 (四) 整型数据 1、整型常量
整型常量即整常数。C语言整常数可用以下三种表示形式。 ①十进制表示。如231、-56.478 ②八进制表示。以
8=1*8
0开头的数是八进制数。如
0123即(123)
2+2*81+3*80=64+16+3=83。
③十六进制表示。以0x开头的数是16进制。如0x123即(123)
16=1*16
2
+2*161+3*160=256+32+3=291。
2、整型变量
整型变量分为:基本型、短整型、长整型、和无符号型四种。 ①基本型,以int表示
②短整型,以short int表示或以short表示 ③长整型,以long int表示,或以long表示
④无符号型,存储单元中全部二进制位(bit)用作存放数本身,而不包括符号。
C语言程序设计详解 6
无符号型中又分为无符号整型、开符号短整型和无符号长整型,分别以unsigned int 、unsigned short 和unsigned long表示。
3、整型数据的取值范围
C标准没有具体规定各类型所占内存字节数,各种机器处理上有所不同,以IBM PC为例,数的范围如表2.1所示。表2.1
Int short[int] Long[int] Unsigned[int] Unsigned short unsigned long 所占位数 16 16 32 16 16 32 数的范围 -32768~32767 即-215~(215-1) -32768~32767 即-215~(215-1) -2147483648~2147483647 即-231~(231-1) 0~65535 即0~(216-1) 0~65535 即0~(216-1) 0~4294967295 即0~(232-1) 4、整型常量的分类
①一个整常量,如果其值在-32768~32767范围内,认为它是int型,它可以赋值给int型和long int型变量。
②一个整常量,如果其值超过了上述范围,而在-2147483648~2147483647范围内,则认为它是long int型,可以将它赋值给一个long int型变量。
③如果某一计算机系统的C版本确定的short int与int型在内存中占据的长度相同,则一个int型的常量出同时一个short int型常量。
④常量中无unsigned型。但一个非负值的整常量可以赋值给unsigned型整变量,只要它的范围不超过变量的取值范围即可。例如:将50000赋给一个unsigned int型变量是可以的,而将70000赋给它是不行的(溢出)。
⑤在一个整常量后面加一个字母l或L,则认为是long int型常量。 (五) 实型数据 1、实型常量
实数在C语言中又称为浮点数。实数有两种表示形式:
①十进制形式。它由数字和小数点组成(注意必须有小数点)。例如:0.123、.123、123.0、0.0都是十进制数形式。
②指数形式。如123.56e4或123.56E4都代表123.56*104。但字母e(或E)之前必须有数字,e后面指数必须为整数。例如:e3、2.1e3.5、.e3、e等都不是
合法的指数形式。
C语言程序设计详解 7
例3:下面四个选项中,均是不合法的浮点数的选项是_____。 (A)160. 0.12 e3 (B)123 2e4.2 .e5 (C)-.18 123e4 0.0 (D)-.e3 .234 1e3 答案:B 详解:
①160. 0.12 -.18 123e4 0.0 .234 1e3是实数的十进制形式或指数形式。
②e3 2e4.2 .e5 -.e3不是正确的指数形式。因为正确的字母e(或E)之前必须有数字,e后面指数必须为整数。对于数据表示形式.e5以及-.e3,e前的.与-.不是有效的数字表示形式。
③123是整数形式。 2、实型变量
C实型变量分为单精度(float型)和双精度(double型)两类。
在一般系统中,一个单精度型数据在内存中占4个字节(32位),一个double型数据占8个字节。单精度实数提供7位有效数字,数值的范围随机器系统而异。在IBM PC中,单精度实数的范围约为10-38~1038,双精度实数的范围约为10-308~10308。
例4: main( ) { float a;
a=111111.666666; printf(“%f”,a); }
输出结果:111111.640621 程序详解:
①一个实型常量不分float型和double型。一个实型常量可以赋给一个float型或double型变量。根据变量的类型截取实型常量中相应的有效位数字。
②由于float型变量只能接收7位有效数字,因此在把111111.666666赋给a时,a只接收了111111.6,由于输出函数printf中的%f格式表示输出小数点后的
C语言程序设计详解 8
6位小数,所以111111.6后的40621属于无意义数字。
③如果a改为double型,则能全部接收上述12位数字。 (六) 字符型数据 1、字符常量:
①普通形式的字符常量:用引号(即撇号)括起来的一个字符,如’a’、’D’、’$’、’?’等都是字符常量。
②转义符:以“\\”开头的字符序列。 常用的以“\\”开头的特殊字符见表2.2 表2.2
字符形式 \\n \ \\v \\b \\r \\f \\\\ \\’ \\ddd \\xhh 功能 换行 横向跳格 竖向跳格 退格 回车 走纸换页 反斜杠字符 单引号字符 1到3位8进进制所代表的字符 1到2位16进制数所代表的字符 例5:若有说明语句:char c=’\\729’;则变量c_____。 (A)包含1个字符 (B)包含2个字符 (C)包含3个字符 (D)说明不合法 答案:D
详解:”\\”后可以有1到3位8进制所代表的字符,本题中”\\”后的”72”属于8进制所代表的字符,而”9”则不属于8进制位所代表的字符,则’\\729’中包含了两个字符常量’\\72’和’9’。而字符常量是用引号(即撇号)括起来的一个字符,所以答案为D。
2、字符变量
字符变量是用来存放字符常量。注意只能存放一个字符。 3、字符数据在内存中的存储形式
C语言程序设计详解 9
将一个字符常量存放到一个字符变量中,实际上并不是把该字符本身存放到内存单元中去,而是将该字符的相应的ASCII码值存放到存储单元中去。
例6:将小写字母转换成大写字母 main( ) { char c1; c1=’a’; c1=c1-32; printf(“%c”,c1); }
输出结果:A 程序详解:
①’a’的ASCII码为97,所以c1=’a’;语句的功能是把97赋值给了c1。 ②c1=c1-32;语句的功能是把97-32的值65赋值给c1。
③printf函数中的%c格式表示以字符方式输出。ASCII码值为65的字符为A,所以运行结果为:A
(七) 字符串常量
字符常量是用一对双引号括起来的零个或多个字符序列。C规定以字符’\\0’作为字符串结束标志。所以字符串”a”实际上包含2个字符:’a’、’\\0’,因此,把它赋值给一个字符变量c:
c=”a”
是错误的。
例7:下面不正确的字符串常量是______。 (A)’abc’ (B)”12’12” (C)”0” (D)” ” 答案:A
详解:’abc’是用单引号引来的,所以’abc’不是正确的字符串常量。 (八) 算术运算符和算术表达式 1、基本算术运算符
①C语言中有5个基本算术运算符, +(加法运算符。如3+5、+3) -(减法运算符。如5-2、-3)
C语言程序设计详解 10
*(乘法运算符,如3*5) /(除法运算符,如5/3,5.0/3)
%(求余运算符,要求%两侧均为整型数据)
例8:在C语言中,要求运算数必须是整型的运算符是____。 (A)/ (B)++ (C)!= (D)% 答案:D
详解:对于%运算符来说,要求两侧均为整型数据,所以表达式3.5%2与3%2.0是错误的。
例9:写出下列程序的输出结果 main( )
{ printf(“%d,%d\\n”,5/3,5%3); printf(“%d,%d\\n”,-5/-3,-5%-3); printf(“%d,%d\\n”,-5/3,-5%3); printf(“%d,%d\\n”,5/-3,5%-3); }
输出结果: 1,2 1,-2 -1,-2 -1,2
程序详解:两个同号整数相除时结果为正整数,如5/3、-5/-3的结果值为1。两个异号整数相除时结果为负整数,多数机器采取“向零取整”法,即-5/-3=-1,5/-3=-1,但如果参加运算的两个数中有一个数为实数时结果为实数。对于求余(%)运算,运算结果与第一个数的符号相同。
②优先级别:先*、/、%后+、-
③运算量:双元运算量,%前后必须为整数。 ④左右结合性:自左至右参预运算。 2、自加自减运算符
①C语言中有4种形式的自加自减运算符: ++i (先使i加1后使用)
C语言程序设计详解 11
i++ (先使用后使i加1) --i (先使i减1后使用) i-- (先使用后使i减1) ②优先级别:高于算术运算。
例10:若x和n均是int型变量,且x和n的初值均为5,则计算表达式后x的值为______,n的值为______。
x+=n++ 答案:10 6
详解:根据优先级别选运算表达式n++,因为n++是后缀表示形式,所以n先参预运算,再运算表达式x+=n,则x为10,最后n自加为6。
例11: main( ) { int x,y,m,n;
x=2;y=2 m=x++*5; n=++y*5;
printf(“%d,%d,%d,%d”,x,y,m,n); }
输出结果:3,3,10,15
程序详解:对于后缀来说是先使用后运算,所以m的值为x在自加以前的2*5得10赋值给m后,x自加变为3。对于前缀来说是先运算后使用,所以m的值为x在自加以后的3*5得15赋值给n。
③运算量:单元运算量,此运算量必须为变量,所以表达式5++、(x+y)++是错误的。
④左右结合性:自右至左参预运算。 (九) 关系运算
1、C语言提供的关系运算符有:
>(大于) >=(大于或等于) <小于) <=(小于或等于) ==(等于)是 !=(不等于)
2、优先级别:关系运算符的优先级别低于纯算术类,高于值类。后两个的
C语言程序设计详解 12
优先级小于前四个。如表达式a+b ①关系表达式的值是整型数0或1,故也可以将其看成一种整型表达式。例如: int i=1,j=7,a; a=i+(j%4!=0); a的值为2。 ②要说明x在区间[a,b]中,普通数学中使用表达式a≤x≥b。但C语言中应写成a<=x&&x<=b。 ③表达式5>2>7>8在数学上是不允许的,而在C中是允许的。按自左至右求解。 ④字符数据的比较按其ASCII码值进行。 ⑤在判定两个浮点数是否相等时,由于存储上的误差,会得出错误的结果。例如: 1.0/3.0*3.0==1.0 (十) 逻辑运算 1、C语言提供的逻辑运算符: &&(逻辑与) ||(逻辑或) !(逻辑非) 2、优先级别:&&与||的优先级别低于关系运算符,高于条件运算符,&& 的优先级别高于||,!的优先级别与自加运算符(++)、自减运算符(--)同级。 3、运算量:&&和||是双元运算符,!是单元运算符。 例12:已知x=43,ch=’A’,y=0;则表达(x>=y&&ch<’B’&&!y)的值是______。 (A)0 (B)语法错 (C)1 (D)“真” 答案:C 说明:C语言不提供逻辑性数据“真”和“假”,在进行逻辑运算时则把 “非零”作为“真”,把0作为“假”,所以表达式!y的运算结果是1。 4、左右结合性:&&和||运算符的结合方向为自左至右,!的结合方向为自 右至左。 5、关于逻辑运算符的进一步说明: C语言程序设计详解 13 ①在一个&&表达式中,若&&的一端为0,则不必再计算另一端,该表达式 的值肯定为0。 ②在一个||表达式中,若||的一端为0,则不必再计算另一端,该表达式的值 肯定为1。 例13:写出下面程序的输出结果。 main( ) { int x,y,z; x=y=z=0; ++x&&++y||++z; printf(“%d,%d,%d”,x,y,z); x=y=z=0; ++x||++y&&++z; printf(“%d,%d,%d”,x,y,z); } 输出结果: 1,1,0 1,0,0 程序说明: ①因为&&的优先级别高于||,所以表达式++x&&++y||++z是一个或表达 式,根据||的一端为0,则不必再计算另一端的原则,先计算表达式++x&&++y的值为1,因为1或任何值都为1,所以表达式++z没有运算,输出结果为:1,1,0。 ②表达式++x||++y&&++z也是一个或表达式,同样根据||的一端为0,则不 必再计算另一端的原则,先计算表达式++z的值为1,因为1或任何值都为1,所以表达式++y&&++z没有运算,输出结果为:1,0,0。 (十一) 赋值运算 1、基本的赋值运算符: =(将赋值运算符右侧的表达式赋给左侧的变量) 2、自反算术赋值运算符 C语言中有5个基本自反算术赋值运算符, C语言程序设计详解 14 +=(a+=3 等价于a=a+3) -=(a-=3 等价于a=a-3) *=(a*=3 等价于a=a*3) /=(a*=3 等价于a=a*3) %=(a%=3 等价于a=a%3) 3、优先级别:赋值运算符与自反算术赋值运算符属于同等级别,低于条件运算符,高于逗号运算符。如对于表达式x%=y+3完全等价于x%=(y+3)。 例14:若有以下定义,则能使值为3的表达式是______。 Int k=7,x=12; (A)x%=(k%5) (B)x%=(k-k%5) (C)x%=k-k%5 (D)(x%=k)-(k%=5) 答案:D 详解:表达式(x%=k)-(k%=5)完全等价于(x=x%k)-(k=k%5)等价于5-2,此表达式的结果为3。 4、运算量:双元运算量,赋值运算符与自反算术赋值运算的第一个量必须为变量,且%=前后必须为整型数据。如对于表达式a*3+=2是错误的。因为此表达式完全等价于(a*3)=(a*3)+2。 5、左右结合性:自右至左参预运算。 例15:若a是int型变量,且a的初值为6,则计算表达式后a的值为______。 a+=a-=a*a 答案:-60 详解:表达式从左向右运算,先计算表达式a=a-36后a为-30,再计算表达式a=a+a后 a的值变为-60。 (十二) 条件运算: 1、条件运算符的基本形式及功能: 条件运算是一种在两个表达式的值中选择一个的操作。它的一般形式为: e1?e2:e3 它的操作过程为,若e1为真,则表达式的值为e2,若为假表达式的值为e3。 2、优先级别:低于逻辑运算,高于赋值运算。 3、运算量:三元运算量,e1一般为算术表达式,e2、e3可以是任意类型的 C语言程序设计详解 15 表达式,条件表达式的值的类型为e2与e3二者中类型较高的。 例16:若有条件表达式(exp)?a++:b--,则以下表达式中能完全等价于表达式(exp)的是______。 答案:exp!=0 说明:对于表达式e1?e2:e3 ,e1一般为算术表达式、逻辑表达式、关系表达式,结果为1(真)或0(假)。也可以为数值exp,结果为结果为非0(真)或0(假)在本例中与exp完全等价的表达式是exp!=0。 4、左右结合性:自右至左运算。 例17:以下程序的运行结果是______。 main() { int k=4,a=3,b=2,c=1; printf(“%d”,k程序解析:条件表达式是从右向左运算,所以在本例中先计算表达式c(十三) 逗号运算 1、逗号运算符的基本形式及功能: 逗号表达式的一般形式为: 表达式1,表达式2。逗号表达式的求解过程是:先求解表达式1,再求解表达式2。整个表达式的值是表达式2的值。 2、优先级别:逗号运算符是所有运算符中级别最低的。 例18:以下符合C语言语法的赋值表达式是______。 (A)d=9+e+f=d+9 (B)d=(9+e,f=d+9) (C)d=9+e,e++,d+9 (D)d=9+e++=d+7 答案:B 解析:表达式d=9+e+f=d+9中9+e+f=d+9是不正确的表示形式,因为赋值号(=)左边不能是表达式。表达式d=9+e,e++,d+9是逗号表达式,因为赋值运算符(=)的优先级别高于逗号运算符(,)。表达式d=9+e++=d+7中9+e++=d+7是不正确的表达式,因为赋值号(=)左边不能是表达式。 C语言程序设计详解 16 3、运算量:二元运算量。 4、左右结合性:从左向右运算。 例19:假设所有变量均为整型,则表达式 a=2,b=5,b++,a+b的值是______。 答案:8 解析:根据逗号运算符从左向右运算的原则,首先把2和5分别赋值给了a,b。再计算表达式b++,b变为6,再计算表达式a+b的值,最后整个表达式的值是是8。 (十四) 强制类型转换 1、强制类型一般形式及功能: 可以利用强制类型转换运算符将一个表达式转换成所需类型。例如:(double) a(将a转换成double类型)其一般形式为: (类型名)(表达式) 2、优先级别:强制类型转换运算符与逻辑非(!)、自加(++)、自减(--)属于同等级别,高于算术运算符。 3、运算量:单元运算量 4、关于强制类型转换运算符的进一步说明: ①强制转换表达式时,表达式应该用括号括起来。如果写成(int )x+y则只将x转换成整型,然后与y相加。 ②如果x原指定为float型,进行强制类型运算后得到一个int型的中间变量,它的值等于x的整数部分(截去小数部分),而x的类型不变(仍为float型)。 例20: main( ) {float x; int i; x=3.6; i=(int)x printf(“x=%f,i=%d”,x,i); } 运算结果:x=3.600000,I=3 C语言程序设计详解 17 程序解析:变量x进行强制类型运算后,其类型仍为float型,值仍为3.6。 (十五) printf函数 printf函数可以输出任意类型的多个数据。 1、printf函数的一般格式 printf(格式控制,输出表列) ①“格式控制”是用双引号括起来的字符串,也称“转换控制字符串”,它包括两种信息:格式说明和普通字符。格式说明是由“%”和格式字符组成,如%d,%f等。它的作用是将输出的数据转换为指定的格式输出。格式说明总是由”%”字符开始的。普通字符即需要原样输出的字符。 ②输出表列是指需要输出的一些数据,可以是表达式,它们之间用“,”隔开。 2、格式控制的完整格式: % - 0 m.n l或h 格式字符 下面对组成格式说明的各项加以说明: ①%:表示格式说明的起始符号,不可缺少。 ②-:有-表示左对齐输出,如省略表示右对齐输出。 ③0:有0表示指定空位填0,如省略表示指定空位不填。 ④m.n:m指域宽,即对应的输出项在输出设备上所占的字符数。N指精度。用于说明输出的实型数的小数位数。为指定n时,隐含的精度为n=6位。 ⑤l或h:l对整型指long型,对实型指double型。h用于将整型的格式字符修正为short型。 3、格式字符: 格式字符用以指定输出项的数据类型和输出格式。 ①d格式:用来输出十进制整数。有以下几种用法: %d:按整型数据的实际长度输出。 %md:m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以 空格,若大于m,则按实际位数输出。 %ld:输出长整型数据。 ②o格式:以无符号八进制形式输出整数。对长整型可以用“%lo”格式输出。同样也可以指定字段宽度用“%mo”格式输出。 例21: main() { int a=-1; C语言程序设计详解 18 printf(“%d,%o”,a,a); } 运行结果:-1,177777 程序解析:-1在内存单元中(以补码形式存放)为(1111111111111111)2,转换为八进制数为(177777)8。 ③x格式:以无符号十六进制形式输出整数。对长整型可以用“%lx”格式输出。同样也可以指定字段宽度用“%mx”格式输出。 ④u格式:以无符号十进制形式输出整数。对长整型可以用“%lu”格式输出。同样也可以指定字段宽度用“%mu”格式输出。 ⑤c格式:输出一个字符。 ⑥s格式:用来输出一个串。有几中用法 %s:例如:printf(“%s”,”CHINA”)输出“CHINA”字符串(不包括双引号)。 %ms:输出的字符串占m列,如字符串本身长度大于m,则突破获m的 限制,将字符串全部输出。若串长小于m,则左补空格。 %-ms:如果串长小于m,则在m列范围内,字符串向左靠,右补空格。 %m.ns:输出占m列,但只取字符串中左端n个字符。这n个字符输出在 m列的右侧,左补空格。 %-m.ns:其中m、n含义同上,n个字符输出在m列范围的左侧,右补空 格。如果n>m,则自动取n值,即保证n个字符正常输出。 ⑦f格式:用来输出实数(包括单、双精度),以小数形式输出。有以下几种用法: %f:不指定宽度,整数部分全部输出并输出6位小数。 %m.nf:输出共占m列,其中有n位小数,如数值宽度小于m左端补空 格。 %-m.nf:输出共占n列,其中有n位小数,如数值宽度小于m右端补空格。 ⑧e格式:以指数形式输出实数。可用以下形式: %e:数字部分(又称尾数)输出6位小数,指数部分占5位或4位。 C语言程序设计详解 19 %m.ne和%-m.ne:m、n和”-”字符含义与前相同。此处n指数据的数字部 分的小数位数,m表示整个输出数据所占的宽度。 ⑨g格式:自动选f格式或e格式中较短的一种输出,且不输出无意义的零。 4、关于printf函数的进一步说明: 如果想输出字符“%”,则应该在“格式控制”字符串中用连续两个%表示,如: printf(“%f%%”,1.0/3); 输出0.333333%。 (十六) scanf函数 scanf函数可以用来输入任何类型的多个数据。 1、scanf函数的一般格式 scanf(格式控制,地址表列) ①“格式控制”的含义同printf函数。普通字符即需要原样输入的字符。 ②地址表列是指由若干个地址组成的表列,它们之间用“,”隔开。 2、格式控制的完整格式: % * m l或h 格式字符 ①格式字符与printf函数中的使用方式相同,以%d、%o、%x、%c、%s、%f、%e,无%u格式、%g格式。 ②可以指定输入数据所占列宽,系统自动按它截取所需数据。如: scanf(“%3d%3d”,&a,&b); 输入:123456 系统自动将123赋给a,456赋给b。 ③%后的“*”附加说明符,用来表示跳过它相应的数据。例如: scanf(“%2d%*3d%2d”,&a,&b); 如果输入如下信息:1234567。将12赋给a,67赋给b。第二个数据”345”被跳过不赋给任何变量。 ④输入数据时不能规定精度,例如: scanf(“%7.2f”,&a); 是不合法的,不能企图输入:12345.67而使a的值为12345.67。 3、输入数据流分隔 C语言程序设计详解 20 ①根据格式字符的含义从输入流中取得数据,当输入流中数据类型与格式字符要求不符时,就认为这一项结束。如: scanf(“%d%c%f”,&a,&b,&c); 如果输入如下信息: 1234r1234.567 则scanf函数在接收数据时发现”r”类型不匹配,于是把”1234”转换成整型赋值给a,把”r”赋给变量b,最后把”1234.567”转换成实型数据赋给c。 ②根据格式项中指定的域宽分隔出数据项。如语句:scanf(“%2d%3f%4f”,&a,&b,&c); 如果输入如下信息: 123456789012345 则scanf函数在接收数据时根据域宽把12赋值给a,345赋值给b,6789赋值给c。 ③隐示分隔符。空格、跳格符(’\’)、换行符(’\\n’)都是C语言认定的数据分隔符。 ④显示分隔符。在scanf函数的两个格式说明项间有一个或多个普通字符,那么在输入数据时,在两个数据之间也必须以这一个或多个字符分隔。如语句: scanf(“a=%d,b=%f,c=%f”,&a,&b,&c); 则输入数据应该为: a=1234,b=67.8,c=98.123 4、关于scanf函数的进一步说明: ①scanf函数中的“格式控制”后面应当是变量地址,而不应是变量名。 例如,如果a、b为整型变量,则 scanf(“%d,%d”,a,b); 是不对的,应将”a,b”改为”&a,&b”。 ②如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输 入数据时应输入与这些字符相同的字符。例如: scanf(“%d,%d”,&a,&b); 输入时应输入:3,4。3与4之间的逗号应与scanf函数中的“格式控制”中的逗号相对应,输入其它符号是不对的。 C语言程序设计详解 21 ③在用“%c”格式输入字符时,空格字符和转义字符都作为有效字符输入。 scanf(“%c%c%c”,&c1,&c2,&c3); 如输入:a b c 。字符’a’赋给c1,字符(空格)’ ’赋给c2,字符’b’ 赋给c3。 5、scanf的停止与返回 ①格式参数中的格式项用法----正常结束。 ②发生格式项与输入域不匹配时----不正常退出: (十七) getchar、putchar函数 1、getchar函数 getchar函数是从终端输入一个字符。getchar函数没有参数,其一般形式为:getchar( )。 2、putchar函数 putchar函数的作用是向终端输出一个字符。 三、经典例题解析 例22: 四、练习 1、下面四个选项中,均是不合法的用户标识符的选项是____。C (A)A P_0 do (B)float la0 _a (C)b-a goto int (D)_123 temp INT 2、若x,i,j和k都是int型变量,则计算下面表达式后,x的值为______。x=(i=4,j=16,k=32)C (A)4 (B)16 (C)32 (D)52 3、下列四个选项中,均是不合法的整型常量的选项是______。D (A)--0f1 -0xffff 0011 (B)-0xcdf 017 12,3456 (C)-018 999 5e2 (D)-0x48eg -068 03f 4、下面四个选项中,均是合法浮点数的选项是______。 (A)1e1 5e-9.4 03e2 (B)-.60 12e-4 -8e5 (C)123e 1.2e-.4 2e-1 C语言程序设计详解 22 (D)-e3 .8e-4 5.e-0 B 5、下面四个选项中,均是合法字符的选项是______。A (A)’\\’’ ‘\\\\’ ‘\\xf’ (B)’\\’ ‘\\017’ ‘\\n’ (C)’\\018’ ‘\\f’ ‘xab’(D)’\\0’ ‘\\101’ ‘xlf’ 6、以下不正确的叙述是______。D (A)在C程序中,逗号运算符的优先级最低。 (B)在C程序中,APH和aph是两个不同的变量 (C)若a和b类型相同,在计算机了赋值表达式a=b后,b的值不变。 (D)当从键盘输入数据时,对于整型变量只能输入整型数值,对于实型变量只能输入实型数据。 C语言程序设计详解 23 第三章 C程序的流程设计 一、算法 1、算法的概念 为解决某一个问题而采取的方法和步骤,就称为算法。 2、算法的性质 ①有穷性:一个算法应包含有限的操作步骤 ②一个初始:此动作序列只有一个初始动作 ③确定性:算法中的每一个步骤都应当是确定性的,仅有一个后继动作。 ④有一个或多个输出:序列终止表示问题得到解答或问题没有解答,没有输出的算法是没有意义的。 二、选择型程序设计 1、if 语句的形式 ①if(条件表达式) 语句 ②if(条件表达式) 语句1 else 语句 2 注意: ①if语句中的条件表达式一般为逻辑表达式或关系表达式,但也可以是任意的数值类型(包括整型、实型、字符型、指针数据)例如下列语句也是合法的。 if(‘a’) printf(“%d” ,’a’); ②在if语句中,分号是语句的结束标志。 ③在if和else后面可以只含一个内嵌的操作语句,也可以有多个操作语句,此时用花括号将几个语句括起来成为一个复合语句。 例1:以下不正确的if语句形式是( )。 A)if(x>y&&x!=y); B)if(x==y) x+=y; C)if(x!=y) scanf(“%d”,&x) else scanf(“%d”,&y); D)if(x 答案解析:scanf(“%d”,&x)末尾应加分号,因为分号是语句不可缺少的部分。 例2:已知int x=10,y=20,z=30;以下语句执行后x,y,z的值是( )。 If(x>y) z=x;x=y;y=z; 答案:x,y,z的值分别是:20 30 30 答案解析:在此语句中,条件x>y为假,所以只执行x=y;y=z;两条语句。 例3、以下不正确的语句是( ) A)if(x>y); B)if(x=y)&&(x!=0) x+=y; C)if(x!=y) scanf(“%d”,&x); D)if(x if(条件1) if(条件2) 语句1 else 语句2 else if(条件3) 语句3 else 语句4 例4:以下程序的输出结果是( ) main( ) { int a=100,x=10,y=20,ok1=5,ok2=0; if(x if (!ok1) a=1; else if(ok2) a=10; printf(“%d\\n”,a); } 答案:100 C语言程序设计详解 25 例5:请阅读以下程序:若运行时输入2.0<回车>,则上面程序的输出结果是( ) #include float a,b; scanf(“%f”,&a); if(a<0.0) b=0.0; else if((a<0.5) &&(a!=2.0) b=1.0/(a+2.0); else if(a<10.0) b=1.0/a; else b=10.0; printf(“%f\\n”,b); } 答案:0.500000 例6:请阅读下面的程序: main( ) { int s,t,a,b; scanf(“%d,%d”,&a,&b); s=1; t=1; if(a>0) s=s+1; if(a>b) t=s+t; else if(a==b) t=5; else t=2*s; printf(“%d,%d”,s,t); } 为了使输出结果t为4,输入量a和b应满足的条件是( )。 答案:b>a>0 例7:运输公司对用户计算运费,距离越远,每公里运费越低,标准如下: s<250km 250≤s<500 500≤s<1000 1000≤s<2000 2000≤s<3000 3000≤s 设每公里每吨货物的基本运费为p,货物重为w,距离为s,折扣为d,则总运费f计算公式为f=p*w*s(1-d),编写程序。(此程序可用if……else来完成, C语言程序设计详解 26 也可以用switch来完成)。 3、switch结构 switch语句是多分支选择语句,其形式如下: switch(表达式) { case 常量表达式1:语句1 case 常量表达式2:语句2 ………… default:语句n+1 } 注意: 1、switch后面括弧内的“表达式”,可以是整型表达式或字符型表达式,也可以枚举型数据 2、当表达式的值与某一个case后面的常量表达式的值相等时,就执行此case后面的语句,若所有的case中的常量表达式的值都没有与表达式的值匹配的,就执行default后面的语句。 3、每一个case的常量表达式的值必须互不相同,否则就会出现互相矛盾的现象。 4、每个case的出现次序不影响执行结果。 5、执行完一个case后面的语句后,流程控制转移到下一个case继续执行,直到遇到break语句或执行完为止。 例8:请读以程序 #include { int x=1,y=0,a=0,b=0; switch(x) { case 1: switch(y) { case 0:a++;break; case 1:b++;break; } case 2: a++;b++;break; } printf(“a=%d,b=%d”,a,b); } 答案:a=2,b=1 C语言程序设计详解 27 程序解析:此程序是switch的嵌套结构,在此程序中break跳出内层switch结构,接着执行外层switch的case 2后的语句。 例9:下面程序的运行结果是( ) main() {int i; for(i=1;i<=5;i++) switch(i%5) { case 0:printf(“*”);break; case 1:printf(“#”);break; default:printf(“\\n”); case 2:printf(“&”); } } 答案:#& & &* 例10:编写程序给出一百制成绩,要求输出成绩等级‘A’、‘B’、‘C’、‘D’、‘E’,90分以上为A,80-89分为‘B’,70-79为‘C’,60-69为‘D’,60分以下为‘E’。 main() {int score; scanf(“%d”,&score); switch(score/10) {case 10: case 9:printf(“等级是A”);break; case 8: printf(“等级是B”);break; case 7: printf(“等级是C”);break; case 6: printf(“等级是D”);break; default: printf(“等级是E”);break; } } 三、循环型程序设计 1、while语句 while语句用来实现”当型”循环结构,其一般形式如下: while(条件表达式) 循环体语句 例11:设有程序段: int k=10; C语言程序设计详解 28 while(k=0) k=k-1; 循环体执行( )次。 答案:0 答案解析:在此程序的while结构中,条件表达式k=0的结果永远为0即为假,所以循环执行的次数为0。 例12:下面程序段的运行结果是( ) x=y=0; while(x<15) y++,x+=++y; printf(“%d,%d”,y,x); 答案:8,20 例13、设有程序段 t=0; while(printf(“*”)) { t++; if(t>3) break; } 循环执行( )次 答案:4 答案解析:while结构中的条件表达式printf(“*”)的值为输出数据的个数,在此例中,printf(“*”)的结果为1,即为真。 2、do-while语句 do-while语句的特点是先执行循环体,然后判断循环条件是否成立,其一般形式为: do{ 循环体语句 }while(条件表达式); 例14、若有如下语句 int x=3; do {printf(“%d\\n”,x-=2);} while(--x); 则上面程序段输出结果是( )。 答案:1 C语言程序设计详解 29 例15、以下程序段循环执行几次。 x=-1; do { x=x*x;} while(!x); 答案:1 例16、下面程序的运行结果是( ) main( ) { int y=10; do{y--;} while(--y); printf(“%d\\n”,y--); } 答案:0 3、for语句 C语言中的for语句使用最为灵活,不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况,它完全可以代替while语句,其一般形式为: for(表达式1;表达式2;表达式2) 语句 说明: ①for语句一般形式中的“表达式1”可以省略,此时应for语句之前给循环变量赋初值。注意省略表达式1时,其后的分号不能省略。 ②如果表达式2省略,即不判断循环条件,循环无终止地进行下去。 ③表达式3也可以省略,但此时程序设计者应另外设法保证循环正常结束。 ④可以省略表达式1和表达式3,只有表达式2,即只给循环条件,在这种情况下,完全等同于while语句。 ⑤3个表达式都可省略,如:for( ; ; ) 语句,相当于while(1) 语句,即不设初值,不判断条件(认为表达式2为真值),循环变量不增值。无终止地执行循环体。 ⑥表达式1可以是设置循环变量初值的赋值表达式,也可以是与循环变量无关的其他表达式。for(sum=0;i<=100;i++) sum=sum+i; ⑦表达式一般是关系表达式(如i<=100)或逻辑表达式(如a例17:若i为整型变量,则以下循环执行次数是( )。 C语言程序设计详解 30 for( i=2 ;i!=0 ; ) printf(“%d”,i--); 答案:21 例18:以下不是无限循环的语句是( ) A)for(y=0,x=1;x>++y;x=i++) i=x; B)for(;;x++=i) C)while(1){x++;} D)for(i=10;;i--) sum+=i; 答案:A 例19、执行语句for( i=1 ; i++<4 ; ); 后变量i的值是( ) 答案:4 例20、下面程序段的功能是计算1000!的末尾含有多少个零。请填空。 for(k=0,i=5;i<=1000;i+=5) {m=i; while( ) {k++;m=m/5;} } 答案:m%5!=0 4、break与continue 在break语句可以使流程跳出switch结构,继续执行switch语句下面的一个语句。实际上,break语句还可以用来从循环体内跳出循环体,即提高结束循环,接着执行循环下面的语句。break语句的一般形式为:break; continue语句只结束本次循环,而不是终止整个循环的执行。continue语句的一般形式为:continue; 例21:下列程序的运行结果是( )。 main( ) { int i,j,x=0; for(i=0;i<2;i++) {x++; for(j=0;j<=3;j++) { if(j%2) continue; x++;} x++; } printf(“x=%d\\n”,x); } 答案: C语言程序设计详解 31 例22、下面程序的运行结果是( ) main( ) { int k=0;char c=’A’; do {switch(c++) case ‘A’:k++;break; case ‘B’:k--; case ‘C’:k+=2;break; case ‘D’:k=k%2;continue; case ‘E’:k=k*10;break; default:k=k/3; } k++; }while(c<‘G’); printf(“k=%d\\n”,k); 答案: 5、循环的嵌套 一个循环体中又包含另一个完整的循环结构称为循环的嵌套。内嵌的循环中还可嵌套循环,这就是多层循环。 6、举例: ①求和问题 例23:s=1+2+…………..+100 例24:s=1!+2!+………… +100! 例25:e=1+1/1!+1/2!+1/3!+…… 精确到10-6 例26:s=1-1/3!+1/5!-1/7!+……. 精确到10-6 #include “math.h” main( ) { long t; double s; for(s=0,t=1,i=0;fabs(1/t)>=1e-6;i++) { t=(-1)*t*i; s=s+1/t; } printf(“%lf”,s); }(例26程序设计) 例27:有一分数序列:2/1,3/2,5/3,8/5,13/6,21/13,……. C语言程序设计详解 32 求出这个数列的前20项之和 main() { int s=0, f1=1,f2=2; for(I=1;I<=20;I++) { s=s+f2/f1; f2=f1+f2; f1=f2-f1; } printf(“%d”,s); } 例28:打印九九表 main( ) {int i,j; for(i=1;i<=9;i++) { for(j=1;j<=9;j++) printf(“%2d*%2d=%2d”,i,j,i*j); printf(“\\n”); } } 例29:打印出下列图形 * * * * * * * * * main( ) { int i,j; for(i=1;i<=5;i++) { for(j=1;j<=5+fabs(i-3);j++) printf(“ ”); for(j=1;j<=5-2*fabs(i-3);j++) printf(“*”); printf(“\\n”); } } C语言程序设计详解 33 例30、打印出下列图形 * *** ***** *** * 例31、兔子繁殖问题 main( ) { int i,f1,f2,f3; for(i=3;i<=12;i++) { f3=f1+f2; f1=f2; f2=f3; } printf(“%d”,f3); } 例32、求最大公约数、最小公倍数。 main( ) {int r,u,v,m,n; scanf(“%d,%d”,&m,&n); m=u;n=v; if(u printf(“最大公约数是:%d,最小公倍数是”,v,m*n/v); } 例33、sn=a+aa+aaa+aa…a,从键盘输入a及n后,求sn 方法一: main() {int n,a; long s; scnaf(“%d%d”,&n,&a); for(i=1;i<=n;i++) s=s*10+i*a; printf(“%ld”,s); } 方法二: C语言程序设计详解 34 main() {int n,a; long s,t=0; scnaf(“%d%d”,&n,&a); for(i=1;i<=n;i++) {t=t*10+a s=s+t; } printf(“%ld”,s); } 例34、给出一个不多于5位的正整数,要求①求出它是几位数②分别打印出每一位数③逆序打印此数据。 main( ) {long a; int i=0;s=0; printf(“它的每一位数:”); While(a!=0) { i=i+1; x=a%10; a=a/10; s=s*10+x; printf(“%d ”,x); } printf(“\\n”); printf(“它是%d位数\\n”,i); printf(“它的逆序是%d\\n”,s); } 例35、输出1—100之间每位数的乘积大于每位数的和的数 main() { int n,k=1,s=0,m; for(n=1;n<=100;n++) {k=1;s=0; m=n; while(m!=0) { k=k+m%10; s=s+m%10; m=m/10; } if(k>s) printf(“%d”,n); } } 例36、猴子吃桃问题,猴子第一天摘下若干桃子,当即吃了一半,还不过 C语言程序设计详解 35 瘾又多吃了一个,第二天又将剩下的桃子吃掉一半,又多吃了一个,以后每天都吃前一天剩下的半多一个,到第10天,再吃桃时只剩下一个桃子,求第一天共摘下多少了桃。 main() {int i,s=1; for(i=9;i<=1;i--) s=2*s+1; printf(“第一天共摘下%d个桃子”,s); } 例37:准备客票:某铁路线上共有10个车站,问需要准备几种车票。 main() {int i,j,s=0; for(I=1;I<=9;I++) for(j=I+1;j<=10;j++) s=s+1; printf(“需要准备%d”,2*s); } 例38、有1020个西瓜,第一天卖一半多两个,以后每天卖剩下的一半多两个,问几天以后能卖完。 main( ) { int day=0,s=1020; while(s<=0) {s=s/2-2; day++; } printf(“%d”,day); } 例39、从三个红球、五个白球、六个黑球中任意取出八个球,且其中必须有白球,输出所有可能的方案。 main() {int red,white,black; for(white=1;white<=5;white++) for(red=0;red<=3;red++) for(black=0;black<=6;black++) if(white+red+black==8) printf(“%d个红球,%d个白球,%d个黑球”,red,white,black); } 例40、二分迭代法 二分迭代法的思想是:先取f(x)=0的两个粗略解x1与x2。若f(x1)与f(x2)符号相反,则方程f(x)=0在区间(x1,x2)中至少有一个根。 While(fabs(x1-x2)>=1e-6) C语言程序设计详解 36 { x3=(x1+x2)/2; if(f(x3)*f(x2)>0) x2=x3; else x1=x3; } 例41、牛顿切线法求a的平方根。 x=√a x2=a 构造函数 f(x)=x2-a x2=x1-f(x1)/f’(x1) double sq-root(double a) {double x; x=a; while(fabs(x*x)-a>=1e-6) x=(x+a/x)*0 5; return(x); } 例42、求积分用梯形法求sin(x)*cos(x)的定积分,设上限a、下限b分别为0,1.2。积分区间分割数n=100. S=h/2[f(a)+f(b)]+h(f(a+h)+f(a+2h)+………..+f(b)) main( ) { int i,n=100; double h,s,a=0,b=1.2; s=0.5*(sin(a)*cos(a)+sin(b)*cos(b)); for(i=1;i<=n;i++) s=s+sin(a+i*h)*cos(a+i*h); printf(“%lf”,s); } 例44、验证一个数是否为素数 main( ) {int n,I,flag=1; C语言程序设计详解 37 scnaf(“%d”,&n); for(i=2;i<=sqrt(n);i++) if(n%i==0) {flag=0;break;} if(flag==1) printf(“%d是素数。”,n); else printf(“%d不是素数。”,n); } 例45、输入一个数,输出这个数的质因子乘积的形式。例如输入72后,输出:72=2*2*2*3*3。 main() { int n,i=2,flag=0; scanf(“%d”,&n); printf(“%d=”,n); while(n!=1) { if(n%i==0) {if(flag==0) {printf(“%d”,i);flag=1;} else {printf(“*%d”,i);} n=n/i; } else i=i+1 } } C语言程序设计详解 38 第四章 模块化程序设计 一、函数 1、C程序结构 一个简单的函数调用例子。 main() {printstar(); print_message(); printstar(); } printstar() { printf(“**************************\\n”); } print_message() { printf(“How do you do!\\n”); } 说明: ①用C语言编写函数至少要编写一个main()函数,执行C程序就是执行相应的main()函数,调用其他函数后流程回到main函数,在main函数中结束整个程序的运行。 ②一个C程序可由一个主函数和若干个函数组成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。 ③所有函数都是平行的,即在定义函数时互相独立的,一个函数并不从属于另一个函数,即函数不能嵌套调用 ④从用户使用的角度看,函数有两种:标准函数,用户自定义函数。 ⑤从函数的形式看,函数分两类:无参函数、有参函数。 例1:以下正确的说法是( )建立函数的目的之一是: A)提高程序的执行效率 B)提高程序的可读性 C)减少程序的篇幅 D)减少程序文件所占内存 答案:B C语言程序设计详解 39 例2:以下正确的说法是( ) A)用户若需调用标准库函数,调用前必须重新定义 B)用户可以重新定义标准函数,若如此,该函数将失去原有含义 C)系统根本不允许用户重新定义标准库函数 D)用户若需调用标准库函数,调用前不必使用预编译命令将该函数所在文件包括到用户源文件中,系统自动去调。 答案:B 2、函数的定义 函数定义就是在程序中设定一个函数模块。 例3: main( ) { double sum(int a,double b );/*函数声明*/ } double sum(int a,double b) /*函数定义*/ { /*函数体*/ } 注意: ①函数名应符合C语言对标识符的规定。以字母或下划线开头,后跟字母、数字下划线的组合。 ②形式参数写在函数后面的一对圆括号内,表示将从主调函数中接收哪些类型的信息,程序进行编译时,并不为形式参数分配存储空间。只有在被调用时,形式参数才临时地占有存储空间。 ③函数返回值类型,如省略返回值类型系统默认为int型,对不需要使用函数返回值的函数,应定义为void类型。 ④函数的外部性,函数不能嵌套,一个函数不能定义在别的函数内部。 3、函数声明 在主调函数中,要对在本函数中将要调用的函数事先作一声明。所谓“声明”是指向编译系统提供必要的信息:函数名,函数的类型,函数参数的个数,类型及排列次序。 ①函数声明末尾要加; C语言程序设计详解 40 ②函数声明可以不写形参名,但必须写类型标识符。如上例中可以把声明改为:double sum(int ,double );但不能只写形式参名而不写类型如:double sum(a,b ); 例4:若调用一个函数,且此函数中没有return语句,则正确的说法是() A)没有返回值 B)返回若干个系统默认值 C)能返回一个用户所希望的函数值 D)返回一个不确定的值 答案:D 例5、C语言规定,函数返回值的类型是由() A)return语句中的表达式类型决定 B)调用该函数时的主调函数类型所决定 C)调用该函数时系统临时决定 D)在定义该函数时所指定的函数类型所决定 答案:D 例6、下面函数调用语句含有实参个数为() func((exp1,exp2),(exp3,exp4,exp5)); 答案:2 例7、若输入的值是-125,以下程序的运行结果是( )。 #include “math.h” main() { int n; scanf(“%d”,&n); n=fabs(n); fun(n); } fun(int n) { int k,r; for(k=2;k<=sqrt(n);k++) { r=n%k; while(r==0) { printf(“%d”,k); n=n/k; if(n>1) printf(“*”); } } C语言程序设计详解 41 if(n!=1) printf(“%d\\n”,n); } 答案:-125=-5*5*5 例9、以下正确的函数定义形式是( )。 A)double fun(int x,int y) B)double fun(int x;int y) C)double fun(int x,int y); D)double fun(int x,y); 答案:A 例10、以下正确的函数形式是()。 A)double fun(int x,int y) {z=x+y;return z;} B)fun(int x,y) {int z; return z;} C)fun(int x,int y) {int x,y;double z; z=x+y;return z;} D)double fun(int x,int y) {doble z; z=x+y;return z;} 答案:D 4、函数的传值调用 参数是函数调用时进行信息交换的载体,关于形参与实参的说明: ①在定义函数中指定的形参,在未出现函数调用时,它们并不占用内存中的存储单元。只有在函数调用时,函数中的形参才被分配内存单元,形参与实参各占一个独立的存储空间,函数返回时,临时存储区被撤销。 ②在被定义的函数中,必须指定形参的类型,实参可以是常量、变量或表达式,但要求它们有确切的值。实参与形参的类型应相同或赋值兼容。 ③ C语言规定,实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实能。 例11、 main( ) { int a=3,b=5; C语言程序设计详解 42 void swap(int x,int y); swap(a,b); printf(“%d,%d”,a,b); } void swap(int x,int y) {int temp; temp=x;x=y;y=temp; } 输出结果:3,5 例12、以下正确的说法是()。 在C语言中, A)实参与其对应的形参各占用独立的存储单元。 B)实参和与其对应的形参共占用一个存储单元 C)只有当实参和与其对应的形参同名时才共占用存储单元。 D)形参是虚拟的,不占用存储单元。 答案:A 例13、以下不正确的说法是()。 C语言规定 A)实参可以是常量、变量或表达式 B)形参可以是常量、变量或表达式 C)实参可以为任意类型 D)实参类型应与其对应形参的一致 答案:B 5、函数的递归调用 在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。 例14、用递归方法计算n!的函数rfact( )。假设n为正数。 求n!可以用递推方法,即从1开始,乘2,再乘3……一直乘到n。这种方法容易理解,也容易实现。递推法的特点是从一个已知的事实出发,按一定规律推出下一个事实,再从这个新的已知的事实出发,再向下推出一个新的事实。 float rfac1(int n) { int i; float t; for(i=1;i<=n;i++) t=t*I; return(t); } C语言程序设计详解 43 求n!也可以用递归方法,即5!=5*4!,而4!=4*3!,3!=3*2!,2!=2*1!,1!=1,可用下面的递归公式表示。 =1 n=1 rfact(n) =rfact(n-1)*n n>1 long rfact(int n) { if( n<=1) return(1); else return(n*rfact(n-1)); } 分析:递归总是由两部分组成的: ①递归方式 ②递归终止条件 例15、求1/n! ①递归方式 n>1 f15(n)=f15(n-1)*1/n ②递归终止条件 n=1 f15=1 double f15(int n) { if(n==1) return(1); else return(f15(n-1)*1/n); } 例16、s=1+1/2+1/3+…….+1/n ①递归方式 n>1 f16(n)=f16(n-1)*1/n ②递归终止条件 n=1 f16(n)=1 double f16(int n) { if(n==1) return(1); else C语言程序设计详解 44 return(f16(n-1)+1/n); } 例17、s=1-1/2+1/3-1/4+…..1/n 精确到10-6 ①递归方式 n>1 f17(n)=f17(n-1)±1/n ②递归终止条件 n=1 f17(n)=1 double f17(int n, int flag) { if(n==1) return(1); else return(f17(n-1,-flag)+1/n*flag); } main() {int n,flag; scanf(“%d”,&n); if (n%2==0) flag==-1; else flag==1; printf(“%lf”,f17(n,flag)); } 例18、以下程序的功能是用递归法计算学生的年龄,已知第一位学生年龄最小,为10岁,其余学生一个比一个大2岁,求第5位学生的年龄。 ①递归方式 n>1 f18(n)=f18(n-1)+2 ②递归终止条件 n=1 f18(n)=10 int f18(int n) { if(n==1) return(10); else return(f18(n-1)+2); } 例19、求u,v两数的最大公约数 ①递归方式 v!=0 f19(u,v)=f19(v,u%v) ②递归终止条件 v==0 f19(u,v)=u int f19(int u,int v) { if (v==0) return(u); else return (f19(v,u%v)); } C语言程序设计详解 45 例20、兔子繁殖问题、牛群繁殖问题 ①递归方式 n>1 f20(n)=f20(n-1)+f20(n-2) ②递归终止条件 n=1 f20(n)=1 n=2 f20(n)=1 int f20(int n) { if(n==1) return(1); else if(n==2) return(1); else return(f20(n-1)+f20(n-2)); } 例 21、写出计算 Ackermann 函数 ack(m,n)的递归函数。对于 m>=0,n>=0,Ack(m,n)定义为: ack(0,n)=n+1 ack(m,0)=ack(m-1,1) ack(m,n)=ack(m-1,ack(m,n-1)) ①递归方式 n>1 ack(m,n)=ack(m-1,ack(m,n-1)) ②递归终止条件 n=0 ack(m,n)=ack(m-1,1) m=0 ack(m,n)=n+1 double ack(int m,int n) { if(m==0) return(n+1); else if(n==0) return(ack(m-1,1)); else return(ack(m-1,ack(m,n-1))); } 例22、分别写出Hermite多项式Hn(x)的值的递推和递归函数。Hn(x)定义如下: H0(x)=1 H1(x)=2x Hn(x)=2xHn-1(x)-2(n-1)Hn-2(x) 递推法: C语言程序设计详解 46 double h1(int n,float x) { float s1=1,s2=2*x,s3; int i; if(n==0) return(1); else if(n==1) return(2*x); else { for(i=2;i<=n;i++) {s3=2*x*s2+2*(n-1)*s1; s1=s2; s2=s3; } } } 递归法: ①递归方式 n>1 h(n,x)=2*x*h(n-1,x)-2*(n-1)*h(n-2,x) ②递归终止条件 n=0 h(n,x)=1 n=1 h(n,x)=2*x double h1(int n,float x) { if(n==0) return(1); else if(n==1) return(2*x); else return(2*x*h(n-1,x)-2*(n-1)*h(n-2,x)); } 例23、求a的平方根 递推法: double sq_root(double a) { double x; x=a; while(fabx(x*x)-a)>=1e-6) x=(x+a/x)*0.5; return(x); } 递归法: ①递归方式 fabx(x*x)-a)>=1e-6 sqroot(x,a)=sqroot((x+a/x)*0.5,a) ②递归终止条件 fabx(x*x)-a)<1e-6 sqroot(x,a)=x double sqroot(double x,double a) { if(fabs(x*x)-a<1e-6) return(x); else C语言程序设计详解 47 return(sqroot((x+a/x)*0.5,a)) } 例24、求一个给定数字的所有质因子,如72=2*2*2*3*3 递推法: void yizi(int n) {int i=2,flag=0; while(n!=1) { if(n%i==0) { if(flag==0) {printf(“%d=%d”,n,i); flag=1; } else printf(“*%d”,i); n=n/i; } else i=i+1; } } 递归法: void yizi(int n,int i,int flag) {if (n!=1) { if(n%i==0) { if (flag==0) {printf(“%d=%d”,n i); flag=1;} else printf(“*%d”,i); yizi(n/i,i,flag); } else yizi(n,i+1,flag); } } 例24、用递归法将一个整数n转换成字符串 void conver(int n) {int i; if(i=n/10)!=0) convert(i); putchar(n%10+’0’); } 二、变量的存储属性 1、动态变量 动态变量是C程序中使用最多的一种变量,在程序执行过程中自动进行建 C语言程序设计详解 48 立和撤销,它们存在于程序的局部,也只在这个局部中可以使用,这种变量称为动态变量,动态变量有两种:自动(auto)变量和寄存器(register)变量。 在一个函数内部定义的变量是局部 例25: main() { int x=1; { void prt(void); int x=3; prt( ); printf(“x=%d\\n”,x); } printf(“x=%d\\n”,x); } void prt(void) { int x=5; printf(“x=%d”,x); } 注意: 1、系统在程序执行过程中动态建立动态撤消,在赋值前它的值是不确定的。 2、局部可用。 3、外层的变量被内层的同名变量屏蔽。 例26:以下正确的说法是() 如果在一个函数中的复合语句中定义了一个变量,则该变量( ) A)只在该复合语句中有效 B)在该函数中有效 C)在本程序范围内均有效 D)为非法变量 例27:以下正确的说法为( ) A)在不同函数中可以使用相同名字的变量 B)形式参数是局部变量 C)在函数内定义的变量只在本函数范围内有效 D)在函数内的复合语句中定义的变量在本函数范围内有效 2、寄存器变量 register C语言程序设计详解 49 通常把使用频率较高的变量定义为寄存器变量。 3、静态变量 static 注意: ①在编译时为其分配空间,如不赋初值,初值为0。 ②局部可用或全局可用。 ③静态变量具有可继承性。 4、外部变量 在一个文件中,定义在所有函数之外的变量称为外部变量。 注意: ①在编译时为其分配空间。 ②全局可用。 ③限定本文件的外部变量只在本文件使用 static int a=3; main( ) { } ④将外部变量的作用域在本文件范围内(或在其它文件)扩充。 extern 三、编译预处理 注意: ①编译预处理是在编译前对源程序进行的一些预加工。 ②C语言 的预处理命令均以“#”开头,末尾不加分号 ③编译预处理命令可以出现在程序中的任何位置 1、宏替换 ①字符串宏替换 ②带参的宏替换 注意: ①宏名与宏体之间应以空格相隔 ②宏名不能用引号 ③较长的定义在一行写不下时,可在末尾加 \\。 ④对带参宏定义时、宏休及其各个形参应用括号括起来。 C语言程序设计详解 50 2、文件包含 格式1:#include“文件标识” 格式2:#include<文件名> 例28:以下叙述中不正确的是() A)预处理命令行都必须以#开头 B)在程序中凡是以#号开头的语句行都是预处理命令 C)C程序在执行过程中对预处理命令行进行处理 D)以下是正确的宏定义 #define IBM_PC 例29:以下叙述中正确的是() A)在程序的一行上可以出现多个有效的预处理命令行 B)使用带参的宏时,参数的类型应与宏定义时的一致 C)宏替换不占用运行时间,只占编译时间 D)在以下定义中C R是称为“宏名”标识符 #define C R 045 例30:设有以下宏定义: #define WIDTH 80 #define LENGTH WIDTH+40 则执行赋值语句:v=LENGTH*20(v为int 型变量)后,v的值是( )。 C语言程序设计详解 51 第五章 数组 数组是指一组同类型数据组成的序列,用一个统一的数组名标识这一组数据,用下标来指示数组中元素的序号,同一数组中的所有元素必须属于同一数据类型。 一、一维数组 1、一维数组的定义 一维数据的定义方式为:类型说明符 数组名[常量表达式];例如:int a[10];说明: ①数组名定名规则和变量名相同,遵循标识符定名规则。 ②数组名后是用方括号括起来的常量表达式,不能用圆括号。 ③常量表达式表示元素的个数,即数组长度。 ④常量表达式中可以包括常量或符号常量,不能包含变量。 例1:以下对一维数组a的正确说明是( )。 A)int a(10); B)int n=10,a[n]; C)int n;scanf(“%d”,&n); D)#define SIZE 10 int a[n]; int a[SIZE]; 答案:D 2、一维数组的初始化 ①可对全部元素赋初值:例如:int a[5]={1,3,5,7,9}; ②可给部分赋值:例:int a[5]={1,3,5}; ③如果想使全部元素为1: 例 int a[5]={1,1,1,1,1};(注:不能省略) ④在对全部元素赋值时,可不指定数组长度。 例 int a[ ]={1,2,3,4,5,6}; ⑤如对一个静态或外部的数组不进行初始化,隐含的初值为0。如不对动态数组初始化,则其初始值为一些不可预料的数。 例2:以下能对一维数组a进行正确初始化的语句是( )。 A)int a[10]=(0,0,0,0,0); B)int a[10]={}; C)int a[]={0}; D)int a[10]={10*1}; 答案:C C语言程序设计详解 52 例3:以下不正确的定义语句是( )。 A)double x[5]={1.0,2.0,3.0,4,0,5.0}; B)int y[5]={1,2,3,4,5,6}; C)char c1[]={‘1’,’2’,’3’,’4’,’5’}; D)char c2[]={‘\\x10’,’\\xa’}; 3、一维数组元素的引用 数组元素的表示形式为:数组名[下标] 例4:数组元素引用实例 main( ) {int I,a[10] for(I=0;I<=9;I++) a[i]=I; for(I=0;I<=0;I++) printf(“%d”,a[i]); } 说明: ①下标可以是整型常量、变量或表达式 ②不要越界 例5:若有说明:int a[10];则对a数组元素的正确引用是( )。 A)a[10] B)a[3.5] C)a(5) D)a[10-10] 例6:下面程序中有错误的行是( )注:每行程序前面的数字表示行号 1 main() 2 { int a[3]={1}; 3 int i; 4 scanf(“%d”,&a); 5 for(I=1;I<3;I++) a[0]=a[0]+a[i]; 6 printf(“a[0]=%d\\n”,a[0]); 7 } 答案:4 4、数组名作为函数参数 main( ) C语言程序设计详解 53 {void swap(int x[2]) int a[2]={3,5}; swap(a); printf(“%d,%d\\n”,a[0],a[1]); } void swap(int x[2]) {int t; t=x[0];x[0]=x[1];x[1]=t; } 输出结果:5,3 注意: 以数组名作参数时,采取的不是“值传递”方式,而是“地址传送”方式,即把实参数组起始地址传给形参数组,这样形参数组就和实参数组共占同一段内存单元。 5、一维数组的应用举例 例7:对n个数排序(由小到大)起泡法 基本思想:将相邻两个数a[0]与a[1]比较,按要求将这两个数排好序;再将a[1]与a[2]比较,.….依次处理,直到将最后两个数比较处理完毕。 void bubble(int a[],int n) {int I,j,temp for(I=1;I<=n-1;I++) for(j=0;j<=n-I-1;j++) if(a[j]>a[j+1]) { temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } 例8:选择法:从所有元素中选择一个最小元素放在a[0]作为第一轮;第二轮是从a[1]开始到最后一个元素选择一个最小的放在a[1]中以此类推。n个数要比较n-1轮。 C语言程序设计详解 54 void selectsort(int a[],int n) {int I,j,p,temp; for(I=0;I 例9:插入法:有n个数,已按由小到大顺序排列好,要求输入一个数据,把它插入到原有数列中,而仍然保持有序。 void f(int a[ ],int n,int x) {int I,j; while(a[i]<=x && I {for(j=n-1;j<=I;j--) a[j+1]=a[j]; a[j]=x; } else a[i]=x; } 例10:插入法:下面程序的运行结果是( )。 main() { int I=0,n=3,j,k=3; int a[5]={1,4,5}; while(I<=n&&k>a[I]) I++; for(j=n-1;j>=I;j--) a[j+1]=a[j]; a[i]=k; for(I=0;I<=n;I++) printf(“%3d”,a[i]); } 答案:1 3 4 5 例11:插入法:若有以下程序段: ………….. int a[]={4,0,2,3,1},I,j,t; for(I=1;I<5;I++) {t=a[i];j=I-1; while(j>=0&&tC语言程序设计详解 55 {a[j+1]=a[j];j--;} a[j+1]=t; } for(I=0;I<5;I++) printf(“%d ”,a[i]); ………… 则该程序段的输出结果是( )。 答案:0 1 2 3 4 例12:下面程序用“两路合并法”把两个已按升序排列的数组合并成一个升序数组。请填空。 main() { int a[3]={5,9,19}; int b[5]={12,24,26,37,48}; int c[10],I=0,j=0,k=0; while(I<3&&j<5) if(__________) {c[k]=b[j];k++;j++;} else {c[k]=a[i];k++;I++;} while(________) { c[k]=b[j];k++;j++;} while(________) { c[k]=a[i];k++;I++;} for(I=0;I 例13:折半查找法 void binary_search(int a[],int n,int x) {int mid,top=0,bot=n-1,I,find=0; do {mid=(top+bot)/2; if(x==a[mid]) {printf(“found”);find=1;} else if(x例14:下面程序用“顺序查找法”查找数组a中是否存在某一关键字。请填空。 main() {int a[8]={25,57,48,37,12,92,86,33}; int I,x; C语言程序设计详解 56 scanf(“%d”,&x); for(I=0;I<8;I++) if(x==a[I]) {printf(“Found! The index is :%d\\n”,--i);____________;} if(____________) printf(“Can’t found!”); } 例15:下面程序的运行结果是( )。 main() {int a[9]={0,6,12,18,42,44,52,67,94}; int x=52,I,n=9,m; I=n/2+1; m=n/2; while(m!=0) { if(x{I=I-m/2-1;m=m/2;} else if(x>a[i]) {I=I+m/2+1;m=m/2;} else break; } printf(“The index is:%d”,I); } 答案:The index is:6 例16:要求写一函数,实现对包含任意个数据的数列实现头尾颠倒。 void f(int a[ ],int n) {int I,t; for(I=0;I } 例17:下面程序的功能是输入5个整数,找出最大数和最小数所在的位置,并把二者对调,然后输出调整后的5个数。请填空。 main() { int a[5],max,min,I,j,k; for(I=0;I<5;I++) scanf(“%d”,&a[i]); min=a[0]; for(I=1;I<5;I++) if(a[i] max=a[0]; for(I=1;I<5;I++) if(a[i]>max) {max=a[i];________________;} ________________________ printf(“%d,%d”,k,j); for(I=0;I<5;I++) printf(“%5d”,a[i]); } 答案: 例18:下面程序的运行结果是( )。 main() {int a[10]={1,2,2,3,4,3,4,5,1,5}; int n=0,I,j,c,k; for(I=0;I<10-n;I++) {c=a[i]; for(j=I+1;j<10-n;j++) if(a[j]==c) {for(k=j;k<10-n;k++) a[k]=a[k+1]; n++; } } for(I=0;I<10-n;I++) printf(“%d”,a[i]); } 答案:12345 例19:当从键盘输入18并回车后,下面程序的运行结果是( )。 main() { int x,y,I,a[8],j,u,v; scanf(“%d”,&x); y=x;I=0; do { u=y/2; a[i]=y%2; I++;y=u; }while(y>=1); for(j=I-1;j>=0;j--) printf(“%d”,a[j]); } 答案:10010 例20:有17个人围成一圈,编号为0—16,从第0号的人开始从1报数, C语言程序设计详解 58 凡报到3的倍数离开圈子,然后再数下去,直到最后只剩下一个人为止,问此人原不的位置是多少号。 main() {int a[17],I,t=0,s=0; for(I=0;I<17;I++) a[i]=1; while(t<=16) { for(I=0;I<17;I++) if(a[i]==0) {s=s+1; if(s%3==0) {a[i]=0;t=t+1;} } } for(I=0;I<17;I++) if(a[i]==1) printf(“%d”,i); } 二、二维数组 1、二维数组的定义 二维数组定义的一般形式为: 类型说明符 数组名[常量表达式][常量表达式] 注意: 二维数组在内存中是按一维连续顺序存放的。设有一个m*n的二维数组a,其中第i行第j列元素a[i][j]在数组中的顺序号计算公式为:i*n+j 例21:以下对二维数组a的正确说明是( )。 A)int a[3][] B)float a(3,4) C)double a[1][4] D)float a(3)(4) 答案:C 2、二维数组的引用 二维数组的元素的表示形式为:数组名[下标][下标] 例22:若有说明:int a[3][4];则对数组元素的正确引用是( )。 A)a[2][4] B)a[1,3] C)a[1+1][0] D)a(2)(1) 答案:C 3、二维数组初始化 ①分行给二维数组赋初值 如:int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; ②可写在一个花括弧内 C语言程序设计详解 59 如:int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; ③可给部分赋值 如:int a[3][4]={1,2,3}; 如:int a[3][4]={{1},{2},{3}}; ④可省略一维长度 如:int a[ ][4]={1,2,3,4,5,6,7,8,9,10}; 如:int a[ ][4]={{1},{2},{3}}; 例23:以下不能对二维数组a进行正确初始化的语句是()。 A)int a[2][3]={0}; B)int a[ ][3]={{1,2},{0}}; C)int a[2][3]={{1,2},{3,4},{5,6}}; D)int a[ ][3]={1,2,3,4,5,6}; 答案:C 例24:若有说明:int a[ ][3]={1,2,3,4,5,6,7};则a数组第一维的大小是(答案:3 4、函数参数(p 160) 例25:求一个3*4矩阵的所有靠外侧的元素之和。设矩阵为: 3 8 9 10 a= 2 5 –3 5 7 0 -1 4 main() {int add(int m,int n,int arr[]); int total,a[3][4]={3,8,9,10,2,5,-3,5,7,0,-1,4}; total=add(3,4,a[0]); printf(“total=%f”,total); } add(int m,int n,int arr[]) {int I,j,sum=0; for(I=0;I 。 ) } 5、二维数组举例 C语言程序设计详解 60 例26:将一个二维数行、列互换后输出。 Void f(int a[ ],int n,int m ,int b[ ]) {int I,j; for(I=0;I main() {int a[3[3]={1,3,6,7,9,11,14,15,17},sum=0,I,j; for(I=0;I<3;I++) for(j=0;j<3;j++) if((I==j)||(I+j==3)) sum=sum+a[I][j]; } 例28:下面程序的功能是检查一个二维数组是否对称(即:对所有I和j都有a[I][j]=a[j][I])。请填空。 main() {int a[4][4]={1,2,3,4,2,2,5,6,3,5,3,7,4,6,7,4}; int I,j,found=0 for(I=0;I<4;I++) for(j=0;Jif(a[i][j]!=a[j][i] ) {found=1;break;} if(found) printf(“No”); } 例29:以下程序是求矩阵a,b的和,结果存入矩阵c中并按矩阵形式输出。请填空。 2 -1 -7 -9 a= -4 0 b= -8 10 3 1 main() {int a[3][2]={2,-1,-4,0,3,1}; C语言程序设计详解 61 int b[4][3]={7,-9,-8,10}; int I,j,k,s,c[3][2]; for(I=0;I<3;I++) for(j=0;j<2;j++) {for(s=0,k=0;k<2;k++) s+=a[i][k]*b[k][j]; c[i][j]=s; } for(I=0;I<3;I++) { for(j=0;j<2;j++) printf(“%6d”,c[I][j]); printf(“\\n”); } } 例30:下面程序的运行结果是( )。 main() {int I,j,row,col,min; int a[3][4]={{1,2,3,4},{9,8,7,6},{-1,-2,0,5}}; min=a[0][0]; for(I=0;I<3;I++) for(j=0;j<4;j++) if(a[i][j] 例31:找出一个二维数组中的鞍点,即该位置上的元素在该行上最大,该列上最小。也可能没有鞍点。 void f31(int m, int n, int arr[] ) {int I,j,k,max,row,col,flag; for(I=0;I flag=1; for(k=0;kif(arr[k*n+j] 例32:打印出以下的杨辉三角形(包括10行) 在 1 1 1 1 1 1 2 1 C语言程序设计详解 62 3 3 1 4 6 4 1 1 5 10 10 5 …....…..... main() {int a[10][10],I,j; for(I=0;I<10;I++) { a[I][0]=1; a[I][I]=1; } for(I=2;I<10;I++) for(j=1;Ja[I][j]=a[I-1][j]+a[I-1][J-1]; } 例33:下面是一个5*5阶螺旋方阵。试编程打印出此形式的n*n的方阵 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9 main() { int a[10][10],I,j,k=0,m,n; printf(“%d”,&n); if(n%2==0) m=n/2; else m=n/2+1; for(I=0;I C语言程序设计详解 63 for(I=0;I 例34:打印“魔方阵”。所谓魔方阵是指这样的方阵,它的每一行、每一列和对角线之和均相等。例如:三阶魔方阵为 8 1 6 3 5 7 4 9 2 答案: 三、字符数组 用来存放字符数据的数组是字符数组。字符数组中的一个元素存放一个字符。 1、字符数组的定义 char a[10]; 2、字符串的结束标志 为了测定字符串的实际长度,C语言规定了一个“字符串的结束标志”,以字符‘\\0’代表。 例35:下面程序段的运行结果是( ) char c[5]={‘a’,’b’,’\\0’,’c’,’\\0’}; printf(“%s”,c); 答案:ab 3、字符数组的初始化 ①逐个字符赋值 C语言程序设计详解 64 char c[10]={‘c’,’o’,’m’,’p’,’u’,’t’,’e’,’r’}; ②对一个字符数组指定一个字符串初值 char c[10]={“computer”}; 系统允许在初始化一个一维字符数组时,省略字符串常量外面的花括号。 如:char c[10]=”computer” 例36:下面是对s的初始化,其中不正确的是() A)char s[5]={“abc”}; B)char s[5]={‘a’,’b’,’c’}; C)char s[5]=”” D)char s[5]=”abcdef” 答案:D 例37:对两个数组a和b进行如下初始化 char a[]=”ABCDEF”; char b[]={‘A’,’B’,’C’,’D’,’E’,’F’};则以下叙述正确的是( )。 A)a与b数组完全相同 B)a与b长度相同 C)a和b中都存放字符串 D)a数组比b数组长度长 答案:D 4、字符数组的输入和输出 ①逐个字符输入输出。用格式符“%c”输入或输出一个字符。 ②将整个字符串一次输入和输出。用“%s”。 ③为了解决scanf函数不能完整地读入带有空格字符的字符串,C语言提供了一个专门用于读字符串的函数gets,它读入全部字符(包括空格),直到遇到回车符为止,用gets(s)表示。 puts(s)的作用是将一个字符串(以’\\0’结束的字符序列)输出到终端,在输出时将字符结束标志’\\0’转换成’\\n’,即输出完字符串后换行。 例38:下面程序段的运行结果是() char c[5]={‘a’,’b’,’\\0’,’c’,’\\0’}; printf(“%s”,c); A)’a’’b’ B)ab C)ab c D)a b 答案:B C语言程序设计详解 65 例39:有两个字符数组a、b,则以下正确的输入语句是() A)gets(a,b); B)scanf(“%s%s”,a,b); C)scanf(“%s%s”,&a,&b); D)gets(“a”),gets(“b”); 答案:B 例40:有字符数组a[80]和b[80],则正确的输出语句是() A)puts(a,b) B)printf(“%s,%s”,a[],b[]); C)putchar(a,b) D)puts(a),puts(b) 答案:D 5、字符串运算函数 ①字符串拷贝函数strcpy如strcpy(str1,”China”) strcpy(str1,str2) ②字符串连接函数strcat如strcat(str1,str2) ③字符串比较函数strcmp如strcmp(str1,str2) ④测字符中长度函数strlen如strlen(“Computer”) 例41;下面程序段的运行结果是( ) char c[ ]=”\\\v\\\\\\0will\\n”; printf(“%d”,strlen(c)); 答案:3 例42:下面程序段的运行结果是( )。 char a[7]=”abcdef”; char b[4]=”ABC”; strcpy(a,b); printf(“%c”,a[5]); 答案:e 例43:判断字符串a和b是否相等,应当使用() A)if(a==b) B)if(a=b) C)if(strcmp(a,b)) D)if(strcpy(a,b)) 答案:C 6、二维数组 二维数组可以认为由若干个一维数组所组成的,因此一个n*m的二维字符数组可以存放n个字符串。 例44:写出程序的输出结果() C语言程序设计详解 66 main() { char str[3][6]={“China”,”Japan”,”Korea”}; str[1][5]=’ ’; printf(“%s\\n”,str[1]); } 答案:Japan Korea 7、字符串数组举例 例45:下面程序段将输出computer,请填空。char c[]=”It’s a computer”; for(I=0;_____________;I++) {____________;printf(“%c”,c[j]); } 答案: I<=7 J=I+7 例46:下面程序段的运行结果是( char x[]=”the teacher”; I=0; while(x[++i]!=’\\0’) if(x[I-1]==’t’) printf(“%c”,x[i]); 答案:he 例47:删除字符 删除一个字符串中的一个字符 ① void del(char a[ ],char b[ ],int n) {int I,j=0; for(I=0;a[i]!=’\\0’;I++) if(I!=n) b[j++]=a[i] } ② void del(char a[ ],int n) { strcpy(&a[n],&a[n+1]); } 。 ) C语言程序设计详解 67 删除一个字符串中的某个字符 void f(char a[ ],char x) {int I; for(I=0;a[i]!=’\\0’;I++) if(a[i]==x) strcpy(&a[I],&a[I+1]); } 例48:下面程序的功能是将已按升序排好序的两个字符串a和b中的字符按升序归并到字符串c中,请填空。 #include “stdio.h” main() { char a[]=”acegikm”; char b[]=”bdfhjlnpq”; char c[80],*p; int I=0,j=0,k=0; while(a[i]!=’\\0’ && b[j]!=’\\0’) {if(a[i]c[k]=’\\0’; if(____________) p=b+j; else p=a+I; strcat(c,p); puts(c); } 答案: c[k]=a[I++] c[k]=b[j++] a[i]==’\\0’ 例49:有已排好序的字符串a,下面的程序是将字符串s中的每个字符按升序的规律插入到a中。请填空。 #include char a[20]=”cehiknqtw”; char s[]=”fbla”; C语言程序设计详解 68 int I,k,j; for(k=0;s[k]!=’\\0’;k++) {j=0; while(s[k]>=a[j]&&a[j]!=’\\0’) j++ for(________________) (__________________); a[j]=s[k]; } puts(a); } 答案: I=strlen(a);I>=j;I— a[i+1]=a[i] 例50:下面程序的功能是在三个字符串中找出最小的。请填空。 main() {char s[20],str[3][20]; int I; for(I=0;I<3;I++) gets(str[i]); strcpy(s,________); if(strcmp(str[2],s)<0) strcpy(s,str[2]); printf(“%s\\n”,__________); } 答案: (strcmp(str[0],str[1]))<0?str[0]:str[1] s 例51:下面程序的运行结果是( ) #include “stdio.h” main( ) {char s[ ]=”ABCCDA”,c; int k; for(k=1;(c=s[k])!=’\\0’;k++) {switch(c) {case ‘A’:putchar(‘%’);continue; case ‘B’:++k;break; default:putchar(‘*’); case ’C’:putchar(‘&’);continue; } putchar(‘#’); } } 答案:#&*&% 例52:输入一行字符,统计其中有多少个单词,规定单词间以一个或多个空 格相隔。 C语言程序设计详解 69 #include “stdio.h” main( ) { char string[81]; int I,s=0,flag=0; gets(string); for(I=0;string[i]!=’\\0’;I++) { if((flag==0)&&(a[i]>=’a’&&a[i]<=’z’)||(a[i]>=’A’&&a[i]<=’Z’)) {flag=1; s=s+1; } if a[i]=’ ’ flag=0; } printf(“%d”,s); } C语言程序设计详解 70 第六章 指针 一、指针的概念 指针即地址,一个变量的指针就是指该变量的地址,存放指针的变量就是指针变量。 注意:指针变量中只能存放地址。 二、指针变量的定义和引用 1、指针变量的定义 int *p; 此语句定义了一个指针变量p,p中可存放一个整型变量的地址。 注意: ①*是指针变量的特征 ②只是分配了一个存储单元,并没有指真正指向,要想使一个指针变量指向一个整型变量必须赋值。例如: int *p,I=3; p=&I; 2、指针变量的引用(两个有关指针的运算符) ①& 取地址运算符号 ②* 指针运算符 *p表示p所指向的变量值。 例3: int *p,a; p=&a; /*指向变量a的值赋给p*/ scanf(“%d”,p); /*从键盘输入一个值赋值给p所指向的变量a*/ *p=5; /*把5赋值给变量p所指向的a*/ 例4:使两个指针变量交换指向 main() { int *p1,*p2,*p,a=10,b=20; p1=&a,p2=&b; printf(“%d,%d\\n”,*p1,*p2); p=p1;p1=p2;p2=p; printf(“%d,%d\\n”,*p1,*p2); } C语言程序设计详解 71 a 10 &a p1 a 10 &b p1 b 20 &b p2 b 20 &a p2 (a) (b) 例5:交换两个指针变量所指向的变量的值。 main() { int *p1,*p2,a=10,b=20,t; p1=&a,p2=&b; printf(“%d,%d\\n”,*p1,*p2); t=*p1;*p1=*p2;*p2=t; printf(“%d,%d\\n”,*p1,*p2); } a 10 &a p1 a 20 &a p1 b 20 &b p2 b 10 &b p2 (a) (b) 例6:以下程序错误的原因是( )。 main( ) { int *p,*q,a,b; p=&a;q=&b; scanf(“%d”,*p); *p=*q; } 答案: 例7:若有定义int a=5;下面对(1),(2)两个语句的正确解释是( )。 1、int *p=&a; 2、*p=a; A) 语句1和2中的*p含义相同。 B) 1和2语句的执行结果都是把变量a的地址赋给指针变量p C) 1在对p进行说明的同时进行初始化,使p指向a,2将变量a的值赋给 指针变量p。 C语言程序设计详解 72 D) 1在对p进行说明的同时进行初始化,使p指向a,2将变量a的值赋于*p。 答案: 例8:若有语句int *point,a=4;和point=&a;下面均代表地址的一组选项是( )。 A) a, point, *&a B) &*a,&a,*point C) *&point,*point,&a D)&a,&*point,point 答案: 三、指针作为函数参数 函数的参数不仅可以是整型、实型、字符型等数据,还可以是指针类型,它的作用是将一个变量的地址传送到另一个函数中。 例9:输入a和b两个整数,按先大后小的顺序输出a和b。 main( ) { void swap(int *px,int *py); int x,y; swap(&x,&y); if(x t=*px; *px=*py; *py=t; } main( ) swap(px,py) X y 例10:一组数组有10个元素,要求输出最大值和最小值。 main( ) {int arr[10]={-1,2,3,-4,-5,7,8,9,10,-7},*p1,*p2,a,b; p1=&a;p2=&b; max_min(arr,p1,p2); printf(“%d,%d”,a,b); } void maxmin(int arr[ ],int *pt1,int *pt2) &b px &a py C语言程序设计详解 73 {int I; *pt1= *pt2= arr[0]; for(I=1;I<10;I++) {if(*pt1>arr[i]) *pt1=arr[i]; if(*pt2>arr[i]) *pt2=arr[i]; } } 四、指针与数组 1、一维数组的指针表示方法 (1)数组中各元素的地址。 int a[10]={1,2,3,4,5,6,7,8,9,10}; a 1 2 ①&a[0] &a[1] &a[2] 、、、&a[i]、、、&a[9] ②a a+1 a+2 、、、a+i、、、、、a+9 注意: 在编译系统计算实际地址时,a+i中的i要乘上数组元素所占的字节数,即a+i*(一个元素所占的字节数) (2)数组元素值 ①a[0] a[1] a[2] 、、、、a[i]、、、、a[9] ②*(a+0) *(a+1) *(a+2)、、*(a+i) *(a+9) 说明: 引用一个数组元素,可以用下标法:如a[i]形式;也可以用指针法如*(a+i)形式。 例11:若有以下定义,int a[5],*p=a;则对数组元素的正确引用是() (A) *&a[5] (B) a+2 (C)*(p+5) (D)*(a+2) 答案: 例12:对数组元素地址的正确引用是() (A)p+5 (B) *a+1 (C)&a+1 (D)&a[0] 答案: 例13:若有定义,则数值不为3的表达式是( ): 3 4 5 6 7 8 9 10 C语言程序设计详解 74 int x[10]={0,1,2,3,4,5,6,7,8,9},*p1; A) x[3] B) p1=x+3,*p1++ C) p1=x+2,*(p1++) D) p1=x+2,*++p1 答案: 例14:分别用下标法、地址法访问数组元素 main( ) {int a[5]={1,3,5,7,9},I,*p; for(I=0;I<5;I++) printf(“%d”,a[i]); for(I=0;I<5;I++) printf(“%d”,*(a+i)); for(p=0;p①指针变量可以实现使本身的值改变,但不要出现a++,因为a是数组名,它是数组首地址是常量,它的值在程序运行期间是固定不变的。 ②要注意指针变量的当前值。 2、二维数组的指针表示方法 例:int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; 1、每行的起始地址 a 1 5 9 2 6 10 3 7 11 4 8 12 ①&a[0][0] &a[1][0] &a[2][0] a[0] a[0][0] a[0][1] a[0][2] a[0][3] 1 2 3 4 a[1] a[1][0] a[1][1] a[1][2] a[1][3] 1 2 3 4 C语言程序设计详解 75 a[2] a[2][0] a[2][1] a[2][2] a[2][3] 1 2 3 4 ②a[0] a[1] a[2] a a[0] a[1] a[2] ③a+0 a+1 a+2 ④*a *(a+1) *(a+2) ⑤&a[0] &a[1] &a[2] 2、各元素的地址 ①&a[0][0] &a[0][1] &a[0][2] ②a[0]+1 a[0]+2 a[1]+2 ③*a+1 *(a+1)+1 *(a+2)+2 3、各元素的值 ①*(&a[0][0]) *(&a[0][1]) *(&a[0][2]) ②*(a[0]+1) *(a[0]+2) *(a[1]+2) ③*(*a+1) *(*(a+1)+1) *(*(a+2)+2) 例15:指向数组元素的指针变量 main( ) {int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int *p;