• 1. C语言程序设计 Copyer:Vigiking
  • 2. 第一章 计算机语言与C语言概述 1.1 计算机语言概念 ■计算机语言定义 计算机能够识别和接受的语言。 要使计算机按自己的意图工作,必须使用计算机所能接受、理解和执行的指令指挥计算机工作。
  • 3. ■计算机语言的种类 机器语言 低级语言 汇编语言 (面向计算机) BASIC 入门语言 F77 科学计算 高级语言 Foxpro 数据库管理 (面向问题 ) C 多用途 计算机语言…………
  • 4. •机器语言 最早问世,用二进制代码构成指令。 如: 100000 (+) 110000 (-) 用机器语言编程的缺点: ─繁琐、不直观、不易调试。 如计算y=2x2+3x-1需要七八条指令。 ─移植性差。 依赖于计算机。
  • 5. •汇编语言 用符号构成指令,如:MOV、ADD 用汇编语言编程: 相对直观,但仍繁琐,仍是面向计算机的语言。 汇编语言是计算机间接接受的语言
  • 6. •高级语言 与低级语言相比,有根本性的区别: 是面向问题的语言。 高级语言的一条指令(语句): y=2*x*x+3*x-1; 对应于:y=2x2+3x-1 y=sin(x); 对应于:y=sinx 用高级语言编程: 直观、易懂、移植性好(不依赖于具体计算机)
  • 7. 上机运行高级语言程序需经过编译: 编译 执行 要上机完成一个计算问题,主要的任务就是用高级语言编写出相应的源程序。即至少要学会一种计算机语言。 高级语言源程序 机器指令 目的程序结果编译程序
  • 8. 1.2 C语言的特点 集高级语言和低级语言的优点于一身: ●能实现低级语言的大部分功能(如直接访问内存物理地址、进行位操作等)。 ●图形功能强。 ●运算符和数据结构丰富。 ●语法限制不太严格,程序设计自由度大。 ●生成目标代码质量高,程序执行效率高。
  • 9. 1.3 简单的C程序介绍 例1: main() 主函数说明 { 程序框架 printf(“abcdef”); 函数体 } 语句 程序的功能是输出字符串:abcdef
  • 10. 例2:求两数之和。 main() { int a,b,c; a a=100; b=50; b c=a+b; c printf(“\n c=%d”,c); } 程序运行结果:c=150
  • 11. 例3:求两数中的最大值。 函数类型 函数名 形参 main() int max (int x,int y) { int a,b,c; { int z; scanf(“%d,%d”,&a,&b); if(x>y) z=x; c=max(a,b); else z=y; printf(“\nmax is:%d”,c); return(z); } } a x b y c z (两个函数组成) 353555
  • 12. 总结上例可知: (1) C程序由函数构成。 (2) 函数由两部分组成: 函数说明部分:函数名、函数类型、形参名、形参类型。 函数体:实现函数的具体操作;由语句构成。 (3) 程序总是从main函数开始执行。 (4) 书写格式自由。 (5)语句必须有分号。 :
  • 13. 第二章 算法 2.1 算法的概念 要利用计算机处理问题,光学习语言的语法规则还不够,最重要的是要学会针对各类型的问题,拟定出有效的解题方法和步骤。解题方法和步骤就是算法。
  • 14. 算法: 为了解决一个问题而采取的有限步骤。 计算机算法: 如何使计算机一步一步地工作的具体过程。
  • 15. 利用计算机处理问题的步骤: 1)设计好算法——算法设计; 2)用计算机语言实现算法——程序设计。 算法必须是“有效”的。 算法设计还要充分考虑算法的好坏。 衡量算法好坏的主要标准: ① 程序简练。 ② 执行速度快。 ③ 占空间少。
  • 16. 例:考虑 的算法。 算法①:直接表达。 直接用语句 s=1+2+3+4+5+6+7+8+9+10当项数较多时该算法不适用s=∑i110
  • 17. 算法②:迭代法(累加求和法) s=1+2+3+4+5+6+7+8+9+10 算法步骤: s i  ① 使s=0 + ② 使i=1 累加器 记数器 ③ s+i→s ④ i+1→i ⑤ 若i≤10转③,否则转⑥ ⑥ 输出s01123364105该算法通用,是好算法
  • 18. 2.2 算法的表示 算法需要有统一的表示方法 常用的表示方法有: 自然语言 流程图 结构化流程图 N-S流程图.
  • 19. 1、自然语言 对于计算 s=1+2+3+4+5+6+7+8+9+10 用自然语言表示为: ①     使s=0 (s为累加器) ②     使i=1 (i为计数器) ③     s+i→s (累加求和公式) ④     i+1→i (计数器加1) ⑤     若i≤10转③,否则转⑥ ⑥ 输出s的值 特点:通俗易懂、文字冗长、含义不大严格。
  • 20. 2、流程图 用流程图符号表示算法。 常用的流程图符号 起止框 输入输出框 处理框 流程线 判断框
  • 21. 对于计算 s=1+2+3+4+5+6+7+8+9+10 用流程图表示为: s+i → s i+1 → i S+i → s S+i → s i≤10 输出s 0 → s 1 → i 直观形象,易于理解,次序清楚YN
  • 22. 3、结构化流程图 传统的流程图有一个弊端:对流程线没有严格的限制,对于较复杂的算法可能会变成乱麻一般(BS型算法)。 为克服这一弊端,提出了由三个基本结构组成算法流程图的思想: 结构化流程图
  • 23. 三个基本结构: ① 顺序结构 按固定顺序(从上到下或从左到右)执行的结构。 ABab
  • 24.    ② 选择结构 根据条件P选择执行哪一个分支。 成立 不成立 pABab成立不成立
  • 25. 例:计算 y= 1/x 当x≠0时 y= 10000 当x=0时 的算法流程图图: 选择结构 输入x X=0? 10000→y 1/x→y 输出y YN
  • 26. ③ 循环结构 重复执行某些操作的结构。 分为两种: 当型循环和直到型循环。 当型循环 直到型循环 P1AAP2aabbYYNN
  • 27. 可以看出,每个基本结构都只有一个入口和一个出口,因此,用三个基本结构构成的流程图不会象乱麻一般,用三个基本结构构成的流程图就成为结构化流程图,用结构化流程图描述的算法称为结构化算法,相应的程序设计就称为结构化程序设计。
  • 28. 观察前例: 0 → s 1 → i S+i → s i+1 → i i≤10 输出s 顺序结构循环结构yn
  • 29. 4 N-S流程图 N-S流程图的三个基本结构: ABP成立不成立ABAB当P1直到P2顺序结构选择结构循环结构
  • 30. 例:计算 y= 1/x 当x≠0时 y= 10000 当x=0时 的N-S流程图: 输入x X=0?是否10000→y 1/x→y 输出y
  • 31. 例: 计算 s=1+2+3+4+5+6+7+8+9+10 的N-S流程图: i≤10 直到i>10 直到型 当型 0 → s 1 → i 输出s 1 → i 0 → s s+i → s i+1 → i s+i → s i+1 → i 输出s
  • 32. 第三章 数据类型、运算符 与 表达式 3.1 C 的数据类型 ●基本类型 (整型、实型、字符型、枚举型) ●构造类型 (略) ●指针类型 (略) ●空类型 各类型包含常量与变量
  • 33. 3.2 常量与变量 ■常量与符号常量 常量 在程序运行过程中其值保持不变的量。 符号常量 用来代表一个常量的标识符。 #define PI 3.1415926
  • 34. 例: #define PI 3.1415926 main() { float r=2,c; c=2*PI*r; printf(“%f”,c); }
  • 35. ■变量 其值可以改变的量。 变量的三要素:   ①  变量名 每个变量有一个名字,作为识别该变量的标识符。 ② 变量的值 每个变量有一个值,变量的值是随时可以改变的。  
  • 36. ③变量的存储单元 每个变量占据一个内存单元,用于存放变量的值。 变量名 a 变量值 存储单元  3
  • 37. 变量的命名规则: 由字母、数字和下划线组成 以字母或下划线开头 a、x1、y_2、_b1、_1c 合法 1x、a+2、Ф、Ω 不合法 变量必须先定义后使用。 程序中何时使用常量?何时使用变量?
  • 38. 3.3 整型数据 ■整型常量 即整常数,c的整常数有三种形式: ①十进制整数 与数学中的整数一致,如:100,123,15等。   ②八进制整数 以0开头的整数,如:010,07,020等。   ③十六进制整数 以0x开头的整数,如:0x10,0xff,0x2a等。
  • 39. ■整型变量 用于存放整数的变量。 分4种类型: ①基本型:int a 16位,可表示的数值范围:-32768—32767 ②短整型:short int b 16位,可表示的数值范围:-32768—32767 ③长整型:long int c 32位,数值范围:-2147483648—2147483647
  • 40. ④无符号型:加上 unsigned 只存放正数。 如:unsigned int x 变量x为无符号整数,16位全表示数码, 数值范围:0—65535 在程序设计中,如果要使用整型变量,必须首先选择以上类型符来定义变量,然后才能使用;
  • 41. 例: main() 定义 { int a,b,c; a=100; b=50; 使用 c=a+b; printf(“%d”,c); } 一般根据什么原则选择变量的类型?
  • 42. 3.4 实型数据 ■实型常量 可使用两种形式表示: ①小数形式:如 1.23, 3.1415926 15.48 ②指数形式:如 1e-20 1.23e5
  • 43. ■实型变量 用于存放实数的变量 分单精度和双精度两种: float a,b 定义a和b为单精度型变量 32位,7位有效数字,10-38—1038 double x,y 定义x和y为双精度型变量 64位,15位有效数字,10-308—10308
  • 44. 例: main() { float r,c; double r,c; r=5; c=2*3.1415926*r; printf(“%f”,c); }
  • 45. 3.5 字符型数据 ■字符常量 用单引号括起来的一个字符。 ’a’,’x’,’*’,’1’等 除此外,以’\’开头的字符如’\n’,’\t’等 称为转义字符,祥见书表3.3
  • 46. ■字符型变量 用于存放字符的变量。 char c1,c2 定义c1和c2为字符型变量 c1=’a’; c2=’b’; 字符赋值 字符型变量存放一个字符,占据一个字节
  • 47. ■字符型数据的存储形式 存放ASCII码 不是 而是 如字符’a’在内存中存放97,’b’ 存放98。 与整数的存储形式一致,它们之间可以通用 一个字符数据既可以作字符用,也可以作整数用(取其ASCII代码)。 如:32+’a’相当于 32+97 若 int x; char c; 则 x=’a’; c=97; x=97; c=’a’; 都允许 a97
  • 48. ■字符串常量 用双引号括起来的字符序列。 如:”abcde”,”china” ”a”也属字符串。 注意”a”与’a’的区别。 对于: char c; c=”a”; 用法错误
  • 49. 字符串中每个字符各占一个字节,并且在字符串结尾加上一个结束标记’\0’ 如:”china” 在内存中占6个字节。   c h i n a \0 C语言中专门的字符串变量,可用字符数组存放(以后介绍)。
  • 50. 3.6 变量赋初值 在定义变量的同时给相应的变量赋初值。 如: int a=3; a float b=5.2; char c=’a’; int x=y=z=6; 效果: 在给变量分配内存单元的同时在相应的单 元中存放初值。 3
  • 51. 3.7 各数值型数据间的混合运算 整型、实型、字符型数据间可以进行混合运算,如: 10-‘a’*1.5 运算时,参加运算的两个数据如果类型不同,则首先将其类型转换成一致再运算,转换规则是: 将优先级低的类型转换到优先级高的类型
  • 52. 数据类型的优先级: 高 double float long unsignde 低 int char
  • 53. 3.8 算术运算符与算术表达式 ■基本的算术运算符 + - * / % ■算术表达式 用算术运算符将运算对象连接起来的式子 用于表达数学公式的式子 如:2*x+y-1/a 表达式经过运算最终得到一个值: 算术表达式的值
  • 54. ■运算符的优先级与结合性 优先级: 在对表达式求值时,如果存在多个运算符,则运算的先后次序按运算符的优先级别从高到底进行。 运算符的优先级关系为: 高:* / % 低:+ - 如:a-2*x 先算 * 2*(a+2) 有括号的情况?
  • 55. 结合性: 如果在一个运算对象两边的运算符的优先级相同,则按规定的“结合方向”处理。 如:a-b+c b与-结合是从左到右,称“左结合性”。 b与+结合是从右到左,称“右结合性”。 每个运算符都有相应的优先级和结合性。 基本算术运算符都是左结合性。
  • 56. 计算表达式例: 2+’A’-1/2.0 1+3/2-1 构造表达式例 2x2+3x-1    a+b a-b a+b a-b x+y x-y a+b/a-b ?(a+b)/(a-b)2*x*x+3*x-1 (*不能省)(a+b)/(a-b)/(x+y)/(x-y)(a+b)/(a-b)/((x+y)/(x-y))(a+b)/(a-b)/(x+y)*(x-y)
  • 57. ■强制类型转换 可以用强制类型转换运算符将一个表达式的值转换成所需类型: 如: (int)(x+y) (float)(7%3)
  • 58. 应用举例: int a=200,b=300,c; c=a*b/100; ? 可知,有自动转换和强制转换, 当自动转换达不到目的时,可用强制转换。 c=(long)a*b/100;c=(long)(a*b)/100;
  • 59. ■自增、自减运算符 自增运算符:++ 使变量值加1 自减运算符:-- 使变量值减1 两种用法: ++i, --i 先加(减)后用 i++, i-- 先用后加(减) 两种用法对i效果一样,但表达式的值不同。 例:假设i的原值为5: j=++i; j=? j=i++; j=?
  • 60. 注意: ●++和--只能用于变量。 如:3++和(a+1)++ 不合法 ●++和--为右结合性。 (-i)++ -i++ -(i++)    若i的原值是5,则该表达式的值是多少?
  • 61. 例:分析执行下列语句后的结果: a=5; a b c d   b=a++; c=--a-b++; d=(a++)-(++b)+c--;   5565067-26-1
  • 62. 两种特殊情况: ① k=(i++)+(i++)+(i++) ② i+++j 是 i+(++j) 还是 (i++)+j ? i++虽然与i=i+1等效,但使用自增自减运算符的代码优化程度好,因而经常使用;但用时需特别小心。
  • 63. 3.9 赋值运算符和赋值表达式 ■赋值运算符 “=”称赋值运算符,其作用是将一个数据赋给一个变量。 如: a=5 不要理解为“等号”。 执行赋值运算的结果,是将右边的数据存入左边变量所对应的内存单元中。
  • 64. ■赋值规则 如果赋值运算符两侧的类型不一致,则在赋值时要进行类型转换,转换规则为: ●实型→整变量 舍去小数部分。 int a=5.5; a中为5。 ●整型→实变量 数值不变,以浮点形式存储。 ●字符型→整变量 放在整形变量低8位。保持原值不变原则。 int a=‘A’;
  • 65. ■复合赋值运算符 在赋值运算符前加上其它运算符,可以构成复合赋值运算符。 a+=3 —— a=a+3 b-=x+5 —— b=b-(x+5) x*=c-6 —— x=x*(c-6) y/=a*4 —— y=y/(a*4) k%=b-2 —— k=k%(b-2) 属于高效率运算符。
  • 66. ■赋值表达式 主要实现赋值运算的表达式。 一般形式: <变量>=<表达式> 如: a=5 y=2*x+3 a=a+1 不是衡等 作用:将右边表达式的值赋给左边的变量。 赋值表达式的值取左边变量的值。
  • 67. 赋值表达式右边的<表达式>可以是任何表达式,如: a=(b=5) 赋值表达式中包含赋值表达式 赋值运算符的优先级低于所有算术运算符,且是右结合性。 a=(b=5) 与 a=b=5 等效。
  • 68. 例:计算以下表达式的值: a=b=c=5 a=5+(c=6) a=(b=4)+(c=6) a=(b=4.5)+(c=6.5) (a、b、c为整型变量) a+=a-=a*a (设a的原值为3)
  • 69. 赋值表达式是C语言中的一个重要成分,在赋值表达式后加一分号就成为常用的赋值语句。如 y=2*x+1; 赋值表达式作为表达式的一种,可以出现在任何表达式中,如: x+2-(b/3-(a=k-5)+’b’
  • 70. 3.10 逗号表达式 逗号也是一种运算符,用它对两个表达式实现连接运算。 3+5,6+8 称逗号表达式。 逗号表达式的一般形式: 表达式1,表达式2 取表达式2的值作为整个逗号表达式的值。 如: a=3*5,a*4 逗号表达式的值为:60
  • 71. 一个逗号表达式又可以与另一个表达式组成一个新的逗号表达式,如: (a=3*5,a*4),a+5 因此,逗号表达式的一般形式可以扩展为: 表达式1,表达式2,表达式3,……, 表达式n 取表达式n的值作为整个逗号表达式的值。 逗号运算符的优先级最低,且是左结合性。 逗号运算符只起到连接作用,没有实际操作。
  • 72. 第四章 最简单的C程序设计 C 程序最基本的成分是语句 目前我们已掌握的语句: 变量说明语句: int a,b,c; 表达式语句: x+y; 特别地: a=5; 赋值语句
  • 73. 可以编写简单程序如: main() { int x,y; x=5; y=2*x*x+3*x-1; } 该程序语法上完整,但还缺少输出。
  • 74. ■数据的输出 用输出函数实现,其中的两种输出函数: 1.putchar 函数 (字符输出函数) 用于输出一个字符。 如: putchar (‘a’); putchar (100); char c=’b’; putchar (c);
  • 75.   例:输出单词Boy的完整程序: #include “stdio.h” 注意该语句的作用 main() { char a, b, c; a=’B’; b=’o’; c=’y’; putchar (a); putchar (b); putchar (c); }
  • 76. 2. printf函数(格式输出函数) 任意类型、任意格式、任意个数。 例如: int a=100,b=56; printf(“a=%d,b=%d”,a,b); 普通字符 格式说明 格式控制 输出表列 输出结果:a=100,b=56 “%” 后的字符称格式字符,不同格式字符对应不同的数据类型。
  • 77. d格式符:按整数格式输出 几种用法: %d 不指定宽度,按实际宽度输出 %md 按指定宽度输出,m为宽度 %ld 用于输出长整型数
  • 78. 例: int a=125,b=453; long c=65535; printf(“a=%d,b=%5d,c=%ld”,a,b,c); 输出结果: a=125,b= 453,c=65535
  • 79. %ld也可以按指定宽度输出: printf(“c=%8ld”,c); 输出结果:c = 65535 注意:格式字符的类型要与对应的输出对象的类型一致。
  • 80. c格式符:用于输出字符 char c=’A’; printf(“c=%c,%c”,c,’B’); 输出结果:c=A,B 输出对象既可以是字符变量、字符常量,还可以是整型表达式。  
  • 81. 如: int a=100; char b=’A’; printf(“\n%d,%c”,a,a); printf(“\n%c,%d”,b,b); 输出结果: 100,d A,65
  • 82. s格式符:用于输出字符串 %s 不指定宽度 %-ms 指定宽度,左靠齐 %ms 指定宽度,右靠齐 %m.ns 指定宽度m,只取左端n个字符, 右靠齐 %-m.ns 指定宽度m,只取左端n个字符, 左靠齐
  • 83. 例: printf(“1:%s”,”abcd”); printf(“2:%8s”,”abcd”); printf(“3:%-8s”,”abcd”); printf(“4:%8.3s”,”abcd”); printf(“5:%-8.3s”,”abcd”); 1:abcd2: abcd3:abcd 4: abc5:abc
  • 84. f格式符:按小数形式输出实数 %f 由系统指定宽度(6位小数) %m.nf 指定宽度m,小数位数n,右靠齐 %-m.nf 指定宽度m,小数位数n,左靠齐 注意:宽度包括符号和小数点。
  • 85. 例: float a=3.141592654,b=14.326795, c=-125.2468; printf(“\na=%f,b=%8.3f,c=%-10.2f”,a,b,c); 输出结果: a=3.141592,b= 14.326,c=-125.24
  • 86. 完整前面的程序:main() { int x,y; x=5; y=2*x*x+3*x-1; printf(“\n y=%d”,y); }
  • 87. 程序设计例: 编写程序计算如图中的电流I.         假设 U=220,R1=30,R2=60,R3=45 UIR1R2R3
  • 88. 算法设计: I=U/R1+U/R2+U/R3 程序设计: main() {   }I=U/R1+U/R2+U/R3;int U=220,R1=30,R2=60,R3=45; float I;printf(“\n I=%f”,I);
  • 89. 正确的程序: main() { int U=220,R1=30,R2=60,R3=45; float I; I=(float)U/R1+(float)U/R2+(float)U/R3; printf(“\n I=%f”,I); }
  • 90. 考虑通用: main() { int U,R1,R2,R3; float I; 输入 U,R1,R2,R3 I=(float)U/R1+(float)U/R2+(float)U/R3; printf(“\n I=%f”,I); }
  • 91. ■数据的输入 getchar函数(字符输入) #include “stdio.h” main() { char c; c=getchar(); 等待键盘输入 putchar(c); }
  • 92. scanf 函数(格式输入) 与printf函数相反。 用于输入若干任意类型的数据。 scanf(“%d%d%d”,&a,&b,&c); 格式控制 地址列表    
  • 93. scanf(“%d%d%d”,&a,&b,&c); 执行此函数时,等待从键盘输入三个整数给a,b,c 若从键盘输入 3 5 8 则系统即从键盘缓冲区取出这三个数分别赋给a,b,c 注意与printf的区别,注意格式的匹配
  • 94. 如: scanf(“%3d%2d%4d”,&a,&b,&c); 若从键盘输入123456789 a=123,b=45,c=6789 若想使a=12,b=5,c=100 则键盘输入应为: 12 5 100 方便的输入格式一般不 指定宽度, 如: scanf(“%d%d%d”,&a,&b,&c);
  • 95. 在键盘输入时,用分隔符把每个数据隔开,标准的分隔符是空格。 如:123 150 23 若想用逗号作分隔符,则: scanf(“%d,%d,%d”,&a,&b,&c); 不要随便使用普通字符,如使用: scanf(“a=%d,b=%d c=%d”,&a,&b,&c) 对应数据输入: a=123,b=150,c=23
  • 96. 前面的欧姆定律: main() { int U,R1,R2,R3; float I; scanf(“%d%d%d%d”,&U,&R1,&R2,&R3); I=(float)U/R1+(float)U/R2+(float)U/R3; printf(“\n I=%f”,I); }
  • 97. 求三角形面积#include “math.h” main() { float a,b,c,area,s; scanf ( “%f,%f,%f”, &a,&b,&c); s=1.0/2*(a+b+c); area=sqrt (s*(s-a)*(s-b)*(s-c)); printf(“\n area=%f”,area); } 使用数学函数
  • 98. 使用三角函数#include “math.h” main() { float x, y; scanf ( “%f”, &x); y=sin(x*3.1415926/180); 以弧度为单位 printf(“\n y=%f”,y); }
  • 99. 第五章   选择结构程序设计 对于如下的函数计算,算法上属于一个选择结构。 y= 用于实现选择结构的主要是if语句。 1/x 当x≠0时 10000 当x=0时
  • 100. if语句的最常见形式为: if(关系表达式)语句1; else 语句2; 如: if(x!=0) y=1/x; else y=10000; 其中 x!=0 就是一个关系表达式 != 就是一个关系运算符
  • 101. 5.1 关系运算符和关系表达式 1、关系运算符 用于进行比较运算的运算符。共有六种: < <= > >= = = != ■优先级与结合性: ① 前4种大于后两种。 ② 低于算术运算符而高于赋值运算符。 ③ 左结合性。
  • 102. 2、关系表达式 一般形式: 〈表达式〉〈关系运算符〉〈表达式〉 如:a>b a+b>b+c 经过关系运算后最终有一个值--关系表达式的值。 关系表达式的值只有 0(假)或1(真)
  • 103. 例:设 a=2, b=4, c=1 计算以下关系表达式的值: a>b a+b>b+c ‘a’>’b’ 可以是字符表达式 (x=2)>(y=5) 可以是赋值表达式 (a(b
  • 104. 例:假设 x=3, y=5, z=1, 计算以下关系表达式的值: x+z>y xz==x>z y>x>z (x==y-2)x+1
  • 105. 5.2 逻辑运算符和逻辑表达式 有时,只用一个简单的关系表达式无法完整地表达一个条件,如: y= 其中的条件需要用逻辑表达式来表达: x!=0 &&a!=0 &&就是一种逻辑运算符。 1/x+1/a 当x≠0,a≠0时 10000 其它
  • 106. 1、逻辑运算符 && 逻辑与 两个操作数都为真时&&运算结果为真。 || 逻辑或 两个操作数之一为真时即为真。 ! 逻辑非 (单目运算) 操作数为真(假)时为假(真)。 如:若 a=2, b=3, c=0 则: a
  • 107. ■优先级(由高到低): ! 逻辑非 算术运算符 关系运算符 &&逻辑与 ||逻辑或 赋值运算符 ■结合性:左结合性
  • 108. 2、逻辑表达式 实际上,前面所举例子即为逻辑表达式: a
  • 109. 例:设 a=2, b=3, c=0 , 计算以下表达式的值: a&&b b&&c a||c !a+c&&b+c !c+a==b||bc+10
  • 110. 对于逻辑表达式的两种基本技能: ① 逻辑表达式的计算。 ② 逻辑表达式的构造。
  • 111. 逻辑表达式的构造举例: a≥b≥c a和b之一为0,但不同时为0 a>=b>=c 5>=4>=3a>=b&&b>=ca==0&&b!=0 || a!=0&&b==0a*b==0a*b==0&&a+b!=0
  • 112. 对于 a==0 && b!=0 || a!=0 && b==0 a==0可以用!a代替 a!=0可以直接用a !a && b || a && !b 但必须是运算结果作为逻辑量的情况下。 y=(a!=0) 与 y=a 不等效
  • 113. 5.3 if语句 1、if语句的三种形式 ①  if(表达式)语句; 有一分支为空。 scanf(“%d”,&score); if(score>=60) printf(“pass”);
  • 114.   ② if(表达式)语句1; else 语句2; if(x!=0) y=1/x; else y=10000;
  • 115.   ③  if(表达式1)语句1; else if(表达式2)语句2; else if(表达式3)语句3; ┇ else 语句n; if(score==100) printf(“A”); else if(score>=90) printf(“B”); else if(score>=80) printf(“C”); else if(score>=70) printf(“D”); else if(score>=60) printf(“E”); else printf(“F”);
  • 116. 对于:     1/x 当x≠0时 10000 当x=0时 一般用: if(x!=0) y=1/x; else y=10000; 也可用: y=10000; if(x!=0) y=1/x; y=? y=1/x; if(x==0)y=10000
  • 117. 例:(习题5.5): x (x<1) 2x-1 (1≤x<10) 3x-11 (x≥10) y=
  • 118. main() { float x,y; scanf(“%f”,&x); if(x<1) y=x; else if(x<10) y=2*x-1; else y=3*x-11; printf(“\n y=%f”,y); }
  • 119. 说明: ① 语句中的表达式可以是任意表达式: if(x) y=1/x; else y=10000; ② 一个if结构不可分割: if(x) y=1/x; z=10; else y=10000; ③一个分支中包含多个语句时,要用{ }: if(a<0) { x=1; y=2;} else {x=10; y=20;}
  • 120. 分支程序设计举例(基本技巧和算法) 例:从键盘输入三个整数到变量a,b,c,输出其中最大的数。 两种典型算法: ①枚举法(将各种可能性枚举出来)。 ②选择法(先假设后判断更新)。
  • 121. 选择法 main() { int a,b,c,max; scanf(“%d,%d,%d”,&a,&b,&c); max=a; if(b>max) max=b; if(c>max) max=c; printf(“\n max=%d”,max); }}
  • 122. 例:从键盘输入三个整数到变量a,b,c,要求按从大到小的顺序输出。 两种典型算法: ①枚举法(将各种可能的排列枚举出来)。 ②换位法(将a,b,c中的数据换位)。
  • 123. 换位法 main() { int a,b,c,t; scanf(“%d,%d,%d”,&a,&b,&c); if(a
  • 124. 2、if语句的嵌套 在if语句中,又包含一个或多个if语句: if(score>=80) if(score>=90) printf(“A”); else printf(“B”); else if(score>=60) printf(“C”); else printf(“D”); 注意else与if的匹配
  • 125. 3、条件运算符 如果两个分支的内容都是给同一个变量赋值,则可用简单的条件运算符处理: if(a>b) max=a; else max=b; 可用: max=a>b ? a : b; 赋值运算符右边为一条件表达式。 条件表达式的一般形式: 表达式1 ? 表达式2 : 表达式3
  • 126. 条件表达式的执行过程: a>b ? a : b             优先级:低于关系运算符,高于赋值运算符。 结合性:右结合性。   表达式1条件表达式 取表达式3的值条件表达式 取表达式2的值 非00
  • 127. 例:求a,b,c中的最大值: max= a>b ? (a>c?a:c) : (b>c?b:c) ;
  • 128. 5.4 switch语句(多分支) 适用于根据一个表达式的值就可确定走哪个分支的情况。 switch(表达式) { 常量表达式1: 语句1 常量表达式2: 语句2 ┋ 常量表达式n: 语句n default: 语句n+1 }
  • 129. 例:成绩分档: switch(score/10) { case 10: printf(“A”); case 9: printf(“B”); case 8: printf(“C”); case 7: printf(“E”); case 6: printf(“F”); default : printf(“G”); } 注:应使用break.
  • 130. switch(score/10) { case 10: printf(“A”); break; case 9: printf(“B”); break; case 8: printf(“C”); break; case 7: printf(“E”); break; case 6: printf(“F”); break; default : printf(“G”); }
  • 131. 5.5 程序举例 (习题5.10) 有4个圆塔,圆心分别为:(2,2),(-2,2),(-2,-2),(2,-2),圆半径为1。这4个塔的高度为10m,塔以外无建筑物。今输入任一点的坐标,求该点的建筑高度(塔外的高度为0)。
  • 132. 算法设计: 条件“在某一圆内” : “在圆1内或在圆2内或在圆3内或在圆4内” 若设变量c1、c2、c3、c4分别代表是否在相应的圆内,则以上条件为: c1||c2||c3||c4   10 在某一圆内 0 在圆外 (x,y) h=
  • 133. c1=(x-2)2+(y-2)2≤1 c2=(x+2)2+(y-2)2≤1 c3=(x+2)2+(y+2)2≤1 c4=(x-2)2+(y+2)2≤1
  • 134. main() { int h,c1,c2,c3,c4; float x,y; scanf(“%f%f”,&x,&y); c1=(x-2)*(x-2)+(y-2)*(y-2)<=1; c2=(x+2)*(x+2)+(y-2)*(y-2)<=1; c3=(x+2)*(x+2)+(y+2)*(y+2)<=1; c4=(x-2)*(x-2)+(y+2)*(y+2)<=1; if(c1||c2||c3||c4) h=10; else h=0; printf(“\n h=%d”,h); }                    
  • 135. 第六章 循环控制 6.1 概述 所谓循环控制,就是如何实现循环结构的控制问题。有4种方法: ① 用goto语句和if构成循环。 ② 用while语句。 ③ 用do-while语句。 ④ 用for语句。
  • 136. 6.2 用goto语句和if语句构成循环 例: 对于计算 s=1+2+3+4+5+6+7+8+9+100 s1 is+i → s i+1 → i i≤10 输出s yn
  • 137. s=0; i=1; lable:s+=i; i++; if(i<=10) goto lable; printf(“%d”,s) ; 语句标号 无条件转向语句 goto 语句可以构造循环,但不主张用,因为它容易破坏结构化程序设计。 goto 语句可以构造循环, 但不主张用,因为它容易破 坏结构化程序设计。
  • 138. 6.3 while语句 while语句是专门用于实现循环控制的语 句之一。 其一般形式为: while (表达式) 语句 含义:当表达式的值为非0时,执行循环体,否则执行后续语句。语句关键 表达循环条件的表达式循环体 语句关键字
  • 139. 执行过程: while (表达式) 语句 表达式循环体0非0
  • 140. i≤10? s=0 i=1s=s+i i=i+1例:用while语句实现前面算法: main() { int i=1,s=0; while(i<=10) { s=s+i; i++; } printf(“\n %d”,s); } 注意与if语句的区别。 循环体中要有使循环条件趋于成立的条件yn
  • 141. while(i<=10) { s=s+i; i++; } 可简写为: while(i<=10) s+=i++;
  • 142. 非0(真)0(假)表达式循环体 6.4 do-while语句 do-while语句主要用于实现直到型循环。 其一般形式为: do 循环体 while(表达式); 执行过程:
  • 143. 真s=0 i=1i≤10?s=s+i i=i+1假例:用do-while语句实现前面算法: main() { int i=1,s=0; do s+=i++; while(i<=10); printf(“\n%d”,s); } 注意与while语句的区别。
  • 144. 假表达式2求解表达式1循环体求解表达式3真 6.5 for 语句 for语句是一种使用最为灵活, 并且是用得最多的循环控制语句, 其一般形式为: for(表达式1;表达式2;表达式3) 循环体 大体含义: 对于()的情况执行循环体内容。
  • 145. 例:用for语句实现前面的算法: s=0; for(i=1;i<=10;i++) s+=i; 标准形式表达式1 表达式2表达式3循环体可以理解: 循环变量i从初值1开始到终值10,步长为1,重复执行循环体。
  • 146. for语句的常见变化: s=0; i=1; for(;i<=10;i++)s+=i; s=0; for(i=1;i<=10;)s+=i++; s=0;i=1; for(;i<=10;)s+=i++; 省略表达式1省略表达式3省略表达式1和3
  • 147.   s=0; i=1; for(;;) { s+=i++; if(i>10) break; } s=10;i=10; for(;--i;) s+=i; 省略表达式2 表达式2是任意表达式
  • 148.   s=0; for(i=1,j=10;i
  • 149. 例:求n! n!=1*2*3…(n-1)*n 参照累加求和 main() { int i,n=5,s=1; for(i=1;i<=n;i++)s*=i; printf(“\n s=%d”,s); } 注意s的初值。 注意当n较大时的情况。 求和与连乘都是最常用的算法,要熟练掌握。
  • 150. 例:求自然数1-100中能被3整除的数之和。 main() { int i,s=0; printf(“\n %d”,s); } 求能被3整除但不被7整除的数之和? for(i=1;i<=100;i++) s+=i;for(i=1;i<=100;i++) if(i%3==0)s+=i;for(i=3;i<=100;i+=3) s+=i; for(i=3;i<=100;i+=3) if (i%7)s+=i;
  • 151. 例:求任意100个数中的最大值。 main() { int i,a,max; max=? for(i=1;i<=100;i++) { scanf(“%d”,&a); if(a>max) max=a; } printf(“\n max=%d”,max); } 循环体中没有引用循环变量。i的作用? 求任意个数中的最大值?for(i=1; ;i++) if(a==-9999)break;max=-32768;
  • 152. 外重循环内重循环执行200次要掌握多重循环执行的全过程6.6 循环的嵌套 循环体内又包含另一个完整的循环结构(多重循环)。 for(i=1;i<=10;i++) { for(j=1;j<=20;j++) { s+=i+j; } }
  • 153. 以上多重循环结构可以简写为: for(i=1;i<=10;i++) for(j=1;j<=20;j++) s+=i+j;
  • 154. 例:找出行号乘以列号等于100的座位。 main() { int i,j; for(i=1;i<=30;i++) for(j=1;j<=20;j++) if(i*j==100) printf(“\n%d,%d”,i,j); } 注意循环的关系
  • 155. 例:百钱买百鸡问题。 给定100块钱,要求正好买100只鸡,已知公鸡5元/只,母鸡3元/只,小鸡1元/3只,问公鸡、母鸡和小鸡应各买多少只? 若考虑用方程组: x+y+z=100 5x+3y+z/3=100 是一多解问题。
  • 156. 用测试法求解的程序: main() { int x,y,z; for(x=1;x<=100;x++) for(y=1;y<=100;y++) for(z=1;z<=100;z++) if(x+y+z==100&&5*x+3*y+z/3.0==100) printf(“\n%d,%d,%d”,x,y,z); }
  • 157. 程序可进一步简化为: main() { int x,y,z; for(x=1;x<=20;x++) for(y=1;y<=33;y++) { z=100-x-y; if(5*x+3*y+z/3.0==100) printf(“\n%d,%d,%d”,x,y,z); } } 用测试法求解问题的典型例子
  • 158. 测试法求解的程序设计有两个要点: ⑴通过循环列出所有可能的解。 ⑵对所有列出的可能的解进行条件测试。
  • 159. 例:判断一个数m是否为素数。 main() { int i,m; scanf(“%d”,&m); for(i=2;i
  • 160. 例:(习题6.6) 打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字的立方和等于该数本身。如:153是一水仙花数,因为 153=13+53+33。
  • 161. main() { int i,j,k,n; for(n=100;n<=999;n++) { i=? j=? k=? if(i*i*i+j*j*j+k*k*k==n) printf(“\n%d”,n); } } 通过循环列出n的所有可能的范围 i=n/100;k=n%10;j=n/10%10;
  • 162. main() { int i,j,k,n; for(i=1;i<=9;i++) for(j=0;j<=9;j++) for(k=0;k<=9;k++) { n=i*100+j*10+k; if(i*i*i+j*j*j+k*k*k==n) printf(“\n%d”,n); } } 通过循环列出i,j,k的所有可能的范围
  • 163. 例:(习题6.4) 求S=∑i! =1!+2!+3!+4!+…+19!+20!20i=1
  • 164. main() { int i,j; float t,s=0; for(i=1;i<=20;i++) { s+=?; } printf(“\n s=%f”,s); } 用两重循环实现s+=t;t=1; for(j=1;j<=i;j++) t*=j;
  • 165. 用递推法: 递推公式:ti=ti-1.i 求S=∑i! =1!+2!+3!+4!+…+19!+20!20i=1=1+∑(i-1)!.i = 1+∑ti-1.i 20i=220i=2
  • 166. main() { int i,j; float t=1,s=t; for(i=2;i<=20;i++) { t=t*i; s+=t; } printf(“\n s=%f”,s); }
  • 167. 例: 递推公式: s=∑—i!1120=— + — + — + — + …+ — 1! 2! 3! 4! 20!1 1 1 1 1ti= ti-1 / i
  • 168. main() { int i,j; float t=1,s=t; for(i=2;i<=20;i++) { t=t*i; s+=t; } printf(“\n s=%f”,s); }t=t/i;
  • 169. 习题6.3: s=a+aa+aaa+aaaa+aaaaa 2+22+222+2222+22222 递推公式:ti= ti-1·? ti= ti-1 * 10+a
  • 170. main() { int i,j,a=2; float t=a,s=t; for(i=2;i<=5;i++) { t=t*10+a; s+=t; } printf(“\n s=%f”,s); }
  • 171. 例:求方程 2x3+3x2-4x+1=0的根。 简单迭代法基本思想: 将原方程f(x)化为:x2=g(x1) x2=(2x13+3x12+1)/4 迭代公式
  • 172. 迭代过程: 假定一个x1x2=g(x1)不成立|x2-x1∣<εx1=x2输出x2成立
  • 173. main() { float x1,x2; scanf(“%f”,&x1); while(1) { x2=(2*x1*x1*x1+3*x1*x1+1)/4; if(fabs(x2-x1)<1e-6)break; ? } printf(“\n %f”,x2); } x1=x2;
  • 174. 牛顿迭代法基本思想: 牛顿迭代公式 f′(x1)=f(x1)/(x1-x2) x2=x1-f(x1)/ f′(x1) x1x2yxf (x1)f (x2)
  • 175. main() { float x1,x2,f1,f; scanf(“%f”,&x1); while(1) { f=2*x1*x1*x1+3*x1*x1-4*x1+1; f1=6*x1*x1+6*x1-4; x2=x1-f/f1; if(fabs(x2-x1)<1e-6) break; x1=x2; } printf(“\n %f”,x2); }
  • 176. 例:求定积分: 数值积分 f (x)abx∫—sinxxdxab等分nh=—b-an第i个矩形:x=?x=a+(i-1).h
  • 177. #include “math.h” main() { int n,i; float a,b,x,y,h,s=0; scanf(“%f%f%d”,&a,&b,&n); h=(b-a)/n; for(i=1;i<=n;i++) { x=a+(i-1)*h; y=sin(x)/x; s+=h*y; } printf(“\n s=%f”,s); }
  • 178. 第七章 数 组 7.1 数据结构与数组的概念 影响程序设计的因素除算法外还有数据结构。 ■数据结构概念 编写一个程序除了重视算法的设计外,还需重视数据类型的选择,即选择合适的数据类型来存放要处理的数据。在程序设计中,数据类型就称为数据结构,选择合适的数据类型实际上就是进行数据结构的设计。
  • 179. 在程序设计中有格言: 数据结构+算法=程序 说明数据结构与算法同等重要,算法依赖于数据结构,对于同一个问题的求解,可以采用不同的数据结构和不同的算法,对不同的数据结构有不同的算法,其复杂程度也会不同,选择合适的数据结构,可以降低算法的复杂程度。因此,在程序设计中应重视数据结构的设计。
  • 180. 例:求任意100个数中的最大值。 main() { int i,a,max; max=-32768 for(i=1;i<=100;i++) { scanf(“%d”,&a); if(a>max) max=a; } printf(“\n max=%d”,max); }用一个简单变量作为数据结构,合理,算法简单
  • 181. 对于三个数的排序: main() { int a,b,c,t; scanf(“%d,%d,%d”,&a,&b,&c); if(a
  • 182. ■数组的概念 int a[10] a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] 一组具有同样类型的数据的集合 统一用一个名字代表---数组名(代表一组数)数组元素下标数组名
  • 183. 数组中的各成员称数组元素,由数组名加下标唯一地确定。 将一组数用一个名字代表,便于管理。 只有一个下标的数组称为一维数组; 可有二维数组、三维数组、…、七维数组。
  • 184. 7.2 一维数组的定义和引用 ■定义 一般形式: 类型符 数组名[常量表达式];   int a[10]; float b[10]; 类型符 数组名 长度 作用:分配一组连续的内存单元
  • 185. 说明: ●数组必须先定义后使用。 ●数组名的命名规则与变量相同。 ●常量表达式表示元素的个数(长度),下标 从0开始。 ●常量表达式不能包含变量,即不允许作动态定义。
  • 186. ■引用 逐个引用其元素,不能进行整体引用。 引用的一般形式: 数组名[下标] 如:a[0]=50; a[1]=100; a[2]=a[0]+a[1]; 与 a2=a0+a1有根本性的区别:下标可变。
  • 187. 例:从键盘输入10个数。 用变量:(不方便) scanf(“%d%d%d%d%d%d%d%d%d%d”, &a0,&a1,&a2,&a3,&a4,&a5,&a6,&a7,&a8,&a9); 用数组: (灵活方便) for(i=0;i<10;i++) scanf(“%d”,&a[i]); 用循环控制输入个数和下标的变化。 注意下标的变化范围。
  • 188. ■初始化 在定义数组的同时给数组赋初值。 inta[10]={0,1,2,3,4,5,6,7,8,9}; int a[10]={0,1,2,3,4}; int a[]={0,1,2,3,4};
  • 189. ■应用举例 (1)对100个学生的分数统计最高分、最低分和平均分。 两种方法: 用变量作为存放初始数据的数据结构 用数组作为存放初始数据的数据结构
  • 190. main() { int i,a,max,min; float aver=0; max=0; min=100; for(i=0;i<100;i++) { scanf(“%d”,&a); if(a>max) max=a; if(a
  • 191. main() { int i,a[100],max,min; float aver=0; for(i=0;i<100;i++) scanf(“%d”,&a[i]); max=a[0]; min=a[0]; for(i=0;i<100;i++) { if(a[i]>max) max=a[i]; if(a[i]a[max]) max=i; if(a[i]
  • 192. (2)统计高于平均分的人数。 main() { int i,a,n; float aver=0; for(i=0;i<100;i++) { scanf(“%d”,&a); aver+=a; } aver/=100; n=0; for(i=0;i<100;i++) { scanf(“%d”,&a); if(a>aver)n++; } printf(“\n %d”,n); }用变量数据结构不合理
  • 193. main() { int i,a[100],n; float aver=0; for(i=0;i<100;i++) scanf(“%d”,&a[i]); for(i=0;i<100;i++) aver+=a[i]; aver/=100; n=0; for(i=0;i<100;i++) if(a[i]>aver)n++; printf(“\n %d”,n); } 用数组
  • 194. 1(3)对100个学生的分数统计出每分一档人数。 0 ? 1 ? 2  ?     3    ?     4    ? ┇ ┇ 99 ? 100 ? main() { int i,a;   for(i=1;i<=100;i++) { scanf(“%d”,&a);  } 输出 }int i,a,n[101]; for(i=0;i<101;i++) n[i]=0;n[a]++;
  • 195. 完整程序: main() { int i,a,n[101]; for(i=0;i<101;i++)n[i]=0;   for(i=1;i<=100;i++) { scanf(“%d”,&a); n[a]++;   } for(i=100;i>=0;i--) printf(“\n %3d:%3d”,i,n[i]); } 体会数组作为存放结果的数据结构时的优越性。
  • 196. 按10分一档统计? main() { int i,a,n[101]; for(i=0;i<101;i++)n[i]=0;   for(i=1;i<=100;i++) { scanf(“%d”,&a); n[a]++;   } } int i,a,n[11]; for(i=0;i<11;i++)n[i]=0;n[a/10]++;
  • 197. (4)对10个学生的分数按从小到大的顺序排序后输出。 两种典型的排序算法:选择法和起泡法。 选择法基本思想: 首先选择最小的数放在0位置,再在剩下的数中选择最小的数放在下一位置,┈┈,依次类推,共进行9次选择。 5 8 7 4 3 9 0 1 2 6
  • 198. 每次选择都要与其后的所有数进行比较换位。 5 8 7 4 3 9 0 1 2 6   ij
  • 199. main() { int a[10],i,j,t; for(i=0;i<10;i++)scanf(“%d”,&a[i]); for(i=0;i<9;i++) for(j=i+1 ;j<10 ;j++) if(a[i]>a[j]) { t=a[i]; a[i]=a[j]; a[j]=t; } for(j=0;j<10;j++) printf(“%3d”,a[j]); } 5 8 7 4 3 9 0 1 2 6
  • 200. 5 8 7 4 3 9 0 1 2 6   ij
  • 201. 先找最小值所在的位置,最后再换位: main() { inta[10],i,j,t,k; for(i=0;i<10;i++)scanf(“%d”,&a[i]); for(i=0;i<9;i++) { k=i; for(j=i+1;j<10;j++) if(a[j]
  • 202. 起泡法基本思想: 首先将所有数中的最大值“冒泡”到最后位置,再将剩下的数中的最大值“冒泡”到上一位置,┈┈,依次类推,共进行9次“冒泡”。 每次“冒泡”都是一种翻滚过程,即相邻两个数进行比较换位。 5 8 7 4 3 9 0 1 2 6
  • 203. main() { int a[10],i,j,t; for(i=0;i<10;i++)scanf(“%d”,&a[i]); for(j=1;j<=9;j++) for(i=0;i<10-j; i++) if(a[i]>a[i+1]) {t= a[i]; a[i]= a[i+1]; a[i+1]=t;} for(j=0;j<10;j++) printf(“%3d”,a[j]); } 要特别注意两个循环的范围。 
  • 204. ( (5) 循环移位 对一数列中的每个数向后移3个位置,最后3个数移到最前面。 5 8 7 4 3 9 0 1 2 6 1 2 6 5 8 7 4 3 9 0
  • 205. 用循环移位实现: 5 8 7 4 3 9 0 1 2 6 main() { int i,j,k,a[10]; for(i=0;i<10;i++)scanf(“%d”,&a[i]);   for(i=0;i<10;i++)printf(“%3d”,a[i]); }for(i=1;i<10;i++)a[i]=a[i-1];for(i=9;i>0;i--)a[i]=a[i-1];k=a[9];a[0]=k;for(j=1;j<=3;j++) { k=a[9];}
  • 206. (6)狐狸找兔子问题绕 围绕着山顶有10个洞,一只兔子和一只狐狸分别住在洞里,狐狸总想吃掉兔子;一天,兔子对狐狸说:你想吃掉我有一个条件,先把洞顺序编号,你从最后一个洞出发,第一次先到第一个洞找我,第二次隔一个洞找,第三次隔两个洞找,┈,依次类推,寻找次数不限,我躲在一个洞里不动,只要找到我你就可以饱餐一顿。狐狸一想只有10个洞,寻找次数又不限,那有找不到的呢?马上答应了条件,结果狐狸跑断了腿也没找到,请问兔子躲在哪个洞里?
  • 207. 12346789105
  • 208. 算法思想: 开辟数组,每个元素代表一个洞,并赋初值0,表示各个洞都还未找,然后按规律找,每找一个洞,对应的数组元素就赋值1,表示已找过,┈┈,最后根据数组元素值1与0来识别各洞是否已找过。
  • 209. main() { int i, k=10; int a[10]={0,0,0,0,0,0,0,0,0,0}; for(i=1;i<=10000;i++) { k=(k+i)%10; if(k==0) k=10; a[k-1]=1;   } for(i=0;i<10;i++) if(a[i]==0) printf(“%3d”,i+1); }
  • 210. 7.3 二维数组的定义和引用 ■定义 一般形式: 类型符 数组名[常量表达式] [常量表达式]; int a[3][4]; float b[5][10];   行 列 
  • 211. 二维数组的逻辑结构就如同一张表格: a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]   存放形式:按行存放。 a[0]a[1]a[2]
  • 212. 二维数组可以看作是一个特殊的一维数组,它的元素又是一个一维数组。C语言这样的处理方法在很多情况下显得很方便。 与一维数组相比,二维数组的定义多一个长度,其元素多一个下标。 在应用中,如果要处理的数据如同一数列,则可定义一维数组来存放;而如果要处理的数据如同一张表格,则应定义二维数组来存放。
  • 213. ■引用 引用形式:数组名[下标][下标] 如:a[0][3]=a[1][2]+a[2][3]; 其元素有两个下标。 例:从键盘输入12个数到二维数组中。 int a[3][4],i,j; for(i=0;i<3;i++) for(j=0;j<4;j++) scanf(“%d”,&a[i][j]); 需要用两重循环来控制两个下标的变化。
  • 214. 如果键盘输入的数据是:1 2 3 4 5 6 7 8 9 10 11 12, 则在数组中如何存放?两个循环换位呢?两个下标换位呢?   int a[3][4],i,j; for(i=0;i<3;i++) for(j=0;j<4;j++) scanf(“%d”,&a[i][j]);     for(j=0;j<4;j++) for(i=0;i<3;i++) scanf(“%d”,&a[i][j]); for(i=0;i<3;i++) for(j=0;j<4;j++) scanf(“%d”,&a[j][i]);
  • 215. 例:输入一个表格的数据到二维数组中,并找最 大值所在的位置 main() { int a[3][4],i,j,i1,j1; for(i=0;i<3;i++) for(j=0;j<4;j++) scanf(“%d”,&a[i][j]); i1=0; j1=0; for(i=0;i<3;i++) for(j=0;j<4;j++) if(a[i][j]>a[i1][j1]){i1=i; j1=j;} printf(“\n %d,%d”,i1,j1); }
  • 216. ■初始化 对二维数组赋初值的几种方法: int a[3][4]= {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int a[3][4]={{1},{5},{9}}; int a[3][4]={{1},{0,6},{0,0,11}}; int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int a[][4]={{0,0,3},{},{9,10}};
  • 217. ■ 举例 (1)矩阵的基本操作 二维数组的逻辑结构就如同一个矩阵,因此,矩阵操作都可用二维数组实现。 a11 a12 a13 … a1n a21 a22 a23 … a2n a31 a32 a33 … a3n … … … … am1 am2 am3 … amnA=假定M=3,N=4
  • 218. 求和: main() { int a[4][4],i,j,s=0; ┈┈┈ for(i=0;i<4;i++) for(j=0;j<4;j++) s+=a[i][j]; ┈┈┈ } 上三角?下三角?主对角线?for(i=0;i<4;i++) for(j=i;j<4;j++) s+=a[i][j]; for(i=0;i<4;i++) for(j=0;j<=i;j++) s+=a[i][j]; for(i=0;i<4;i++) for(j=i;j<=i;j++) s+=a[i][j]; for(i=0;i<4;i++) s+=a[i][i]; 1 2 3 4 5 6 7 8 10 11 12 13 14 15 16
  • 219. 非方阵转置:aij→bji main() { int a[3][4],b[4][3],i,j; ┈┈┈ for(i=0;i<3;i++) for(j=0;j<4;j++) b[j][i]=a[i][j]; ┈┈┈ } 1 2 3 4 5 6 7 8 9 10 11 121 5 9 2 6 10 3 7 11 4 8 12
  • 220. 方阵转置:aij∽aji main() { int a[3][3],i,j,t; ┈┈┈ for(i=0; i<3; i++) for(j=0; j<3; j++) {t=a[i][j]; a[i][j]=a[j][i]; a[j][i]=t;} ┈┈┈ } 1 2 3 4 5 6 7 8 9for(j=i+1; j<3; j++)
  • 221. 将矩阵中和值为最大的那一行元素与首行对换。 main() { int a[3][4],i,j,t,s,smax=-32768,row; ┈┈┈ for(i=0;i<3;i++) { s=0;for(j=0;j<4;j++)s+=a[i][j]; if(s>smax) {smax=s; row=i;} } for(j=0;j<4;j++) {t=a[0][j];a[0][j]=a[row][j]; a[row][j]=t;} ┈┈┈ }1 5 3 8 4 6 1 7 9 2 5 6
  • 222. 7.4 字符数组 用于存放字符的数组称字符数组。 字符数组的每一个元素存放一个字符。 字符数组的独特之处: (1)字符数组可以看作字符串变量。 (2)对字符数组可以进行某些整体操作。 (3)有专用的字符串处理函数。
  • 223. 1、将字符数组作为字符串变量 char c[10]; 给c分配10个字节的内存单元。 把c看作数组时,按数组元素的形式访问: c[0]=’a’; c[1]=’b’; c[2]=’c’; c[3]=’d’; a b c d char c[10]={‘a’,’b’,’c’,’d’} ; 也属于字符赋初值的形式。
  • 224. 如果把字符序列看作一个整体(字符串),则c就可看作是存放这个字符串的串变量;但必须在字符序列后加上“字符串结束标志”后,才能成为完整的字符串。 如:c[4]=’\0’; 或 c[4]=0;   a b c d \0 也可以按字符串形式初始化: char c[10]=”abcd”; char c[]=”abcd”; 分配5个字节
  • 225. 2、对字符数组的整体操作 对字符数组的有些操作可以整体进行,如输入输出。 for(i=0;i<10;i++) printf(“%c”,c[i]); 对数组元素操作 printf(“%s”,c); 整体操作 注意以上两种操作有区别。 可将前者改为: for(i=0;c[i]!=’\0’;i++)printf(“%c”,c[i]);
  • 226. 对于输入: for(i=0;i<10;i++) scanf(“%c”,&c[i]); 对数组元素操作 scanf(“%s”,c); 整体操作 对字符数组输入输出可以整体进行,但不允许整体赋值: char c[10]=”abcd”,x[10]; x=c; 不允许
  • 227. 对于二维字符数组,可以看作是一维的字符串数组。 例:从键盘输入10个人的名字到计算机: main() { int i; char name[10][20]; 10个元素的一维字符串数组 for(i=0;i<10;i++) scanf(“%s”,name[i]); … } 只给一个下标
  • 228. 3、字符串处理函数 c语言的函数库中提供了一系列专用于字符串处理的函数,需要时可直接调用。 (1)puts(字符串) 用于输出字符串。 其中字符串可以是字符串常量,也可以是字符数组。 例: char str[]=”China”; puts(str); puts(”China”); 两个输出等效
  • 229. (2)gets(字符数组) 用于从键盘输入一个字符串到字符数组中。 函数返回字符数组的起始地址。 例: char str[10]; gets(str); 执行该函数调用时,计算机等待输入字符串
  • 230. (3)strcat(字符数组,字符串) 用于将字符串连接到字符数组的后面。 其中字符串可以是字符串常量,也可以是字符数组。 例: char a[10]=”abcd”, b[10]=”xyz”; strcat(a,b); 与strcat(a,”xyz”)等效 puts(a); 输出结果是:abcdxyz
  • 231. (4) strcpy(字符数组,字符串) 用于将字符串拷贝到字符数组中。 其中字符串可以是字符串常量,也可以是字符数组。 例: char a[10], b[10]=”abcdef”; strcpy(a,b); 与strcpy(a,”abcdef”)等效 不能用a=b 赋值 puts(a); 输出结果是:abcdef
  • 232. (5) strcmp(字符串1,字符串2) 用于比较两个字符串的大小。 比较结果通过函数的返回值体现: 字符串1=字符串2时:返回0。 字符串1>字符串2时:返回一正整数。 字符串1<字符串2时:返回一负整数。
  • 233. 两个字符串之间谁大谁小取决于最先有差异的两个字符的ASCII代码的大小。 如: strcmp(“abcde”,”abcde”); 返回0 strcmp(“abcdefgh”,”abcxyz”);返回负整数 strcmp(“a”,”ABCD”); 返回正整数
  • 234. 例:从键盘输入两个字符串,输出其中大的一个。 #include “string.h” main() { char a[10],b[10]; gets(a); gets(b); if(strcmp(a,b)>0) puts(a); 不能用a>b else puts(b); } 使用字符串处理函数需要包含头文件string.h
  • 235. (6) strlen(字符数组) 测试字符串的实际长度(从返回值得到)。 (7) strlwr(字符串) 将字符串中的大写字母全改为小写字母。 (8) strupr(字符串) 将字符串中的小写字母全改为大写字母。 注意:在使用字符串处理函数时,别忘了将头文件string.h包含进去。
  • 236. 4、字符串操作举例 (1)从键盘输入一字符串到数组a中,再拷贝到数组b中(不用库函数)。 main() { char a[50],b[50]; int i; scanf(“%s”,a); for(i=0; a[i]; i++) b[i]=a[i]; b[i]=0; printf(“%s”,b); }
  • 237. (2)从键盘输入两个字符串到数组a和b中,在将b中的内容连接到a中(不用库函数)。 main() { char a[50],b[50]; int i,j; scanf(“%s%s”,a,b); for(i=0; a[i]; i++); for(j=0; b[j]; j++) a[i++]=b[j]; a[i]=0; printf(“%s”,a); }
  • 238. (3)从键盘输入一字符串,并将其中的大写字母改成小写字母后输出(不用库函数)。 main() { char a[50]; int i; scanf(“%s”,a); for(i=0; a[i]; i++) if(a[i]>=’A’&&a[i]<=’Z’) a[i]+=32; printf(“%s”,a); }
  • 239. 第八章 函数 8.1 概述 C语言的程序除主函数外,还可以有若干个其他函数—块状结构。 一般把其中相对独立的算法和功能定义成一个独立的函数,以供需要的地方调用。 优点: (1) 减少代码的重复现象。 (2) 便于分工合作。 (3) 便于阅读。 (4) 便于独立算法的代码移植。
  • 240. 8.2 函数的定义和调用 举例说明: 对于求两个数中的最大值,有三个步骤: (1)从键盘输入两个数给a和b。 (2)求a和b中的最大值。 (3)输出结果。 把求最大值的算法部分定义成一个独立的函数:
  • 241. 函数类型 函数名 函数参数(形参) int max(int x,int y) main() { int z; { int a,b,c; if(x>y)z=x; scanf(“%d%d”,&a,&b); else z=y; c=max(a,b); return(z); printf(“%d”,c); } } x y z a b c35
  • 242. 说明: (1)程序由两个函数组成,它们逻辑上相互独立(功能、变量)。 (2)程序的执行总是从主函数开始,主函数总是被执行一次,其他函数只有在被调用时才获得控制。 (3)函数调用有两个作用:转移控制权和传递参数。 (4)return的作用也有两个:交回控制权和返回结果。 (5)实参可以是常量、变量或表达式,但类型要一致。
  • 243. 定义一个函数除考虑算法外就是:如何设计函数的参数,通过何种途径交回结果。 例:求自然数1—100中的素数之和。 ? prime( ? ) main() { int i; { int i,s=0; for(i=2;i
  • 244. 例:求5!+7!+4!的值。 ? fac( ? ) { int i,s=1; for(i=1;i<=n;i++) s*=i; return(s); } main() { printf(“\n%d”,fac(5)+fac(7)+fac(4)); } int nint
  • 245. main() { float s,fac(); 对被调函数声明 s=fac(5)+fac(7)+fac(4); printf(“\n%f”,s); } float fac(int n) { int i; float s=1; for(i=1;i<=n;i++) s*=i; return(s); } 不需声明的情况:int char 主调函数在后
  • 246. 若被调用的函数是库函数,则应用#include命令将所调用函数的有关信息包含进来,如: 例: #include “math.h” main() { float x,y; scanf(%f”,&x); y=sin(x); printf(“%f”,y); }
  • 247. 8.3 函数的嵌套调用 C语言的函数定义虽然相互平行、相互独立的,但可以嵌套调用,形如: 主函数 函数A 函数B
  • 248. 例:求多项式 S= ∑i!+ ∑i!+ ∑i!的值。 float fac(int n) {…} ? sum( ? ) { int i; float s=0; for( i=? ) s+=fac(i); return(s); } main() { printf(“%f”,sum(1,5)+sum(7,11)+sum(15,20); } 157111520int a, int bfloati=a;i<=b;i++
  • 249. 8.4 函数的递归调用 在函数调用的过程中,出现直接或间接地调用该函数本身。如: f1() f2() f3() { { { f1(); f3(); f2(); } } } 直接 间接 递归调用 递归调用
  • 250. 在实际应用中,有些问题既可用递归实现,也可不用递归(如求n!); 也有些问题非有递归不可(如汉诺塔问题);不少问题使用递归显得很方便。 用递归方法求n! : 递推公式: 1 当n=0或n=1时 n!= n(n-1)! 当n>1时
  • 251. float fac(int n) main() { float f; { float f; if(n==0||n==1) f=1; f=fac(4); else f= ? printf(“\n%f”,f); return(f); } } 递归调用的执行过程: n*fac(n-1);
  • 252. 主函数 函数fac 函数fac 函数fac 函数fac 问题:4个return的执行顺序?后进先出! f=fac(4); n=4 if(n==0||n==1)f=1; else f=n*fac(n-1); return(f) n=3 if(n==0||n==1)f=1; else f=n*fac(n-1); return(f )n=2 if(n==0||n==1)f=1; else f=n*fac(n-1); return(f) n=1 if(n==0||n==1)f=1; else f=n*fac(n-1); return(f)
  • 253. 8.5 数组作为函数的参数 当要传递的参数较少时,用简单变量作为函数的参数是方便的,但当要传递的参数是批量时,需要用数组作为函数的参数。
  • 254. 例:编写函数,求100个数的平均数。 float aver(int a[100]) { int i; float s=0; for(i=0;i<100;i++) s+=a[i] ; return(s/100); } main() { int x[100]; float av; 输入x; av=aver(x); }对应的实参也应为数组 型参数组的长度可省略 a[]
  • 255. 通用函数考虑: float aver(int a[],int n) main() { int i; float s=0; { int x[100],n=100; for(i=0;i
  • 256. 例:阅读程序: main() swap(int a, int b) { int a=3,b=5; { int t; swap(a,b); t=a; a=b; b=t; printf(“\n%d,%d”,a,b); printf(“\n%d,%d”,a,b); } } a b a b 35
  • 257. 以下程序的运行结果是 ? main() f ( int b[], int x) { int a[2]={2,4}, x=5; { x++; f (a,x); b[0]+=2; printf(“%d,%d,%d”, b[1]+=3; x,a[0],a[1]); } } A) 5,2,4 B) 6,4,7 C) 6,2,4 D) 5,4,7
  • 258. 例:排序。 void sort(int a[], int n) { int i,j,t; for(i=0;ia[j]){t=a[i];a[i]=a[j]; a[j]=t;} } main() { int x[5],i; for(i=0;i<5;i++) scanf(“%d”,&x[i]); sort(x,5); for(i=0;i<5;i++) printf(“%4d”,x[i]); }xa
  • 259. 例:方阵转置(二维数组情况)。 at(int a[3][3]) x a { int i,j,t; for(i=0;i<3;i++) for(j=i+1;i<3;i++) { t=a[i][j]; a[i][j]=a[j][i]; a[j][i]=t;} } int x[3][3]; at(x); 行长不等情况?通用函数的处理?
  • 260. 通用函数,按一维数组处理: at(int a[],int n) { int i,j,t; for(i=0;i
  • 261. 8.6 局部变量和全局变量 1、局部变量 在函数内定义的变量称局部变量。 局部变量只在该函数内使用。 float f1(int x) int f2() main() { int i,j; { int a,b,c; { int m,n; ┊ ┊ { int i,j; ┊ ┊ ┊ } } } }
  • 262. 说明: (1)任何函数(包括主函数)内定义的变量都是局部变量。 (2)不同函数内定义的变量即使同名也互不干扰。 (3)复合语句中定义的变量只在该复合语句中有效。
  • 263. 2 、  全局变量 在函数以外定义的变量,也称外部变量。 全局变量可以为为本文件中其它函数所共用。它的作用范围是从定义变量的位置开始到本源文件结束。
  • 264. int p=1,q=5; float f1(int a) { int b,c; ┊ } char c1,c2; char f2(int x,int y) { int i,j; ┊ } main() { int m,n; ┊ }全局变 量c1,c2 的作用 范围 全局 变量 p,q的 作用 范围
  • 265. 全局变量主要用于作为不同函数间数据传递的桥梁。 例:编写一个函数,求n个数中的最大值、最小值和平均值。并编写主函数完成:输入100个数,调用该函数进行统计,输出结果。
  • 266. int max,min; float aver(int a[],int n) { int i; float s=0; for(i=0;imax)max=a[i]; if(a[i]
  • 267. 分析不用全局变量的情况: float aver(int a[],int n) { int i; float s=0; int max,min; for(i=0;imax)max=a[i]; if(a[i]
  • 268. int max,min; float aver(int a[],int n) { int i; float s=0; for(i=0;imax)max=a[i]; if(a[i]
  • 269. 阅读程序,给出运行结果: int a=4,b=5,c=6; int f(int a,int b) { a/=2; c+=b-a; return(a+b+c); } main() { int a=2,d; d=f(a+2,a+b); printf(“\n %d,%d,%d,%d”, a,b,c,d); } 4526abcabad4721120
  • 270. 8.7 变量存储类别 1、动态存储方式和静态存储方式 静态存储方式:在程序运行期间分配固定存储单元的方式。 动态存储方式:在程序运行期间根据需要动态分配存储单元的方式。变量变量局部变量动态存储方式变量全局变量静态存储方式变量空间角度生存期角度
  • 271. 存放在静态存储区的变量:静态存储方式。 存放在动态存储区的变量:动态存储方式。 程序区 静态存储区 动态存储区 程序开始运行时分配空间,运行结束时释放 主要存放全局变量和静态局部变量根据需要动态分配,动态释放的区域 主要存放动态局部变量和现场保护等 用户区
  • 272. 2、局部变量的存储方式 每个局部变量在定义时可以指定其存储方式,即对每个局部变量的定义除定义其数据类型外,还应定义其存储方式。定义存储方式用auto(自动的)static(静态的)。如: int f1() int f2() { auto int a=1; {static int b=1; ┊ ┊ } } 缺省时为auto
  • 273. 动态局部变量在函数调用时分配、赋初值,调用结束时释放。 静态局部变量在第一次调用时分配、赋初值,调用结束时不释放,其单元及其值仍保留,下次调用时不重新分配,不重新赋初值。 在实际应用中,如果希望在函数调用结束后仍保留某个局部变量的值给下次调用时使用,则可定义该变量为静态的。
  • 274. 例:编写一个函数计算: y=2x-1 第一次计算 3x+1 第二次计算 4x+2 其他
  • 275. float f ( float x) main() { float y; { static int n=1; printf(“%f”,f(2.0)); if(n==1)y=2*x-1; printf(“%f”,f(1.0)); else if(n==2)y=3*x+1; printf(“%f”,f(3.0)); else y=4*x+2; } n++; return(y); }
  • 276.        3 、全局变量的存储方式 全局变量都是静态存储方式,不允许用auto来定义全局变量。 可以用static来定义全局变量。如: static int x; 静态全局变量(内部的) int y; 非静态全局变量(外部的)
  • 277. 对于非静态全局变量,其它文件中的函数只要用extern加以外部说明,就可以访问。 而对于静态全局变量,只局限在本文件中的所有函数访问,其它文件中的函数即使用extern加以外部说明,也不能访问。
  • 278. int y; main() { y=0; } int f1() { y=2; } extern int y; int f3() { y=10; } int f4() { y=100; } static int y;
  • 279. 8.8 内部函数和外部函数 用extern加以定义的函数称外部函数 。 用extern加以定义的函数称外部函数 。 extern int f1() static int f2() { { ┊ ┊ } } 缺省为外部函数。 外部函数可以为其他文件中的函数所调用。 内部函数只为本文件中的函数所调用(保护)。
  • 280. main() { f3(); 允许 } int f1() { f4(); 不允许 }extern int f3() { f4(); 允许 } static int f4() { }
  • 281. 第九章 编译预处理 C语言提供了一些以#开头的指令,如: #define #include 等。 这些指令是在编译以前就事先进行处理的,因而称为“编译预处理”指令。 可以用编译预处理指令实现以下三种功能: 1) 宏定义 2) 文件包含 3) 条件编译
  • 282. 9.1 宏定义 1、不带参的宏定义 用一个指定的标识符(宏名)代表一个字符串。 一般形式: #define 标识符 字符串 如:#define PI 3.1415926 有了这一宏定义后,程序中凡是用到3.1415926的地方都可以以宏名PI出现。
  • 283. 例:定义一个宏名来代表一个参数。 #define PI 3.1415926 main() { float r=2,c,s; c=2*PI*r; s=PI*r*r; ┈} 系统在对程序进行编译以前,首先将所有的编译预处理指令进行预处理,对本例来说,就是将程序中所有的宏名PI还原成3.1415926, ——宏展开。
  • 284. 例:定义一个宏名来代表一个数据个数。 #define N 100 main() { int a[N],i; float s=0; for(i=0;i
  • 285. 例:定义一个宏名来代表一个计算公式。 #define PI 3.1415926 #define AREA PI*r*r main() { r=3,s; s=AREA; printf(“\n %f”,s); } 注意:系统对宏定义的预处理是一个字符串的还原过程,不要把宏名看成一个整体。
  • 286. 注意以下程序的运行结果: #define F x+y main() { int x=3,y=5,z; z=2*F; printf(“\n %d”,z); }
  • 287. 2、带参的宏定义 宏定义也可以带参数,其一般形式为: #define 宏名(参数表) 字符串 如:#define S(a,b) a*b area=S(3,2); 定义宏名S代表矩形面积,参数a,b为边长。 宏展开过程:
  • 288. 9.2 文件包含处理 在一个源文件中将另一个源文件的内容包含进来。 文件包含指令的一般形式: #include “文件名” 或 #include <文件名>
  • 289. #include “file2.c”A file1.cA BBfile1.cfile2.c
  • 290. 例: #include “math.h” main() { float a,b,c,s,area; scanf(“%f%f%f”,&a,&,&c); s=0.5*(a+b+c); area=sqrt(s*(s-a)*(s-b)*(s-c)); printf(“\n %f”,area); }
  • 291. #include “f.h” main() { printf(“\n%f”,f1(3.0)); printf(“\n %f”,fac(10)); } float f1(float r) { return(PI*r*r); } float fac(int n) { int i; float s=1; for(i=1;i<=n;i++) s*=i; return(s); }#define PI 3.1415926 float f1(float r); float fac(int n); f.cf.h
  • 292. 第十章 指针 指针:C的一个重要概念、重要特色。它使C具备了强大的功能,使C成为程序设计语言之首。正确而灵活地运用它,就可以方便地处理很多其它高级语言所不能处理的问题。 不掌握指针等于没有掌握C语言的精华。
  • 293. 10.1 指针的概念 简单地说,指针就是地址。 要掌握指针的概念就必须弄清: ■内存地址概念? ■变量与地址的关系? ■如何通过地址进行变量的存取?
  • 294. 说明例: 内存用户数据 1000 3 i 1002 6 j 1004 9 k 对变量值的存取总是按地址进行的----直接访问。 int i,j,k; i=3; j=6; k=i+j;程序经编译后,变量名就不复存在,以地址对应。
  • 295. 也可以采用“间接访问”方式: 先将变量i的地址存放到另一变量p1中,要访问i时,先取出p1的内容(变量i的地址),再去访问该地址所对应的内存单元中的内容(变量i的值)。
  • 296. 内存用户数据 1000 3 i 1002 6 j 1004 9 k 2000 1000 p1 2004 1002 p2 int i,j,k; i=3; j=6; k=i+j; int *p1, *p2; p1=&i; p2=&j;
  • 297. 在以上概念的基础上对指针下定义: 变量的地址就是该变量的指针。 存放地址的变量称指针变量。 p1是指向变量i的指针变量。 10001002100410001002ijkp1p2
  • 298. 10.2 变量的指针 变量的指针 指针变量 指向变量的指针变量 用“*”代表“指向” 如*p1代表它所指向的变量i,同一内存单元。 以下两个语句等价: i=3; 直接访问 *p1=3; 间接访问
  • 299. 内存用户数据 1000 3 i 1002 6 j 1004 9 k 2002 1000 p1 2004 1002 p2 int i,j,k; i=3; j=6; k=i+j; int *p1, *p2; p1=&i; p2=&j; *p1=3;
  • 300. ■指针变量的定义 指针变量也必须先定义后使用。 int *p1; 注意: ① *表示该变量为指针变量,但变量名是p1。 ② 一个指针变量只能指向同一类型的变量。 int i,*p1; float a; p1=&i; 合法 p1=&a; 不合法
  • 301. ■指针变量的引用 两种用法: ①用地址运算符& p1=&i; ②用指针运算符* (实行间接访问) *p1=100; k=*p1; 注意:指针变量只能放地址(指针)。 p1=100; 不允许
  • 302. 例: main() { int a=100,b=10; int *p1,*p2; 定义指针变量,尚无具体指向 p1=&a; p1指向a p2=&b; p2指向b printf(“\n %d,%d”,a,b); printf(“\n %d,%d”, *p1,*p2); } 注意:要区别定义和引用中的“*”
  • 303. 要特别注意以下用法的后果: int *p1; *p1=100;
  • 304. 例:输入a和b两个整数,按先大后小的顺序输出 main() { int a,b,*p1, *p2, *p; scanf(“%d,%d”,&a,&b); 1000 5 a p1=&a; p2=&b; 1002 9 b if(a
  • 305. 重要概念: 只要得到某变量的地址(指针),就可通过指针而不需通过逻辑名来访问该变量。 手段:用指针变量保存变量的地址。
  • 306. ■指针变量作为函数的参数 可将指针变量作函数的参数,接受实参地址,获得具体指向,进而通过指针变量间接访问主调函数的变量。
  • 307. swap(int *p1, int *p2) { int t; t=*p1;  *p1=*p2; *p2=t; 1000 5 a } 1002 9 b main() { int a,b; scanf(“%d,%d”,&a,&b); 2000 p1 if(a
  • 308. swap(int p1, int p2) 不用指针变量情况 { int t; t=p1;   p1=p2; p2=t; 1000 5 a } 1002 9 b main() { int a,b; scanf(“%d,%d”,&a,&b); 2000 p1 if(a
  • 309. 重要概念:使用指针变量作函数参数,被调函数可以通过主调函数给定的地址去操作主调函数中的局部变量。 (可用于传递多个结果)
  • 310. 例:编写函数,求一元二次方程的两个实根。 #include “math.h” ? root(float a,float b,float c, ) { float d; d=b*b-4*a*c; if(d<0||a==0)return(0); ? =(-b+sqrt(d))/2/a; ? =(-b-sqrt(d))/2/a; return(1); }float *x1, float *x2 *x1 *x2 int main() { int k; float a,b,c,xa,xb; scanf(“%f,%f,%f”,&a,&b,&c); k=root(a,b,c,&xa,&xb); if(k) printf(“\n %f,%f”,xa,xb); }
  • 311. 例:求n个数的最大值、最小值和平均值。 float aver(int a[], int n, int *max, int *min) { int i; float s=0; *max=a[0]; *min=a[0]; for(i=0;i*max) *max=a[i]; if(a[i]<*min) *min=a[i]; } return(s/100); } main() { int a[100],i,max,min; float av; for(i=0;i<100;i++) scanf(“%d”,&a[i]); av=aver(a,100,&max,&min); printf(“ %d,%d,%f”,max,min,av); }
  • 312. 10.3 数组的指针 数组有一个首地址: 数组的指针。 每个数组元素也都有地址: 数组元素的指针。 532168742000200220042006
  • 313. ■指向数组元素的指针变量 int a[10],*p; p=a; 指向数组 p=&a[0]; 指向数组元素 51247680392000200220042006p
  • 314. ■通过指针引用数组元素 p=&a[0]; p指向a[0] *p=1; 等效于a[0]=1; 即可通过p来访问a[0] 也可以通过p来访问其它元素: *(p+1)=3; 等效于a[1]=3; 其中p+1指向a[1] 注意:p+1不是地址加1,而是加一个数据类型单 位。
  • 315. 一般地,当p指向a[0]时: p+i ∽ a+i ∽ &a[i] *(p+i) ∽ *(a+i) ∽ a[i] ∽ p[i] 即以下几个语句等效: a[i]=10; *(p+i)=10; *(a+i)=10; p[i]=10;
  • 316. 例:从键盘输入10个数到数组a: int a[10],i,*p=a,s=0 for(i=0;i<10;i++) scanf(“%d”,&a[i]); for(i=0;i<10;i++) scanf(“%d”,a+i); for(i=0;i<10;i++) scanf(“%d”,p+i);
  • 317. 累加求和的各种用法: for(i=0;i<10;i++)s+=a[i]; for(i=0;i<10;i++)s+=*(a+i); for(i=0;i<10;i++)s+=*(p+i); for(i=0;i<10;i++)s+=p[i]; for(i=0;i<10;i++)s+=*p++; 等效于*(p++) for(p=a;p
  • 318. 使用指针变量访问数组时,要特别注意指针变量的当前值。 注意下例:   main() { int a[10],*p=a,i; for(p=a;p
  • 319. ■数组名作为函数参数 有了指针概念的基础上,重新回顾数组名作为函数参数时,数据的传递情况: 例:将数组a中的n个数按相反顺序存放。
  • 320. int inv(int x[], int n) { i,j,m,t; main() m=(n-1)/2; { for(i=0;i<=m;i++) int a[10],i; { j=n-1-i; 输入a t=x[i]; inv(a,10); x[i]=x[j]; 输出a x[j]=t; } } } a与x共用同一片内存单元ax
  • 321. int inv(int *x, int n) 指针变量作函数参数时的传递情况 { i,j,m,t; main() m=(n-1)/2; { for(i=0;i<=m;i++) int a[10],i; { j=n-1-i; 输入a t=x[i]; inv(a,10); x[i]=x[j]; 输出a x[j]=t; } } 下标法 } a10001000x{ j=n-1-i; t=*(x+i); *(x+i)= *(x+j); *(x+j)=t; } 指针法
  • 322. 进一步优化: int inv(int *x, int n) { main() int *i=x,*j=x+n-1,t; { for(;i
  • 323. 例:选择法排序函数 void sort(int *a, int n) { int i,j,t; for(i=0;ia[j]){t=a[i]; a[i]=a[j]; a[j]=t;} } 只将形参改为指针变量,仍按下标法使用 void sort(int *a, int n) { int i,j,t; for(i=0;i*(a+j)) {t=*(a+i); *(a+i)= *(a+j); *(a+j)=t;} } 按指针法使用
  • 324. 进一步优化: void sort(int *a, int n) { int *i, *j,t; for(i=a;i*j) {t=*i; *i=*j; *j=t;} } main() { int a[10],j; for(j=0;j<10;j++)scanf(“%d”,a+j); sort(a,10); for(j=0;j<10;j++)printf(“%5d”,a[j]); }
  • 325. 分段排序? main() { int a[10],j; for(j=0;j<10;j++)scanf(“%d”,a+j); for(j=0;j<10;j++)printf(“%5d”,a[j]); }sort(a,5);sort(a+5,5);
  • 326. ■指向多维数组的指针和指针变量 从本质上说,多维数组的指针与一维数组的指针相同,但在概念上和使用上,多维数组的指针要复杂些。 以二维数组的指针为例:
  • 327. ●二维数组的地址 : 一维:a,&a[i],a+i 二维: a,&a[i][j],a+i (行指针),a[i](特殊的一维数组元素,列指针),a[i]+j 358724691604int a[3][4] 100010081016a+0a+1a+2a[0]a[1]a[2]讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a+i); 输入数据:1 2 3 讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a[1]+i); 输入数据:1 2 3 讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a[1]+i+2); 输入数据:1 2 3 讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a+i+1); 输入数据:1 2 3
  • 328. 注意:指针运算符*作用在行指针上的结果仍是指针----列指针; *作用在列指针上的结果---具体元素。 *(a+0),*(a+1),*(a+2) ——仍是地址。*(a+i) *(a[0]),*(a[1]),*(a[1]) ——具体元素值。*(a[i]) *(a+i)+j 也是地址,但要区别: (a+i)+j——行指针 (a+1)+1 ? *(a+i)+j——列指针 *(a+1)+1 ? 100010081016a+0a+1a+2a[0]a[1]a[2]*(a+1)
  • 329. 如果要通过a+i形式的地址访问数组元素的具体内容,则: *(*(a+i)) 或 *(*(a+i)+j) 如:*(*(a+1)) —— a[1][0] *(*(a+1)+2) —— a[1][2] 讨论: *(a+2) *(*(a+1)+3) *(a[1]+1) *(*(a+1)+5)
  • 330. 例:求数组a的所有元素之和。 可有多种用法: for(i=0;i<3;i++) for(i=0;i<3;i++) for(j=0;j<4;j++) for(j=0;j<4;j++) s+=a[i][j]; s+=*(a[i]+j); for(i=0;i<3;i++) for(j=0;j<4;j++) s+=*(*(a+i)+j);
  • 331. ( ●指向二维数组的指针变量 同样可使一个指针变量p指向二维数组a,再通过p访问数组元素。 main() { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int i,k,*p; p=a; k=*p;? k=*(p+2);? for(p=a;p
  • 332. 例:求矩阵的上三角元素之和。 main() { int a[3][4],*p,i,j,s=0; 输入a p=a; for(i=0;i<3;i++) for(j=i;j<4;j++) s+=p[i*4+j]; 或 s+=*(p+4*i+j) printf(“\n %d”,s); }
  • 333. ●指向由m个元素组成的一维数组的指针变量 可以这样定义一个指针变量: int (*p)[4] 表示p为指向由4个元素组成的行指针变量。 当p=a时,可通过p引用a[i][j]: p[i][j] 或 *(*(p+i)+j)
  • 334. 例:求矩阵的上三角元素之和。 main() { int a[3][4],(*p)[4],i,j,s=0; 输入a p=a; for(i=0;i<3;i++) for(j=i;j<4;j++) s+=p[i][j]; 或 s+=*(*(p+i)+j) printf(“\n %d”,s); }
  • 335. ●多维数组的指针作函数参数 用于接受实参数组地址的形参可用两种:行指针和列指针。 以方阵转置为例: void at(int (*a)[3]) 用行指针 { int i,j,t; 缺点:不通用 for(i=0;i<3;i++) for(j=i+1;j<3;j++) { t=a[i][j]; a[i][j]=a[j][i]; a[j][i]=t; } }
  • 336. 用列指针: void at(int *a,int n) { int i,j,t; for(i=0;i
  • 337. 10.4 字符串的指针和指向字符串的指针变量 ■字符串的表示形式 可用两种方法访问字符串: ①用字符数组存放字符串 ②用字符指针指向一个字符串
  • 338. main() { char c[5]=”abc”; 定义字符数组,并将字符串存入 char *p1=c,*p=”abc”;定义指针变量,指向字符串 printf(“%s”,c); 通过数组名访问字符串 printf(“%s”,p); 通过指针变量访问字符串 printf(“%-c”,*(p+3)); 通过指针变量访问字符 } cabc\0abc\010001000p
  • 339. 与其它一维数组的指针相比,字符串的指针有其独特之处: ①可以通过指针对字符串进行整体访问。 ②对字符串的操作依赖于结束符。 ③可以整体赋初值。 ④有各种字符串处理函数。
  • 340. 本节重点掌握: ①通过数组和通过指针操作字符串的基本方法。 ②常用的字符串处理方法。
  • 341. 例:字符串拷贝操作。 main() { char a[]=”abcdef”,b[20]; int i; for(i=0; *(a+i)!=’\0’; i++) *(b+i)=*(a+i); *(b+i)=’\0’; printf(“%s”,b); } main() { char a[]=”abcdef”,b[20],*p1, *p2; p1=a; p2=b; for( ; *p1!=’\0’;p1++,p2++) *p2=*p1; *p2=’\0’; printf(“%s”,b); }用指针变量处理
  • 342. 将拷贝操作编成一函数: void copy_string(char *from,char *to) { for(; *from; from++,to++) *to=*from; *to=0; } 还可以改成: void copy_string(char *from,char *to) { for(; *from;) *to++=*from++; *to=0; }
  • 343. 字符串合并函数: void append_string(char *from,char *to) { for(;*to; to++); for(; *from;) *to++=*from++; *to=0; }
  • 344. 阅读程序: void f (char *c) main() { { c+=2; char c[20]=”abcdef”; (*c)++; f(c+1); c++; *c=0; printf(“%s”,c); } }  
  • 345. ■内存空间的动态分配 在程序设计中,对于要处理的批量数据,我们往往是选用数组作为存放这些数据的数据结构,然而,数组有一个明显的缺点,就是在定义数组时,其长度必须是常值,无法根据需要动态地定义。这样,在很多情况下,不是定义的数组长度不够,就是定义太长以至于浪费。 采用动态分配可以克服这一缺点,并且可以随时释放。
  • 346. 动态分配内存空间步骤: ①定义一指针变量。 ②申请一片内存空间,并将其首地址赋给指针变量。此时便可通过指针变量访问这片内存。 ③用完后释放这片内存空间。   int *p; p=malloc(byte); …… free(p); 以上函数的原形在stdio.h中。 p
  • 347. 例:对n个学生的分数排序后输出。 #include “stdio.h” void sort(int *a, int n) { ┈ } main() { int *a,j,n; scanf(“%d”,&n); a=malloc(n*sizeof(int)); if(!a) exit(0); for(j=0;j
  • 348. 10.5 函数的指针和指向函数的指针变量 ■用函数指针变量调用函数 可以用指针变量指向一个函数,一个函数在编译时被分配给一个入口地址,这个入口地址就称为函数的指针。可以用指针变量指向函数,然后通过该指针变量调用此函数。
  • 349. int max(int x, int y) { int z; if(x>y) z=x; else z=y; return(z);} main() { int (*p)(); 定义指向函数的指针变量p int a,b,c; p=max; 将p指向函数max scanf(“%d%d”,&a,&b); c=(*p)(a,b); 通过p调用函数max 等效于c=max(a,b); printf(“\n %d”,c); }
  • 350. ■把指向函数的指针变量作为函数参数 指向函数的指针变量最常见的用途是把它作为函数的参数,用于接受主调函数传来的某一函数的入口地址,从而在被调函数中可以通过该指针变量调用它所指向的函数,这样,被调函数中就可实现非固定函数的调用,以达到编写通用函数的目的。 例:用矩形法编写一个通用的求定积分的函数。 关键问题:如何处理被积函数是未知的。
  • 351. double intgral (double a,double b,int n, double (*f)( ) ) { int i; double h,x,y,s=0; h=(b-a)/n; for(i=1;i<=n;i++) { x=a+(i-1)*h; y=(*f)(x); s+=h*y; } return(s); } double f1(double x) { return(3*x*x+2*x-1); } main() { double s; s=integral(1.0,2.0,100,f1); }
  • 352. 第十一章 结构体 11.1 概述 在实际应用中,有不少应用问题如果只采用已学的变量和数组作为数据结构显得很不方便。 例:输入100个学生的学号、姓名和考试成绩,编写程序找出高分者和低分者。 用变量和数组作数据结构可编写程序如下:
  • 353. main() { int i, num, maxnum, minnum; char name[20], maxname[20], minname[20]; int score, maxscore, minscore; maxscore=0; minscore=100; for(i=1; i<=100; i++) { scanf(%d%s%d”,&num,name,&score); if(score>maxscore) { maxscore=score; maxnum=num; strcpy(maxname,name); } if(score
  • 354. 明显缺点: ①变量过多,同一学生的各个数据无联系,没有整体概念,不便管理。 ②操作不便(如更新过程)。 显然,选用一种能把一个学生的数据构造成一个整体的构造型数据结构更合适,但不能是数组。 对于这种情况,可以将一个学生的数据定义为一个结构体类型:
  • 355. struct student 类型名 { int num; 成员表 char name[20]; int score; }; 定义了一个结构体类型,它包含三个成员。
  • 356. 11.2 定义结构体类型变量的方法 前面定义的结构体类型只是一种“模型”,还必须定义结构体变量后才能存放数据。 定义结构体变量有三种方法:
  • 357. 1、先定义结构体类型再定义结构体变量 定义了结构体类型后: struct student st, stmax, stmin; 类型符 变量名 定义了三个结构体变量,每个变量包含三个成员,每个变量可存放一个学生的数据。
  • 358. 2、在定义结构体类型的同时定义结构体变量 struct student { int num; char name[20]; int score; }st, stmax, stmin;
  • 359. 3、直接定义结构体类型变量 struct 不出现类型名 { int num; char name[20]; int score; }st, stmax, stmin; 常用第一种方法
  • 360. 说明: ①类型与变量不同,只对变量分配空间与操作。 ②对成员可以单独使用,相当于普通变量。 ③成员也可以是一个结构体变量。 struct date struct student { int month; { int num; int day; char name[20]; int year; struct date birthday; }; }st1, st2;
  • 361. ④成员名可以与程序中的变量名相同,两者代表不同的对象。
  • 362. 11.3 结构体变量的引用 ■成员引用 可以对成员单独引用,形式为: 结构体变量名 . 成员名 成员运算符 st.num=1001;st.score=90;strcpy(st.name,”Li”); printf(“%d%s%d”,st.num,st.name,st.score); scanf(“%d%s%d”,&st.num,st.name,&st.score) ; 可以引用成员的地址
  • 363. 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员,只能对最低级的成员进行存取与运算。 st1.birthday.year=1960; st1.birthday.month=5; st1.birthday.day=15;
  • 364. ■整体引用 可以对结构体变量进行整体赋值: stmax=st; 将st中的所有内容赋值给stmax。 对结构体变量的整体操作只限于赋值操作和参数传递,而且要求类型一致。不能对结构体变量进行整体输入输出。
  • 365. 结构体应用举例: 编写程序输入100个学生的学号、姓名和考试成绩,找出高分者和低分者。 struct student { int num; char name[20]; int score; };
  • 366. main() { int i; struct student st,stmax,stmin; stmax.score=0; stmin.score=100; for(i=1;i<=100;i++) { scanf(“%d%s%d”,&st.num,st.name,&st.score); if(st.score>stmax.score) stmax=st; if(st.score
  • 367. 11.4 结构体变量的初始化 对结构体变量可以在定义时指定初始值 struct student { int num; char name[20]; int score; }st={1001,”wang”,95};
  • 368. 11.5 结构体数组 可以定义结构体数组来存放批量数据。 ■结构体数组的定义 struct student { int num; char name[20]; int score; };struct student a[100]; 定义a数组,可以存放100个学生的数据。 a数组的每个元素又是一个结构体变量。
  • 369. ■结构体数组的初始化 在定义结构体数组的同时指定初值。 struct student { int num; char name[20]; int score; }; struct student a[2]= {{1001,”LiLi”,85},{1002,”wang”,90}};
  • 370. 或: struct student { int num; char name[20]; int score; } a[2]= {{1001,”LiLi”,85},{1002,”wang”,90}};
  • 371. ■结构体数组元素的引用 成员引用: a[0].num=1001; strcpy(a[0].name,”wang”); a[0].score=85; 整体引用: a[1]=a[0]; 与普通数组元素的引用相同
  • 372. ■结构体数组的应用 输入100个学生的学号、姓名和考试成绩,然后按从高分到低分的顺序排列后输出。
  • 373. struct student { int num; char name[20]; int score; }; main() { int i, j; struct student a[100], t; for(i=0;i<100;i++) scanf(“%d%s%d”,&a[i].num,a[i].name, &a[i].score);
  • 374. for(i=0;i<99;i++) for(j=i+1; j<100; j++) if(a[i].score
  • 375. 例:(p266例11.2) 对候选人得票的统计程序。设有三个候选人,每次输入一个得票候选人的名字,要求最后输出各候选人的得票结果。 #include “string.h” struct person { char name[20]; int count; }leader[3]={“Li”,0,”zhang”,0,”wang”,0};
  • 376. main() { int i, j ; char leader_name[20]; for(i=1;i<=100;i++) { scanf(“%s”,leader_name); for(j=0;j<3;j++) if(strcmp(leader_name,leader[j].name)==0) leader[j].count++; }
  • 377. printf(“\n”); for(i=0;i<3;i++) printf(“\n%15s%5d”, leader[i].name,leader[i].count); }
  • 378. 11.6 指向结构体类型数据的指针 ■指向结构体类型变量的指针 struct student st, st1; struct student *p; p=&st; 定义指向结构体类型数据的指针变量p
  • 379. 通过指针变量引用结构体变量: ①成员引用 (*p).num=1001; 或 p->num=1001; (*p).score=85; 或 p->score=85; strcpy((*p).name,”wang”); 或 strcpy(p->name,”wang”); ②整体引用 st1=*p; 等效于 st1=st;
  • 380. ■指向结构体数组的指针 struct student a[100]; struct student *p; p=a; 通过指针变量引用结构体数组元素: ①成员引用 (*p).num=1001; 或 p->num=1001; (*p).score=85; 或 p->score=85; strcpy((*p).name,”wang”); 或strcpy(p->name,”wang”);  
  • 381. 一般地: (*(p+i)).num=1001; 或 (p+i)->num=1001; (*(p+i)).score=85; 或 (p+i)->score=85; strcpy((*(p+i)).name,”wang”); 或 strcpy((p+i)->name,”wang”); 也可以用下标法:p[i].num=1001; ②整体引用 *(p+1)=*(p+0); 或 p[1]=p[0];
  • 382. ■用结构体变量和指向结构体的指针作函数参数 用结构体变量作函数参数时,对应的实参应该是同类型的结构体变量(或数组元素),参数传递是“值传递”。 用指向结构体的指针作函数参数时,对应的实参应该是同类型的结构体变量的地址(或数组的地址),参数传递是“地址传递”。
  • 383. main() { struct student st={1001,”LiLi”,70}; f(st); printf(“\n %5d%10s%5d”, st.num,st.name,st.score); } f(struct student a) { a.score=90; printf(“\n %5d%10s%5d”, a.num,a.name,a.score); } 1001 LiLi 70st1001 LiLi 70a90
  • 384. main() { struct student st={1001,”LiLi”,70}; f(&st); printf(“\n %5d%10s%5d”,st.num,st.name,st.score); } f(struct student *a) { a->score=90; printf(“\n%5d%10s%5d”,a->num,a->name,a>score); } 通过指针变量a可以访问它所指向的结构体。  1001 LiLi 70st20002000a90
  • 385. 11.7 用指针处理链表 ■链表概述 链表是一种重要的数据结构─动态数据结构。 以具体例子来说明链表的概念及其应用: 例:选择合适的数据结构来存放一批学生的学号及考试成绩,以便进一步处理。 由于学生人数未知,用静态数据结构不合适。 用链表处理较恰当。
  • 386. 用链表处理该问题的基本思路: 将各学生的数据进行离散存放,来一个学生就分配一小块内存(结点)。并将各结点用指针依次连接起来─链表。 每结点应包含下一结点的开始地址。 最后一个结点中的指针为空。 链头指针指向第一个结点,是访问链表的重要依据。 这样的链表称单向链表。 head 学号 成绩 指针学号 成绩 指针学号 成绩 指针学号 成绩 指针学号 成绩 NULL
  • 387. 一个结点可用如下结构体描述: typedef struct student { int num; 学号 int score; 成绩 struct student *next; 下一结点的首地址 } STU; typedef : 自定义类型符(见11.10)
  • 388. ■单向链表的建立 ①输入一个学生的数据。 ②分配结点空间,数据存入。 ③将该结点的首地址赋给上一结点的next,若该结点是第一个结点,则赋给头指针。 ④将该结点的next置为空,表示该结点为当前的最后结点。 head 学号 成绩 next学号 成绩 next学号 成绩 next学号 成绩 next学号 成绩 NULL
  • 389. STU *creat() { STU st,*p0=NULL,*p,*head=NULL; while(1) { scanf("%d%d",&st.num,&st.score); if(st.num<0) break; p=malloc(sizeof(STU)); *p=st; (*p).next=NULL; if(p0==NULL) head=p; p0为前一结点的指针 else (*p0).next=p; p0=p; } return head; }head 学号 成绩 next学号 成绩 next学号 成绩 NULL
  • 390. ■单向链表的访问 以输出为例 ①通过头指针找到第一个结点. ②输出当前结点的内容,并通过next找到后继结点,┄┄,直到next为空.
  • 391. void output(STU *head) { STU *p=head; while(p) { printf("\n %d %d",(*p).num,(*p).score); p=(*p).next; } } head 学号 成绩 next学号 成绩 next学号 成绩 NULL学号 成绩 next
  • 392. ■删除结点操作 ①按链表的访问方法找到相应结点。 ②若该结点是第一个结点,则将后继结点指针赋给头指针。 若该结点是最后一个结点,则将前缀结点的next置为空。 若该结点是中间结点,则将后继结点指针赋给前缀结点的next。 ③释放该结点所占的内存单元。 head 学号 成绩 next学号 成绩 next学号 成绩 NULL
  • 393. STU *delete(STU *head,int number) { STU *p =head,*p0=NULL; while(p) { if((*p).num==number) { if(p==head) head=(*p).next; else if((*p).next==NULL) (*p0).next=NULL; else (*p0).next=(*p).next; free(p); break; }else {p0=p; p=(*p).next;} } return head; } 假定要删除某一指定学号的结点
  • 394. ■插入操作 假定将结点p 插入到结点p0的后面, 则插入操作的关键为: p->next=p0->next; p0->next=p; head 学号 成绩 next学号 成绩 next学号 成绩 next学号 成绩 NULL
  • 395. 第十三章 文件 3.1 文件概述 ■文件概念 所谓文件就是:存储在外部介质上 的信息集合。 根据存储的介质不同可分为: 磁盘文件、磁带文件等。 根据内容的不同可分为: 程序文件、数据文件等。
  • 396. ■使用文件输入输出的必要性 这里主要讨论数据文件的输入输出,即如何将文件中的数据“输入”到程序的数据结构中,如何将程序的数据结构中的数据“输出”到文件中。 以往的输入输出方法: 键盘输入 ,屏幕输出。 这种方法不适用于数据量大的情况。
  • 397. 键盘输入和屏幕输出例: main() { int i, a[1000]; for(i=0;i<1000;i++) scanf(“%d”,a+i); ┊ for(i=0;i<1000;i++) printf(“%5d”,a[i]); }缺 点: ①可能出现重复输入。 ②输出的数据不能保存,不便于进一步使 用。
  • 398. 采用文件输入输出可以克服这些缺点。 main() { int i, a[1000]; for(i=0;i<1000;i++) scanf(“%d”,a+i); ┊ for(i=0;i<1000;i++) printf(“%5d”,a[i]); } } 文件文件
  • 399. ■ C文件分类 按在磁盘上存储的形式不同,可分为: 文本文件: 以ASCII字符存放—可见、可编辑、占空间大。 二进制文件: 以二进制形式存放—不可见、不可编辑、占空间小。 使用时可根据需要选择。
  • 400. 13.2 文件类型指针 每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息。这些信息保存在一个FILE类型的结构体变量中。 若 FILE *fp; 则fp就称为指向文件类型的指针变量。访问文件通过文件指针进行。
  • 401. FILE结构体类型是由系统定义的。 具体定义如下: typedef struct { short level; 缓冲区“满”或“空”的程度 unsigned flags; 文件状态标志 char fd; 文件描述符 unsigned char hold; 如无缓冲区不读取字符 short bsize; 缓冲区的大小 unsigned char *buffer; 缓冲区的位置 unsigned char *curp; 当前读写指针 unsigned istemp; 临时文件,指示器 short token; 用于有效性检验 }FILE;
  • 402. 13.3 文件的打开与关闭 对文件的读写之前应“打开”该文件。 使用结束后“关闭”此文件。 ■文件的打开(fopen 函数) 用fopen函数实现对文件的打开。 fopen函数调用的一般形式: FILE *fp; fp=fopen(文件名,读写方式);
  • 403. 例如: fp=fopen(“a1.txt”, ”r”); 以只读方式打开文件a1.txt。 fopen函数返回指向a1.txt文件的指针,即fp是指向a1.txt文件的指针变量,往后就可以通过fp访问a1.txt文件。
  • 404. 文件读写方式: “r” 按只读方式打开一个文本文件 “w” 按只写方式打开一个文本文件 “a” 按追加方式打开一个文本文件 “rb” 按只读方式打开一个二进制文件 “wb” 按只写方式打开一个二进制文件 “ab” 按追加方式打开一个二进制文件
  • 405. “r+” 按读写方式打开一个文本文件 “w+” 按读写方式建立一个新的文本文件 “a+” 按读写方式打开一个文本文件 “rb+” 按读写方式打开一个二进制文件 “wb+” 按读写方式建立一个新的二进制文 “ab+” 按读写方式打开一个二进制文件
  • 406. 说明: (1)不能用”r”方式打开一个不存在的文 件,”r”方式只读不能写。 (2)“w” 方式只写不能读,具有建立和 覆盖功能。 (3)调用fopen函数时,如果返回NULL则 表示打开不成功。
  • 407. ■文件的关闭(fclose 函数) 在使用完一个文件后应用fclose 函数关 闭文件,形式为: fclose(文件指针); 如: fclose(fp); 关闭后fp不再指向该文件。
  • 408. 13.4 文件的读写 文件打开后,就可以对它进行读写了。 ■文本文件的读写 即如何将以文本方式存放的文件输入到程序的数据结构中。如何将程序的数据结构中的数据以文本方式输出到文件中。
  • 409. 用于对文本文件读写的函数有: fscanf fprintf fgetc, getc fputc, putc fgets fputs
  • 410. 以例子说明fscanf和fprintf的使用。 例:已知文本文件f1.txt中存放有100个学生的分数,要求读入这些数据,并按从高到低的顺序排序后输出到另一文件中。
  • 411. #include “stdio.h” void sort(int *a,int n) { ……} main() { int i,a[100]; FILE *fp; fp=fopen(“f1.txt”, “r”); if(fp==NULL) exit(0); 定义一个指向文件的指针变量打开文件,使fp指向文件f1.txt
  • 412. for(i=0;i<100;i++) fscanf(fp,”%d”,a+i); fclose(fp); sort(a,100); fp=fopen(“f2.txt”, “w”); for(i=0;i<100;i++) fprintf(fp,”%4d”,a[i]); fclose(fp); }从fp所指的文件中读数据关闭fp所指的文件注意:文本文件的输入格式要与 文件中的数据格式匹配。
  • 413. ■二进制文件的读写 即如何将以二进制方式存放的文件输入到程序的数据结构中。 如何将数据结构中的数据以二进制方式输出到文件中。 读写函数: fread fwrite getw putw
  • 414. 例:将前例中的排序结果改用二进制方式输出到文件f3.dat中。 #include “stdio.h” void sort (int *a,int n) { ……} main() { int i,a[100]; FILE *fp; fp= f open(“f1.txt”, “r”); if(fp==NULL) exit(0);
  • 415. for(i=0;i<100;i++) fscanf(fp,”%d”,a+i); fclose1(fp); sort(a,100); fp=fopen(“f3.dat”, “wb”); fwrite(a, sizeof(int), 100, fp ); fclose(fp); }数据的开始地址数据的每一项的长度数据的项数文件的指针
  • 416. 如果要将二进制文件f3.dat读到数组中,则有: #include “stdio.h” main() { int a[100]; FILE *fp; fp=fopen(“f3.dat”,“rb”);if(fp==NULL) exit(0); fread(a,sizeof(int),100,fp); fclose(fp); ┊ }
  • 417. 13.5 文件的定位 文件中有一个位置指针,指向当前读写位置。如果顺序读写一个文件,每次读写完一个字符后,该位置指针自动指向下一个字符位置。如果想改变这样的规律,强制使位置指针指向指定位置,可以用有关函数。
  • 418. ■rewind函数 rewind函数的作用是使位置指针重返回文件的开头。 例:对文本文件f1.txt中的100个分数求超过平均分的人数。
  • 419. #include “stdio.h” main() { int i,a,n=0; float aver=0; FILE *fp; fp=fopen(“f1.txt”,“r”); for(i=0;i<100;i++) { fscanf(fp,”%d”,&a); aver+=a; }aver/=100; rewind(fp); for(i=0;i<100;i++) { fscanf(fp,”%d”,&score); if(score>aver) n++; } fclose(fp); printf(“\n %d”,n); }
  • 420. ■fseek函数和随机读写 使用fseek函数可以将位置指针指向所需的位置。 fseek函数调用的一般形式: fseek(文件指针,位移量,参考点); 以起始点为基准,向前移动的字节数0 或 SEEK_SET 文件开始 1 或 SEEK_CUR 当前位置 2 或 SEEK_END 文件末尾
  • 421. 例:如果fp是指向一个存放100个整数的二进制文件,要读取第50个数到变量n时: fseek(fp,sizeof(int)*(50-1),SEEK_SET); fread(&n, sizeof(int), 1,fp);
  • 422. 例:如果fp是指向一个存放100个整数的文本文件,并已知每个数按3位数字的定长格式存放,要读取第50个数到变量n时: fseek ( fp, 3*(50-1), SEEK_SE ); fscanf ( fp, ”%3d”, &n); 若要从当前位置跳过10个数后读取一个数: fseek ( fp, 3*10, SEEK_CUR ); fscanf ( fp, ”%3d”, &n);
  • 423. 例:已知文本文件f5.txt中存放有100个学生的学号、姓名和考试成绩;要求从键盘输入任一学号,检索出相应学生的数据。 说明: (1)文件f5.txt中每行为一个学生的数据,按定长格式存放,依次为:学号(整数,占5格)、姓名(占10格)、成绩(整数,占4格)。 (2)按学号从小到大的顺序连号存放,起始学号为1001。
  • 424. #include “stdio.h” typedef struct { int num; char name[20]; int score; }STU; main() { int no; STU st; FILE *fp;
  • 425. fp=fopen ( “f5.txt” , ”r” ); scanf ( “%d” , &no ); fseek ( fp, (no-1001)*19, 0 ); fscanf ( fp, ”%5d%10s%4d” ,&st.num ,st.name, &st.score ); printf (“\n%5d%10s%4d” , st.num , st.name, st.score ) ; fclose ( fp ) ; } 1001 LiLi 90 1002 WangPing 100 1003 HuHeng 75 ┊ 如果是二进制文 件呢?
  • 426. 上例的检索方法称为“定位检索”。 如果是非定长格式,则需要用“遍历检索”。 while ( !feof ( fp ) ) { fscanf ( fp, ”%d%s%d”, &st.num, st.name, &st.score ); if ( st.num==no ) { printf ( “\n%5d%10s%4d”, st.num, st.name, st.score ); break; } } 速度慢,但不受限制
  • 427. 综合例: 已知文本文件f1.txt中存放有武汉市所有公民的有关性别和年龄的数据,请编写程序分别找出其中10名男寿星和10名女寿星,并将20名寿星的数据以文本文件的方式存入到文件f2.txt中(先男后女)。
  • 428. 说明: ① 文件f1.txt中每行为一个公民的数据,共有3项,依次为:姓名(不超过10个字符)、性别(0表示男,1表示女)和年龄(整数),项间以空格分隔。 ②  未给出公民个数,将文件中的数据读完为止。
  • 429. 算法思想: 开辟一个存放20名寿星数据的结果表a(结构体数组),然后逐个读取公民数据,每读取一个就向a中“判断插入”一个,男性公民往前段插,女性公民往后段插。
  • 430. 读一个公民的数据到p读完否?NYWanghao 0 100 Liming 0 98 ┊ wudan 1 99 xiaofang 1 95 ┊ 男性插入女性插入寿星表a
  • 431. #include typedef struct { char name[10]; int sex; int age; } PEP;
  • 432. 插入函数,将一个公民的数据插入到寿星表 void insert (PEP *a, int n, PEP t ) { int i,j; if ( t.age < a[n-1].age ) return ; for ( i =0; ia[i].age) break; for ( j=n-1; j>i; j--) a[j]=a[j-1]; 移位 a[i]=t; 插入 }
  • 433. main() { int j; PEP p,a[20]; FILE *fp; fp=fopen (“f1.txt”,”r”); if(!fp) exit(0); for (j=0;j<20;j++) a[j].age=0; while( !feof ( fp ) ) { fscanf ( fp, ”%s%d%d”, p.name, &p.sex, &p.age ); insert (a+10*p.sex,10,p); } fclose(fp);
  • 434. fp=fopen(“f2.txt”,”w”); for(j=0;j<20;j++) fprintf(fp,”\n%15s%2d%5d”, a[j].name, a[j].sex,a[j].age ); fclose ( fp ); }