<ins id="h3npf"><span id="h3npf"></span></ins>
<cite id="h3npf"><span id="h3npf"></span></cite><progress id="h3npf"></progress>
<cite id="h3npf"></cite>
<listing id="h3npf"><dl id="h3npf"><progress id="h3npf"></progress></dl></listing>
<cite id="h3npf"><video id="h3npf"></video></cite>
<cite id="h3npf"><video id="h3npf"><menuitem id="h3npf"></menuitem></video></cite>
<ins id="h3npf"></ins><var id="h3npf"><video id="h3npf"></video></var><cite id="h3npf"></cite><cite id="h3npf"></cite>

C语言入门教程

来源互联网 由 RaNHaOFaN110 贡献 责任编辑鲁倩  

C语言程序设计

Copyer:Vigiking

\f第一章 计算机语言与C语言概述

1.1 计算机语言概念 计算机语言定义 计算机能够识别和接受的语言 要使计算机按自己的意图工作必 须使用计算机所能接受理解和执 行的指令指挥计算机工作

\f计算机语言的种类

机器语言

计 算 机 语 言

?#22270;?#35821;言

面向计算机

汇编语言

BASIC 入门语言 F77 科学计算 高级语言 Foxpro 数据库管理 面向问题 C 多用途

\f?机器语言 最早问世用二进制代码构成指令 如 100000 + 110000 - 用机器语言编程的缺点 繁琐不直观不易调试 如计算y=2x2+3x-1需要七八条指令 移植性差 依赖于计算机

\f?汇编语言 用符号构成指令如MOVADD 用汇编语言编程 相对直观但仍繁琐仍是面向计算 机的语言

汇编语言是计算机间接接受的语言

\f?高级语言 与?#22270;?#35821;言相比有根本性的区别 是面向问题的语言 高级语言的一条指令语句 y=2*x*x+3*x-1; 对应于y=2x2+3x-1 y=sin(x); 对应于y=sinx 用高级语言编程 直观易懂移植性好不依赖于具体计 算机

\f上机运行高级语言程序需经过编译

编译

高级语言源程序

执行

机器指令 目的程序 结果

编译程序

要上机完成一个计算问题主要的任务就是 用高级语言编写出相应的源程序即至少要 学会一种计算机语言

\f1.2

C语言的特点

集高级语言和?#22270;?#35821;言的优点于一身 能实现?#22270;?#35821;言的大部分功能如直接 访问内存物理地址进?#24418;?#25805;作等 图形功能强 运算符和数据结构丰富 语法限制不太严格程序设计自由度大 生成目标代码质量高程序执行效率高

\f1.3

简单的C程序介绍

例1

main() 主函数说明 { 程序框架 printf(abcdef); 函数体 } 语句

程序的功能是输出字符串abcdef

\f例2求两数之和 main() { int a,b,c; a=100; b=50; c=a+b; printf(\

c=%d,c); }

程序运行结果c=150

a b c

\f例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(\

max is:%d,c); return(z); } }

a b c

3 5 5

x y z

3 5 5

两个函数组成

\f总结上例可知 1 C程序由函数构成 2 函数由两部分组成 函数说明部分函数名函数类型形 参名形参类型 函数体实现函数的具体操作由语句 构成 3 程序总是从main函数开始执行 4 书写格式自由 5语句必须有分号

\f第二章

2.1 算法的概念

算法

要利用计算机处理问题光学习语言

的语法规则还不够最重要的是要学会

针对各类\r

因转码可能存在排版?#20219;?#39064;敬请谅解以下文字仅供您参考:

C语言程序设计

Copyer:Vigiking

第一章 计算机语言与C语言概述

1.1 计算机语言概念 计算机语言定义 计算机能够识别和接受的语言

要使计算机按自己的意图工作必 须使用计算机所能接受理解和执 行的指令指挥计算机工作

计算机语言的种类

机器语言

计 算 机 语 言

?#22270;?#35821;言

面向计算机

汇编语言

BASIC 入门语言 F77 科学计算 高级语言 Foxpro 数据库管理 面向问题 C 多用途

?机器语言 最早问世用二进制代码构成指令

如 100000 + 110000 - 用机器语言编程的缺点 繁琐不直观不易调试

如计算y=2x2+3x-1需要七八条指令

移植性差

依赖于计算机

?汇编语言 用符号构成指令如MOVADD 用汇编语言编程 相对直观但仍繁琐仍是面向计算 机的语言

汇编语言是计算机间接接受的语言

?高级语言 与?#22270;?#35821;言相比有根本性的区别 是面向问题的语言

高级语言的一条指令语句 y=2*x*x+3*x-1; 对应于y=2x2+3x-1 y=sin(x); 对应于y=sinx 用高级语言编程 直观易懂移植性好不依赖于具体计 算机

上机运行高级语言程序需经过编译

编译

高级语言源程序

执行

机器指令 目的程序 结果

编译程序

要上机完成一个计算问题主要的任务就是 用高级语言编写出相应的源程序

即至少要 学会一种计算机语言

1.2

C语言的特点

集高级语言和?#22270;?#35821;言的优点于一身 能实现?#22270;?#35821;言的大部分功能如直接 访问内存物理地址进?#24418;?#25805;作等

图形功能强

运算符和数据结构丰富

语法限制不太严格程序设计自由度大

生成目标代码质量高程序执行效率高

1.3

简单的C程序介绍

例1

main() 主函数说明 { 程序框架 printf(abcdef); 函数体 } 语句

程序的功能是输出字符串abcdef

例2求两数之和

main() { int a,b,c; a=100; b=50; c=a+b; printf(

c=%d,c); }

程序运行结果c=150

a b c

例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(

max is:%d,c); return(z); } }

a b c

3 5 5

x y z

3 5 5

两个函数组成

总结上例可知 1 C程序由函数构成

2 函数由两部分组成 函数说明部分函数名函数类型形 参名形参类型

函数体实现函数的具体操作

由语句 构成

3 程序总是从main函数开始执行

4 书写格式自由

5语句必须有分号

第二章

2.1 算法的概念

算法

要利用计算机处理问题光学习语言

的语法规则还不够最重要的是要学会

针对各类型的问题拟定出有效的解题

方法和步骤

解题方法和步骤就是算法

算法

为了解决一个问题而采取的有限步骤

计算机算法

如何使计算机一步一步地工作的具体过程

利用计算机处理问题的步骤 1设计好算法算法设计

2用计算机语言实现算法程序设计

算法必须是有效的

算法设计还要充分考虑算法的好坏

衡量算法好坏的主要标准 程序简练

执行速度快

占空间少

例考虑 s=i

1

10

的算法

算法٣直接表达

直接用语句 s=1+2+3+4+5+6+7+8+9+10

当项数较多时该算法不适用

算法ڣ迭代法累加求和法 s=1+2+3+4+5+6+7+8+9+10 算法步骤 s i 使s=0 + 10 6 3 1 0 5 4 3 2 1 使i=1 累加器 记数器 s+is 该算法通用是好算法 i+1i 若i10转ۣ否则转 输出s

2.2

算法的表示

算法需要有统一的表示方法 常用的表示方法有 自然语言 流程图 结构化流程图 N-S流程图.

1自然语言 对于计算 s=1+2+3+4+5+6+7+8+9+10 用自然语言表示为 使s=0 s为累加器 使i=1 i为计数器 s+is 累加求和公式 i+1i 计数器加1 若i10转,否则转 输出s的值 特点?#21644;?#20439;易懂文字冗长含义不大严格

2流程图 用流程图符号表示算法

常用的流程图符号

起止框 处理框 输入输出框 流程线

判断框

对于计算 s=1+2+3+4+5+6+7+8+9+10 用流程图表示为

s+i s

0 s

S+i s

i+1 i Y i10

1 i

N

输出s

直 观 形 象 易 于 理 解 次 序 清 楚

3结构化流程图

传统的流程图有一个弊端对流程线没 有严格的限制对于较复杂的算法可能会 变成乱麻一般BS型算法

为克服这一弊端提出了由三个基本结构 组成算法流程图的思想 结构化流程图

三个基本结构 顺序结构 按固定顺序从上到下或从左?#25509;b?#25191;行 的结构

a A

B

b

选择结构 根据条件P选择执行哪一个分支

a

成立

成立 不成立

不成立

p A B

b

例计算

y= 1/x y= 10000 当x0时 当x=0时 的算法流程图图

选择结构

输入x Y N

X=0?

10000y

输出y

1/xy

循环结构 重复执行某些操作的结构

分为两种 当型循环和直到型循环

a a

A

P1 N b Y

A

P2 Y b N

当型循环

直到型循环

可以看出每个基本结构都只有 一个入口和一个出口因此用三 个基本结构构成的流程图不会象乱 麻一般用三个基本结构构成的流 程图就成为结构化流程图用结构 化流程图描述的算法称为结构化算 法相应的程序设计就称为结构化 程序设计

观察前例

顺 序 结 构

0 s 1 i y

S+i s i+1 i

循 环 结 构

i10 n

输出s

4 N-S流程图

N-S流程图的三个基本结构 顺 序 结 构

A

P

成立

B

A

选 不成立 择 结 B 构

B

循 环 结 构

当P1

A 直到P2

例计算 y= 1/x y= 10000 的N-S流程图

输入x

X=0? 是

当x0时 当x=0时

10000y

输出y

1/xy

例 计算 s=1+2+3+4+5+6+7+8+9+10 的N-S流程图

0 s 1 i 0 s 1 i

i10

s+i s

i+1 i

直到i>10

s+i s i+1 i 输出s

输出s

直到型

当型

第三章

3.1

数据类型运算符 与 表达式

C 的数据类型

基本类型 整型实型字符型枚举型 构造类型 略 指针类型 略

空类型

各类型包含常量与变量

3.2

常量与变量

常量与符号常量 常量 在程序运行过程中其值保持不变的量

符号常量

用来代表一个常量的标识符

#define PI 3.1415926

例 #define PI 3.1415926 main() { float r=2,c; c=2*PI*r; printf(%f,c); }

变量 其值可以改变的量

变量的三要素: 变量名 每个变量有一个名字作为识别该变量的 标识符

变量的值 每个变量有一个值变量的值是随时 可以改变的

变量的存储单元 每个变量占据一个内存单元用于存放变量 的值

变量名

a 变量值

3

存储单元

变量的命名规则 由字母数字和下划线组成 以字?#23500;?#19979;划线开头

ax1y_2_b1_1c 1xa+2 合法 不合法

变量必须先定义后使用

程序中?#38382;?#20351;用常量?#38382;?#20351;用变量

3.3 整型数据 整型常量 即整常数c的整常数有三种形式 十进制整数 与数学中的整数一致如10012315等

八进制整数 以0开头的整数如010,07,020等

十六进制整数 以0x开头的整数如0x10,0xff,0x2a等

整型变量 用于存放整数的变量

分4种类型 ?#20521;?#26412;型int a 16位可表示的数值范围-3276832767 短整型short int b 16位可表示的数值范围-3276832767 长整型long int c 32位数值范围-21474836482147483647

无符号型加上 unsigned 只存放正数

如unsigned int x 变量x为无符号整数16位全表示数码 数值范围065535 在程序设计中如果要使用整型变 量必须首先选择以上类型符来定义变量 然后才能使用

例 main() { int a,b,c; a=100; b=50; c=a+b; printf(%d,c); }

定义

使用

一般根据什么原则选择变量的类型

3.4

实型数据

实型常量 可使用两种形式表示 小数形式如 1.23, 3.1415926 指数形式如 1e-20 1.23e5 15.48

实型变量 用于存放实数的变量 分单精度和双精度两种 float a,b 定义a和b为单精度型变量 32位7位有效数字10-381038 double x,y 定义x和y为双精度型变量 64位15位有效数字10-30810308

例 main() { float r,c; double r,c; r=5; c=2*3.1415926*r; printf(%f,c); }

3.5

字符型数据

字符常量 用单引号括起来的一个字符

a?,?x?,?*?,?1?等

除此外以\?开头的字符如

?,?\t?等 称为转义字符祥见书表3.3

字符型变量

用于存放字符的变量

char c1,c2 定义c1和c2为字符型变量 c1=?a?; c2=?b?; 字符赋值

字符型变量存放一个字符占据一个字节

字符型数据的存储形式 a 存放ASCII码 不是 而是 97 如字符a?在内存中存放97b? 存放98

与整数的存储形式一致它们之间可以通 用 一个字符数据既可以作字符用也可以作 整数用取其ASCII代码

如32+?a?相当于 32+97 若 int x; char c; 则 x=?a?; c=97; x=97; c=?a?; 都允许

字符串常量 用双引号括起来的字符序?#23567;?p>如abcde,china a也属字符串

注意a与a?的区 别

对于 char c; c=a; 用法错误

字符串中每个字符各占一个字节并且在 字符串结尾加上一个结束标记\0?

如china 在内存中占6个字节

c h i n a \0

C语言中专门的字符串变量可用字符数 组存放以后介绍

3.6 变量赋初值 在定义变量的同时给相应的变量赋初值

如 int a=3; a 3 float b=5.2; char c=?a?; int x=y=z=6; 效果 在给变量分配内存单元的同时在相应的单 元中存放初值

3.7

各数值型数据间的混合运算

整型实型字符型数据间可以进行混合运 算如 10-?a?*1.5 运算时参加运算的两个数据如果类型不同 则首先将其类型转换成一?#30053;?#36816;算转换规则 是 将优先级低的类型转?#22351;接?#20808;级高的类型

数据类型的优先级

double long unsignde 低 float

int

char

算术运算符与算术表达式 基本的算术运算符 + - * / % 算术表达式 用算术运算符将运算对象连接起来的式子 用于表达数学公式的式子 如2*x+y-1/a 表达式经过运算最终得到一个值 算术表达式的值

3.8

运算符的优先级与结合性 优先级 在对表达式求值时如果存在多个运算符则 运算的先后次序按运算符的优先级别从高到底进 行

运算符的优先级关系为 高* / % 低+ 如a-2*x 先算 * 2*(a+2) 有括号的情况

结合性 如果在一个运算对象两边的运算符的优先级相 同则按规定的结合方向处理

如a-b+c b与-结合是从左?#25509;b?#31216;左结合性

b与+结合是从右到左称右结合性

每个运算符都有相应的优先级和结合性

基本算术运算符都是左结合性

计算表达式例 2+?A?-1/2.0 1+3/2-1 构造表达式例 2x2+3x-1 a+b a+b

a-b

2*x*x+3*x-1 (a+b)/(a-b) ? a+b/a-b

(*不能省

a-b x+y x-y

(a+b)/(a-b)/(x+y)/(x-y) (a+b)/(a-b)/((x+y)/(x-y))

(a+b)/(a-b)/(x+y)*(x-y)

强制类型转换

可以用强制类型转换运算符将一个表达式 的值转换成所需类型 如 (int)(x+y) (float)(7%3)

应用举例

int a=200,b=300,c; c=(long)a*b/100; ? c=(long)(a*b)/100; c=a*b/100;

可知有自动转换和强制转换 当自动转换达不到目的时可用强制转换

自增自减运算符 自增运算符++ ?#36129;?#37327;值加1 自减运算符-?#36129;?#37327;值减1 两种用法 ++i, --i 先加减后用 i++, i-先用后加减 两种用法对i效果一样但表达式的值不同

例假设i的原值为5 j=++i; j=? j=i++; j=?

注意 ++和--只能用于变量

如3++和a+1++ 不合法 ++和--为右结合性

(-i)++ -i++ -(i++) 若i的原值是5则该表达式的值是多少

例分析执行下列语句后的结果

a=5; a b=a++; 6 5 c=--a-b++; d=(a++)-(++b)+c--;

b 7 6 5

c -1 0

d -2

两种特殊情况 k=(i++)+(i++)+(i++)

i+++j

是 i+(++j) 还是 (i++)+j ?

i++虽然与i=i+1等效但使用自增自减 运算符的代码优化程度好因而经常使用

但用时需特别小心

3.9 赋值运算符和赋值表达式

赋值运算符

=称赋值运算符其作用是将一个数据

赋给一个变量

如 a=5

不要理解为等号

执行赋值运算的结果是将右边的数据存

入左边变量所对应的内存单元?#23567;?p> 赋值规则 如果赋值运算符两侧的类型不一致则在赋 值时要进行类型转换转换规则为 实型整变量 舍去小数部分

int a=5.5; a?#24418;?

整型实变量 数值不变以浮点形式存储

字符型整变量 放在整形变量低8位

保持原值不变原则

int a=?A?;

复合赋值运算符 在赋值运算符前加上其它运算符可以构成 复合赋值运算符

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) 属于高效?#35797;?#31639;符

赋值表达式 主要实现赋值运算的表达式

一般形式 <变量>=<表达式> 如 a=5 y=2*x+3 a=a+1

不是衡等

作用将右边表达式的值赋给左边的变量

赋值表达式的?#31561;?#24038;边变量的值

赋值表达?#25509;?#36793;的<表达式>可以是任何表 达式如 a=(b=5) 赋值表达式中包含赋值表达式

赋值运算符的优先级低于所有算术运算符

且是右结合性

a=(b=5) 与 a=b=5 等效

例计算以下表达式的值

a=b=c=5

a=5+(c=6)

a=(b=4)+(c=6)

a=(b=4.5)+(c=6.5) abc为整型变量

a+=a-=a*a

(设a的原值为3)

赋值表达式是C语言中的一个重要成分 在赋值表达式后加一分号就成为常用的赋值 语句

如 y=2*x+1; 赋值表达式作为表达式的一种可以出 现在任何表达式中如 x+2-(b/3-(a=k-5)+?b?

3.10

逗号表达式

逗号也是一种运算符用它对两个表达式

实现连接运算

3+5,6+8 称逗号表达式

逗号表达式的一般形式 表达式1表达式2 取表达式2的值作为整个逗号表达式的值

如 a=3*5,a*4 逗号表达式的值为60

一个逗号表达?#25509;?#21487;以与另一个表达式组成 一个新的逗号表达式如 (a=3*5,a*4),a+5 因此逗号表达式的一般形式可以扩展为 表达式1表达式2表达式3, 表达式n 取表达式n的值作为整个逗号表达式的值

逗号运算符的优先级最低,且是左结合性

逗号运算符只起到连接作用没有实际操作

第四章

最简单的C程序设计

C 程序最基本的成分是语句 目前我们已掌握的语句 变量说明语句: int a,b,c; 表达式语句 x+y; 特别地 a=5; 赋值语句

可以编写简单程序如

main()

{ int

x,y;

x=5;

y=2*x*x+3*x-1;

} 该程序语法上完整但还缺少输出

数据的输出

用输出函数实现,其中的两种输出函数 1.putchar 函数 字符输出函数 用于输出一个字符

如: putchar (?a?); putchar (100); char c=?b?; putchar (c);

例输出单词Boy的完整程序 #include stdio.h 注意该语句的作用 main() { char a, b, c;

a=?B?; b=?o?; c=?y?;

putchar (a);

putchar (b);

putchar (c);

}

2. printf函数格式输出函数 ?#25105;?#31867;型?#25105;?#26684;式?#25105;?#20010;数

例如 int a=100,b=56; printf(a=%d,b=%d,a,b);

普通字符 格式说明 格式控制 输出表列

输出结果a=100,b=56

% 后的字符称格式字符不同格式字符对应不同的数据类型

d格式符按整数格式输出 几种用法 %d 不指定宽度按实际宽度输出 %md 按指定宽度输出m为宽度 %ld 用于输出长整型数

例 int a=125,b=453;

long

c=65535;

printf(a=%d,b=%5d,c=%ld,a,b,c);

输出结果

a=125,b= 453,c=65535

%ld也可以按指定宽度输出 printf(c=%8ld,c); 输出结果c = 65535

注意格式字符的类型要与对应的输出对象 的类型一致

c格式符用于输出字符 char c=?A?; printf(c=%c,%c,c,?B?);

输出结果c=A,B

输出对象既可以是字符变量字符常量还 可以是整型表达式

int a=100;

char

b=?A?;

printf(

%d,%c,a,a); printf(

%c,%d,b,b); 输出结果 100,d A,65

s格式符用于输出字符串 %s %-ms %ms %m.ns 不指定宽度 指定宽度左?#31185;?指定宽度右?#31185;?指定宽度m只取左端n个字符 右?#31185;?%-m.ns 指定宽度m只取左端n个字符 左?#31185;?p> 例

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

f格式符按小数形式输出实数

%f ?#19978;低?#25351;定宽度6位小数 指定宽度m小数位数n右?#31185;?p>%m.nf

%-m.nf 指定宽度m小数位数n左?#31185;?注意宽度包括符号和小数点

例 float a=3.141592654,b=14.326795, c=-125.2468; printf(

a=%f,b=%8.3f,c=%-10.2f,a,b,c); 输出结果 a=3.141592,b= 14.326,c=-125.24

完整前面的程序

main()

{

int

x=5;

x,y;

y=2*x*x+3*x-1; printf(

y=%d,y); }

程序设计例

编写程序计算如图中的电流I.

I

U

R1

R2

R3

假设 U=220,R1=30,R2=60,R3=45

算法设计 程序设计 main() {

I=U/R1+U/R2+U/R3

int U=220,R1=30,R2=60,R3=45; float I; I=U/R1+U/R2+U/R3; printf(

I=%f,I); }

正确的程序 main() { int U=220,R1=30,R2=60,R3=45; float I; I=(float)U/R1+(float)U/R2+(float)U/R3; printf(

I=%f,I); }

考虑通用 main() { int U,R1,R2,R3; float I; 输入 U,R1,R2,R3 I=(float)U/R1+(float)U/R2+(float)U/R3; printf(

I=%f,I); }

数据的输入 getchar函数字符输入

#include stdio.h main() { char c; c=getchar(); 等待键盘输入 putchar(c); }

scanf 函数格式输入

与printf函数相反

用于输入若干?#25105;?#31867;型的数据

scanf(%d%d%d,&a,&b,&c);

格式控制 地址列表

scanf(%d%d%d,&a,&b,&c);

执行此函数时等待从键盘输入三个整 数给a,b,c 若从键盘输入 3 5 8 则?#20302;?#21363;从键盘缓冲区取出这三个数分 别赋给a,b,c 注意与printf的区别注意格式的匹配

如 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);

在键盘输入时用分隔符把每个数据隔开 标准的分隔符是空格

如123 150 23 若想用逗号作分隔符则 scanf(%d%d%d,&a,&b,&c); 不要随便使用普通字符如使用 scanf(a=%db=%d c=%d,&a,&b,&c) 对应数据输入 a=123,b=150,c=23

前面的欧姆定律: 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(

I=%f,I); }

求三角形面积

#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(

area=%f,area); }

使用数学函数

使用三角函数

#include math.h main() { float x, y; scanf ( %f, &x); y=sin(x*3.1415926/180); printf(

y=%f,y); }

以弧?#20219;?#21333;位

第五章 选择结构程序设计

对于如下的函数计算算法上属于一个选 择结构

1/x 当x0时

y=

10000 当x=0时 用于实现选择结构的主要是if语句

if语句的最常见形式为

if(关系表达式)语句1

else 语句2

如 if(x!=0) y=1/x; else y=10000; 其中 x!=0 !=

就是一个关系表达式 就是一个关系运算符

51 关系运算符和关系表达式

1关系运算符

用于进行比较运算的运算符

共有六种

< <= > >= = = != 优先级与结合性 前4?#25191;?#20110;后两种

低于算术运算符而高于赋值运算符

左结合性

2关系表达式 一般形式 表达式关系运算符表达式

如a>b

a+b>b+c

经过关系运算后最终有一个值--关系表达 式的值

关系表达式的值只有 0(假或1真

例设 a=2, b=4, c=1 式的值

a>b a+b>b+c ?a?>?b? x=2>(y=5) (a<b)>(b<c) a<b= =b<c

计算以下关系表达

可以是字符表达式 可以是赋值表达式 甚至可以是关系表达式

例假设 x=3, y=5, z=1, 计算以下关系 表达式的值

x+z>y x<y==y<z y>z==x>z y>x>z (x==y-2)<z+1==x+y a=x+y==x+z<y+x!=z+1>x+1

52 逻辑运算符和逻辑表达式 有时只用一个简单的关系表达式无法完整 地表达一个条件如

1/x+1/a 10000 当x0,a0时 其它

y=

其中的条件需要用逻辑表达式来表达 x!=0 &&a!=0 &&就是一种逻辑运算符

1逻辑运算符

&& 逻辑与 两个操作数都为真时&&运算结果为真

|| 逻辑或 两个操作数之一为真时即为真

! 逻辑非 单目运算 操作数为真假时为假真

如若 a=2, b=3, c=0 则 a<b&&b<c 0 a<b||b<c 1 !(a<b) 0

优先级由高到低 ! 逻辑非 算术运算符 关系运算符 &&逻辑与 ||逻辑或 赋值运算符

结合性左结合性

2逻辑表达式 实际上前面所举例子即为逻辑表达式 a<b&&b<c a<b||b<c !(a<b) 逻辑表达式的?#20302;?#26679;只有1和0但参加逻辑 运算的操作数可以是?#25105;?#31867;型的数据可以是 ?#25105;?#22823;小

例 a+b&&b+c 是合法的逻辑表达式

此时以0代表假非0代表真

例设 a=2, b=3, c=0 , 计算以下表达式

的值 a&&b b&&c a||c

!a+c&&b+c

!c+a==b||b<a

a+c||a+b>c+10

对于逻辑表达式的两种基本技能 逻辑表达式的计算

逻辑表达式的构造

逻辑表达式的构造举例 abc a>=b>=c 5>=4>=3 a>=b&&b>=c

a和b之一为0但不同时为0 a==0&&b!=0 || a!=0&&b==0 a*b==0 a*b==0&&a+b!=0

对于 a==0 && b!=0 || a!=0 && b==0 a==0可以用!a代替 a!=0可以直接用a !a && b || a && !b 但必须是运算结果作为逻辑量的情况下

y=(a!=0) 与

y=a

不等效

53

if语句

1if语句的三种形式 if表达式语句

有一分支为空

scanf(%d,&score); if(score>=60) printf(pass);

if表达式语句1

else 语句2

if(x!=0) y=1/x; else y=10000;

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);

对于 1/x

y=

当x0时

? y=1/x; if(x==0)y=10000

10000 当x=0时

一般用 if(x!=0) y=1/x; else y=10000;

也可用 y=10000; if(x!=0) y=1/x;

例习题5.5: x (x<1) y= 2x-1 (1x<10) 3x-11 (x10)

main()

{

float x,y; scanf(%f,&x); if(x<1) y=x; else else if(x<10) y=2*x-1; y=3*x-11;

printf(

y=%f,y); }

说明 语句中的表达式可以是?#25105;?#34920;达式 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;}

分支程序设计举例基本?#35760;?#21644;算法

例从键盘输入三个整数到变量a,b,c,输 出其中最大的数

两种典型算法 枚举法将各种可能性枚举出来

选择法先假设后判断更新

}

选择法 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(

max=%d,max); }

例从键盘输入三个整数到变量a,b,c,要求

按从大到小的顺序输出

两种典型算法 枚举法将各种可能的排列枚举出来

换位法将a,b,c中的数据换位

}

换位法 a b c 3 8 8 5 3 3 5 5 main() { int a,b,c,t; scanf(%d,%d,%d,&a,&b,&c); if(a<b) {t=a; a=b; b=t;} if(a<c) {t=a; a=c; c=t;} if(b<c) {t=b; b=c; c=t; } printf(

%d,%d,%d,a,b,c); }

2if语句的?#30701;?在if语句中又包含一个或多个if语句 if(score>=80) if(score>=90) printf(A); else printf(B); else if(score>=60) printf(C); else printf(D); 注意else与if的匹配

3条件运算符 如果两个分支的内容都是给同一个变量赋值 则可用简单的条件运算符处理 if(a>b) max=a; else max=b; 可用 max=a>b ? a : b; 赋值运算符右边为一条件表达式

条件表达式的一般形式 表达式1 表达式2 表达式3

条件表达式的执行过程 a>b ? a : b

非0 表达式1 0

条件表达式 取表达式2的值

条件表达式 取表达式3的值

优先级低于关系运算符高于赋值运算符

结合性右结合性

例求a,b,c中的最大值

max= a>b ? (a>c?a:c) : (b>c?b:c) ;

54

switch语句多分支

适用于根据一个表达式的值就可确定 走哪个分支的情况

switch表达式 { 常量表达式1 语句1 常量表达式2 语句2 常量表达式n 语句n default 语句n+1 }

例成绩分档 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.

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; break; break; break; break;

55 程序举例 (习题5.10) 有4个圆塔圆心分别为22) -22-2-22-2圆 半径为1

这4个塔的高?#20219;?0m,塔以外无 建筑物

今输入?#25105;?#28857;的坐标求该点的 建筑高度塔外的高?#20219;?

算法设计

10 在某一圆内 x,y

h=

0 在圆外

条件在某一圆内 在圆1内或在圆2内或在圆3内或在圆4内 若设变量c1c2c3c4分别代表是否在相 应的圆内则以上条件为 c1||c2||c3||c4

c1=(x-2)2+(y-2)21 c2=(x+2)2+(y-2)21 c3=(x+2)2+(y+2)21 c4=(x-2)2+(y+2)21

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(

h=%d,h); }

第六章 循环控制

61 概述 所谓循环控制就是如何实现循环结构 的控制问题

有4种方法

用goto语句和if构成循环

用while语句

用do-while语句

用for语句

62 用goto语句和if语句构成循环 例: 对于计算 s=1+2+3+4+5+6+7+8+9+10

0

1

s

i

s+i s

i+1 i

y i10

n 输出s

s=0;

i=1; lable:s+=i; i++;

goto 语句可以构造循环 goto 语句可以构 但不主张用因为它容易破 造循环但不主张用 坏结构化程序设计

因为它容易破坏结构 化程序设计

if(i<=10) goto lable;

printf(%d,s) ;

语句标号 无条件转向语句

63 while语句 while语句是专门用于实现循环控制的语 句之一

其一般形式为 while (表达式) 语句

语句关键字 语句关键

表达循环条件的 表达式

循环体

含义当表达式的值为非0时执行循环体 否则执行后续语句

执行过程:

while (表达式) 语句

表 达 式 非0 循环体

0

例用while语句实现前面算法 s=0 main() i=1 { int i=1,s=0; while(i<=10) i10 { s=s+i; y i++; s=s+i i=i+1 } printf(

%d,s); }

n

注意与if语句的区别

循环体中要有使循环条件趋于成立的条件

while(i<=10) { s=s+i; i++; }

可简写为

while(i<=10) s+=i++;

64 do-while语句 do-while语句主要用于实现直到型循环

其一般形式为 do 循环体 while(表达式); 执行过程

循环体

非0真

表达式

0假

例用do-while语句实现前面算法 main() { int i=1,s=0; do s+=i++; while(i<=10); printf(

%d,s); 真 } 注意与while语句的区别

s=0 i=1 s=s+i i=i+1

i10?

65

for 语句

求解表达式1

for语句是一种使用最为灵活 并且是用得最多的循环控制语句

表达式2

其一般形式为

for(表达式1;表达式2;表达式3) 循环体

大体含义 对于的情况执行循环体内容

循环体

求解表达式3

例用for语句实现前面的算法

s=0;

for(i=1;i<=10;i++) s+=i;

标准形式

表达式1

表达式2

表达式3

循环体

可以理解 循?#32321;?#37327;i从初值1开始到终值10步长为1重复执 行循环体

for语句的常见变化 s=0; i=1; for(;i<=10;i++)s+=i;

省略表达式1

s=0; 省略表达式3 for(i=1;i<=10;)s+=i++;

s=0;i=1; 省略表达式1和3 for(;i<=10;)s+=i++;

s=0; i=1; for(;;) { s+=i++; if(i>10) break; } s=10;i=10; for(;--i;) s+=i;

省略表达式2

表达式2是 ?#25105;?#34920;达式

s=0; for(i=1,j=10;i<j;i++,j--) s+=i+j; 1 2 3 4 5 6 7 8 9 10

用逗号表达式

i

j

在程序设计中不要过分追求它的多变性

例求n! n!=1*2*3(n-1)*n 参照累加求和 main() { int i,n=5s=1; for(i=1;i<=n;i++)s*=i; printf(

s=%d,s); } 注意s的初值

注意当n较大时的情况

求和与连乘都是最常用的算法要熟练掌握

例求自然数1-100中能被3整除的数之和

main() { int i,s=0; for(i=1;i<=100;i++) if(i%3==0)s+=i; s+=i; for(i=3;i<=100;i+=3) s+=i; for(i=3;i<=100;i+=3) if (i%7)s+=i; printf(

%d,s); } 求能被3整除但不被7整除的数之和

例求?#25105;?00个数中的最大值

main() { int i,a,max; max=-32768; max=? for(i=1;i<=100;i++) for(i=1; ;i++) { scanf(%d,&a); if(a==-9999)break; if(a>max) max=a; } printf(

max=%d,max); } 循环体中没有引用循?#32321;?#37327;

i的作用 求?#25105;?#20010;数中的最大值

66 循环的?#30701;?循环体内又包含另一个完整的循环结构(多重 循环)

外重循环 for(i=1;i<=10;i++) { for(j=1;j<=20;j++) 内重循环 { s+=i+j; 执行200次 } }

要掌握多重循环 执行的全过程

以上多重循环结构可以简写为

for(i=1;i<=10;i++) for(j=1;j<=20;j++) s+=i+j;

例?#39029;?#34892;号乘以列号等于100的座位

main() { int i,j; for(i=1;i<=30;i++) for(j=1;j<=20;j++) if(i*j==100) printf(

%d,%d,i,j); }

注意循环的关系

例百钱买百鸡问题

给定100块钱要求正好买100只鸡已知公 鸡5元/只母鸡3元/只小鸡1元/3只?#20351;?鸡母鸡和小鸡应各买多少只 若考虑用方程组

x+y+z=100

5x+3y+z/3=100

是一多解问题

用测试法求解的程序 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(

%d,%d,%d,x,y,z); }

程序可进一步简化为 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(

%d,%d,%d,x,y,z); } } 用测试法求解问题的典型例子

测试法求解的程序设计有两个要点 通过循环列出所有可能的解

对所有列出的可能的解进行条件测试

例?#21495;?#26029;一个数m是否为素数

main() { 用测试法求解 int i,m; scanf(%d,&m); for(i=2;i<m;i++) if(m%i==0)break; if(i==m) printf(

%d is a prime,m); else printf(

%d is not a prime,m); }

例习题6.6 打印出所有的?#20843;?#20185;花数?#20445;?#25152;谓?#20843;?仙花数是指一个三位数其各位数字的 立方和等于该数本身

如153是一水仙花 数因为 153=13+53+33

main() { int i,j,k,n; for(n=100;n<=999;n++) { i=n/100; i=? j=n/10%10; j=? k=n%10; k=? if(i*i*i+j*j*j+k*k*k==n) printf(

%d,n); } } 通过循环列出n的所有可能的范围

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(

%d,n); } } 通过循环列出i,j,k的所有可能的范围

例习题6.4

求S=i! =1+2+3+4++19+20

i=1

20

main() { int i,j; float t,s=0; for(i=1;i<=20;i++) { t=1; for(j=1;j<=i;j++) t*=j; s+=t; s+=?; } printf(

s=%f,s); } 用两重循环实现

用递推法 求S=i! =1+2+3+4++19+20

i=1 20 i=2 20 i=2

20

=1+(i-1)!.i = 1+ti-1.i

递推公式ti=ti-1.i

main() { int i,j; float t=1,s=t; for(i=2;i<=20;i++) { t=t*i; s+=t; } printf(

s=%f,s); }

1 1 1 1 1 1 s= = + + + + + 2! 3! 4! 20! 1 i 1!

20

递推公式 ti= ti-1 / i

main() { int i,j; float t=1,s=t; for(i=2;i<=20;i++) { t=t*i; t=t/i; s+=t; } printf(

s=%f,s); }

习题6.3: s=a+aa+aaa+aaaa+aaaaa 2+22+222+2222+22222

递推公式ti= ti-1 10+a ? t *

main() { int i,j,a=2; float t=a,s=t; for(i=2;i<=5;i++) { t=t*10+a; s+=t; } printf(

s=%f,s); }

例求方程 2x3+3x2-4x+1=0的根

简单迭代法基本思想 将原方程f(x)化为x2=g(x1)

x2=(2x13+3x12+1)/4 迭代公式

迭代过程

假定一个x1 x2=g(x1)

x1=x2

不成立

|x2x1O<

成立

输出x2

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;

? x1=x2;

} printf(

%f,x2); }

牛顿迭代法基本思想

y f (x1)

f (x2)

x

x2 x1

牛顿迭代公式 f(x1)=f(x1)/(x1-x2) x2=x1-f(x1)/ f(x1)

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(

%f,x2); }

例求定积分 sinx dx a x

f (x)

b

数值积分

等分n

b-a h= n

a

x

b

x=a+(i-1).h 第i个矩形x=?

#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;

s+=h*y; }

y=sin(x)/x;

printf(

s=%f,s); }

第七章 数 组

71 数据结构与数组的概念 影响程序设计的因素除算法外还有数据结构

数据结构概念 编写一个程序除了重视算法的设计外还需 重视数据类型的选择即选择合?#23454;?#25968;据类型来 存放要处理的数据

在程序设计中数据类型就 称为数据结构选择合?#23454;?#25968;据类型实际上就是 进行数据结构的设计

在程序设计中有格言

数据结构+算法=程序 说明数据结构与算法同等重要算法依赖 于数据结构对于同一个问题的求解可以 采用不同的数据结构和不同的算法对不同 的数据结构有不同的算法其复杂程度?#19981;?不同选择合?#23454;?#25968;据结构可以降低算法 的复杂程度

因此在程序设计中应重视数 据结构的设计

例求?#25105;?00个数中的最大值

main() { int i,a,max; 用一个简单变量作为 max=-32768 数据结构合理算 法简单 for(i=1;i<=100;i++) { scanf(%d,&a); if(a>max) max=a; } printf(

max=%d,max); }

对于三个数的排序 main() 仍可用变量作 为数据结构 { int a,b,c,t; scanf(%d,%d,%d,&a,&b,&c); if(a<b) {t=a; a=b; b=t;} if(a<c) {t=a; a=c; c=t;} if(b<c) {t=b; b=c; c=t; } printf(

%d,%d,%d,a,b,c); } 对于很多个数的排序用变量会很复杂而用数组会 使算法很简单

数组的概念

int a[10]

数组名

a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]

下标

数组元素

一组具有同样类型的数据的集合 统一用一个名?#25191;?#34920;---数组名代表一组数

数组中的各成员称数组元素由数组名加 下标唯一地确定

将一组数用一个名?#25191;?#34920;便于管理

只有一个下标的数组称为一维数组

可有二维数组三维数组七维数组

72 一维数组的定义和引用 定义 一般形式 类型符 数组名[常量表达式]

int a[10];

类型符

float b[10]

数组名 长度

作用分配一组连续的内存单元

说明 数组必须先定义后使用

数组名的命名规则与变量相同

常量表达式表示元素的个数长度下标 从0开始

常量表达式不能包含变量即不允许作动态 定义

引用 逐个引用其元素不能进行整体引用

引用的一般形式 数组名[下标]

如a[0]=50; a[1]=100; a[2]=a[0]+a[1]; 与 a2=a0+a1有根本性的区别下标可变

例从键盘输入10个数

用变量(不方便) scanf(%d%d%d%d%d%d%d%d%d%d?#20445;?&a0,&a1,&a2,&a3,&a4,&a5,&a6,&a7,&a8,&a9); 用数组 (灵活方便) for(i=0;i<10;i++) scanf(%d,&a[i]); 用循环控制输入个数和下标的变化

注意下标的变化范围

初始化 在定义数组的同时给数组赋初值

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};

应用举例 1对100个学生的分数统计最高分最低分 和平均分

两种方法 用变量作为存放初始数据的数据结构 用数组作为存放初始数据的数据结构

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<min) min=a; aver+=a; } aver/=100; printf(

%d,%d,%f,max,min,aver); }

main() { int i,a[100],max,min; 用数组 float aver=0; for(i=0;i<100;i++) scanf(%d,&a[i]); max=a[0]; min=0; min=a[0]; max=0; for(i=0;i<100;i++) { if(a[i]>a[max]) max=i; if(a[i]>max) max=a[i]; if(a[i]<min) min=a[i]; if(a[i]<a[min]) min=i; aver+=a[i]; } 找最大最小的位置 aver/=100; printf(

%d,%d,%f,max,min,aver)

}

2统计高于平均分的人数

用变量 main() n=0; { int i,a,n; for(i=0;i<100;i++) float aver=0; { scanf(%d,&a); for(i=0;i<100;i++) if(a>aver)n++; { scanf(%d,&a); } aver+=a; printf(

%d,n); } } aver/=100;

数据结构不合理

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(

%d,n); }

13对100个学生的分数统计出每分一?#31561;?#25968;

0 main() 1 { int i,a; int i,a,n[101]; 2 for(i=0;i<101;i++) n[i]=0; 3 for(i=1;i<=100;i++) 4 { scanf(%d,&a); n[a]++; } 99 输出 100 }

完整程序 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(

%3d:%3d,i,n[i]); }

按10分一?#20302;?#35745; main() { int i,a,n[101]; i,a,n[11]; for(i=0;i<101;i++)n[i]=0; for(i=0;i<11;i++)n[i]=0; for(i=1;i<=100;i++) { scanf(%d,&a); n[a]++; n[a/10]++; } }

4对10个学生的分数按?#26377;?#21040;大的顺序排序 后输出

两种典型的排序算法选择法和起泡法

选择法基本思想 首先选择最小的数放在0位臵再在剩下的数 中选择最小的数放在下一位臵?#26469;?#31867; 推共进行9次选择

5 8 7 4 3 9 0 1 2 6

每次选择都要与其后的所有数进行比较换位

5 8 7 4 3 9 0 1 2 6

i

j

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

5

8

7

4

3

9

0

1

2

6

i

j

先找最小值所在的位臵最后?#20521;?#20301;: 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]<a[k])k=j; t=a[i]; a[i]=a[k]; a[k]=t; } for(j=0;j<10;j++) printf(%3d,a[j]); }

起泡法基本思想 首先将所有数中的最大值冒泡到最后位 臵再将剩下的数中的最大值冒泡到上一位 臵?#26469;?#31867;推共进行9次冒泡

每次冒泡都是一种翻滚过程即相邻两个 数进行比较换位

5

8

7

4

3

9

0

1

2

6

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]); } 要特别注意两个循环的范围

(5) 循环移位 对一数列中的每个数向后移3个位臵最后3个 数?#39057;?#26368;前面

5 8 7 4 3 9 0 1 2 6

1

2

6

5

8

7

4

3

9

0

用循环移位实现 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(j=1;j<=3;j++) k=a[9]; { k=a[9]; for(i=9;i>0;i--)a[i]=a[i-1]; for(i=1;i<10;i++)a[i]=a[i-1]; a[0]=k; }

for(i=0;i<10;i++)printf(%3d,a[i]); }

(6)狐狸找兔子问题绕 围绕着山顶有10个洞一只兔子和一只狐狸 分别住在洞里狐狸总想吃掉兔子

一天兔子 对狐狸说你想吃掉我有一个条件先把洞顺序 编号你从最后一个洞出发第一次先到第一个 洞找我第二次隔一个洞找第三次隔两个洞找 ?#26469;?#31867;推寻找次数不限我躲在一个洞里 不动只要找到我你就可以饱餐一顿

狐狸一想 只有10个洞寻找次数又不限那有找不到的呢 马上答应了条件结果狐狸跑断了腿也没找到 请问兔子躲在哪个洞里

1 10 2

3

9

4 8 5 6

7

算法思想

开辟数组每个元素代表一个洞并赋初值

0表示各个洞都还未找然后按规律找每 找一个洞对应的数组元素就赋值1表示已 找过最后根据数组元素值1与0来识别 各洞是否已找过

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); }

73 二维数组的定义和引用

定义 一般形式 类型符 数组名[常量表达式] [常量表达式]

int a[3][4]; 行 列 float b[5][10];

二维数组的逻辑结构就如同一张表格 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]

二维数组可以看作是一个特殊的一维数组

它的元素又是一个一维数组

C语言这样的处 理方法在很多情况下显得很方便

与一维数组相比二维数组的定义多一个 长度其元素多一个下标

在应用中如果要处理的数据如同一数列 则可定义一维数组来存放

而如果要处理的数 据如同一张表格则应定义二维数组来存放

引用 引用形式数组名[下标][下标] 如a[0][3]=a[1][2]+a[2][3]; 其元素有两个下标

例从键盘输入12个数到二维数组?#23567;?p> int a[3][4],i,j; for(i=0;i<3;i++) for(j=0;j<4;j++) scanf(%d,&a[i][j]); 需要用两重循环来控制两个下标的变化

如果键盘输入的数据是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(i=0;i<3;i++) for(j=0;j<4;j++) for(j=0;j<4;j++) for(j=0;j<4;j++) for(i=0;i<3;i++) scanf(%d,&a[i][j]); scanf(%d,&a[j][i]);

例输入一个表格的数据到二维数组中并找最 大值所在的位臵 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(

%d,%d,i1,j1); }

初始化

对二维数组赋初值的几种方法 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}};

举例 1矩阵的基本操作 二维数组的逻辑结构就如同一个矩阵因此 矩阵操作都可用二维数组实现

a11 a12 a13 a1n a21 a22 a23 a2n A= a31 a32 a33 a3n am1 am2 am3 amn

假定M=3N=4

求和 1 2 3 5 6 7 main() 9 10 11 { int a[4][4],i,j,s=0; 13 14 15 for(i=0;i<4;i++) for(i=0;i<4;i++) 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]; for(j=i;j<4;j++) s+=a[i][j]; for(j=0;j<4;j++) s+=a[i][j]; } for(i=0;i<4;i++) for(j=0;j<=i;j++) s+=a[i][j]; 上三角下三角主对角线

4 8 12 16

非方阵转臵aijbji

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 12

1 2 3 4

5 6 7 8

9 10 11 12

方阵转臵aijaji main() { int a[3][3],i,j,t; for(i=0; i<3; i++) for(j=i+1; j<3; j++) for(j=0; j<3; j++)

1 2 3 4 5 6 7 8 9

{t=a[i][j]; a[i][j]=a[j][i]; a[j][i]=t;} }

将矩阵中和值为最大的那一行元素与首行对换

main() { int a[3][4],i,j,t,s,smax=-32768,row; 1 5 3 8 for(i=0;i<3;i++) 4 6 1 7 { s=0;for(j=0;j<4;j++)s+=a[i][j]; 9 2 5 6 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;} }

74 字符数组 用于存放字符的数组称字符数组

字符数组的每一个元素存放一个字符

字符数组的独特之处 1字符数组可以看作字符串变量

2对字符数组可以进行某些整体操作

3有专用的字符串处理函数

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?} ; 也属于字符赋初值的形式

如果把字符序列看作一个整体字符串 则c就可看作是存放这个字符串的串变量

但必 须在字符序列后加上字符串结束标志后才 能成为完整的字符串

如c[4]=?\0?; 或 c[4]=0; a b c d \0

也可以按字符串形式初始化 char c[10]=abcd; char c[]=abcd; 分配5个字节

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]);

对于输入 for(i=0;i<10;i++) scanf(%c,&c[i]); 对数组元素操作 scanf(%s,c); 整体操作 对字符数组输入输出可以整体进行但不允许 整体赋值 char c[10]=abcd,x[10]; x=c; 不允许

对于二维字符数组可以看作是一维的字符 串数组

例从键盘输入10个?#35828;?#21517;字到计算机 main() { int i; char name[10][20]; 10个元素的一维字符串数组 for(i=0;i<10;i++) scanf(%s,name[i]); }

只给一个下标

3字符串处理函数 c语言的函数库中提供了一系列专用于字符串 处理的函数需要时可直接调用

1puts(字符串) 用于输出字符串

其中字符串可以是字符串常量也可以是字 符数组

例 char str[]=China; puts(str); puts(China); 两个输出等效

2gets(字符数组) 用于从键盘输入一个字符串到字符数组?#23567;?p> 函数返回字符数组的起始地址

例 char str[10]; gets(str); 执行该函数调用时计算机等待输入字符串

3strcat(字符数组,字符串) 用于将字符串连接到字符数组的后面

其中字符串可以是字符串常量也可以是字 符数组

例 char a[10]=abcd, b[10]=xyz; strcat(a,b); 与strcat(a,xyz)等效 puts(a); 输出结果是abcdxyz

4 strcpy(字符数组,字符串 用于将字符串拷贝到字符数组?#23567;?p> 其中字符串可以是字符串常量也可以是字 符数组

例 char a[10], b[10]=abcdef; strcpy(a,b); 与strcpy(a,abcdef)等效 不能用a=b 赋值 puts(a); 输出结果是abcdef

5 strcmp(字符串1字符串2) 用于比较两个字符串的大小

比较结果通过函数的返回值体现 字符串1=字符串2时返回0

字符串1>字符串2时返回一正整数

字符串1<字符串2时返回一负整数

两个字符串之间谁大谁小取决于最先有差 异的两个字符的ASCII代码的大小

如 strcmp(abcde,abcde); strcmp(a,ABCD)

返回0 返回正整数 strcmp(abcdefgh,abcxyz)

返回负整数

例从键盘输入两个字符串输出其中大的一 个

#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

6 strlen(字符数组) 测试字符串的实际长度从返回值得到

7 strlwr(字符串) 将字符串中的大写字母全改为小写字母

8 strupr(字符串) 将字符串中的小写字母全改为大写字母

注意在使用字符串处理函数时别忘了将 头文件string.h包含进去

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); }

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); }

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); }

第八章

函数

81 概述 C语言的程序除主函数外还可以有若干 个其他函数块状结构

一般把其中相对独立的算法和功能定义成 一个独立的函数以供需要的地方调用

优点 1 2 3 4 减少代码的重复现象

便于分工合作

便于阅读

便于独立算法的代码移植

82 函数的定义和调用 举例说明 对于求两个数中的最大值有三个步骤 1从键盘输入两个数给a和b

2求a和b中的最大值

3输出结果

把求最大值的算法部分定义成一个独立的 函数

函数类型 函数名 函数参数形参 int max(int x,int y) { int z; if(x>y)z=x; else z=y; return(z); } x y z main() { int a,b,c; scanf(%d%d,&a,&b); c=max(a,b); printf(%d,c);

}

a

3

b

5

c

说明 1程序由两个函数组成它们逻辑上相互独 立功能变量

2程序的执行总是从主函数开始主函数总 是被执行一次其他函数只有在被调用时才获 ?#27599;?#21046;

3函数调用有两个作用转移控制权和传递 参数

4return的作用也有两个?#33322;换?#25511;制权和返 回结果

5实参可以是常量变量或表达式但类型 要一致

定义一个函数除考虑算法外就是:如何设 计函数的参数通过何种途径?#25442;?#32467;果

例求自然数1100中的素数之和

? ? ) main() int prime( int m) { int i; { int i,s=0; for(i=2;i<m;i++) for(i=1;i<=100;i++) if(m%i==0) return 0; ? if(prime(i) ) s+=i; return 1; ? printf(

%d,s); else return 1; } }

例求5+7+4的值

int fac( int n ) ? ? { int i,s=1; for(i=1;i<=n;i++) s*=i; return(s); } main() { printf(

%d,fac(5)+fac(7)+fac(4)); }

main() { float s,fac(); 对被调函数声明 s=fac(5)+fac(7)+fac(4); printf(

%f,s); } float fac(int n) { int i; float s=1; for(i=1;i<=n;i++) s*=i; return(s); } 不需声明的情况int char 主调函数在后

若被调用的函数是库函数则应用#include 命令将所调用函数的有关信息包含进来如 例 #include math.h main() { float x,y; scanf(%f,&x); y=sin(x); printf(%f,y); }

83 函数的?#30701;?#35843;用 C语言的函数定义虽然相互平行相互独立的 但可以?#30701;?#35843;用形如 主函数 函数A 函数B

例求多项式 S= i!+ i+ i的值

1 7 15

5

11

20

float

fac(int n)

{}

a, float sum( int ? int b ) ? { int i; float s=0; for( i=a;i<=b;i++ ) s+=fac(i); i=? return(s); } main()

{

printf(%f,sum(1,5)+sum(7,11)+sum(15,20); }

84 函数的递归调用 在函数调用的过程中出现直接或间接地调用 该函数本身

如 f1() f2() f3() { { { f1(); f3(); f2(); } } }

直接 递归调用

间接 递归调用

在实际应用中有些问题既可用递归实现 也可不用递归如求n!

也有些问题非有递归不可如?#21495;?#22612;问 题

不少问题使用递归显得很方便

用递归方法求n! 递推公式 1 当n=0或n=1时 n!= n(n-1)! 当n>1时

float fac(int n) main() { float f; { float f; if(n==0||n==1) f=1; f=fac(4); else f= n*fac(n-1); ? printf(

%f,f); return(f); } } 递归调用的执行过程

主函数

函数fac

函数fac

f=fac(4);

n=4 if(n==0||n==1)f=1; else f=n*fac(n-1); return(f)

函数fac

n=3 if(n==0||n==1)f=1; else f=n*fac(n-1); return(f )

函数fac

n=1 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)

问题4个return的执行顺序后进先出

85 数组作为函数的参数 当要传递的参数较少时用简单变量作为 函数的参数是方便的,但当要传递的参数是批量 时需要用数组作为函数的参数

例编写函数求100个数的平均数

float aver(int a[100]) a[] { int i; float s=0; for(i=0;i<100;i++) s+=a[i] ; return(s/100); } main() { int x[100]; float av; 输入x

对应的实?#25105;?#24212;为数组 av=aver(x); 型参数组的长度可省略 }

通用函数考虑 float aver(int a[],int n) { int i; float s=0; n=5; for(i=0;i<n;i++) s+=a[i]; a[0]=3 ; return(s/n); }

a

3

main() { int x[100],n=100; float av; 输入x av=aver(x,n); }

n 100

x

n

5 100

值传递 地址传递

例阅读程序 main() swap(int a, int b) { int a=3,b=5; { int t; swap(a,b); t=a; a=b; b=t; printf(

%d,%d,a,b); printf(

%d,%d,a,b); } } a

3

b

5

a

b

以下程序的运行结果是 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

例?#21495;?#24207;

void sort(int a[], int n) { int i,j,t; for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) if(a[i]>a[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]); }

x

a

例方阵转臵二维数组情况

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); 行长不等情况通用函数的处理

通用函数按一维数组处理 at(int a[],int n) { int i,j,t; for(i=0;i<n;i++) for(j=i+1;i<n;i++) { t=a[i*n+j]; a[i*n+j]=a[j*n+i]; a[j*n+i]=t;} } int x[3][3]; at(x,3);

86 局部变量和全?#30452;?#37327; 1局部变量 在函数内定义的变量称局部变量

局部变量只在该函数内使用

float f1(int x) int f2() main() { int i,j; { int a,b,c; { int m,n; { int i,j; } } } }

说明 1任何函数包括主函数内定义的变量都 是局部变量

2不同函数内定义的变?#32771;?#20351;同名也互不干 扰

3复合语句中定义的变量只在该复合语句中 有效

2 全?#30452;?#37327; 在函数以外定义的变量,也称外部变量

全?#30452;?#37327;可以为为本文件中其它函数所共用

它的作用范围是从定义变量的位臵开始到本源 文件结束

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; }

全?#30452;?量c1,c2 的作用 范围

全局 变量 p,q的 作用 范围

全?#30452;?#37327;主要用于作为不同函数间数据传 递的桥梁

例编写一个函数求n个数中的最大值 最小值和平均值

并编写主函数完成输入100 个数调用该函数进?#22411;?#35745;输出结果

int max,min; float aver(int a[],int n) 用全?#30452;?#37327;传递结果 { int i; float s=0; for(i=0;i<n;i++) { if(a[i]>max)max=a[i]; if(a[i]<min)min=a[i]; s+=a[i]; } return (s/n); } main() { int x[100],i; float av; for(i=0;i<100;i++) scanf(%d,&x[i]); av=aver(x,100); printf(

%d,%d,%f,max,min,av); }

分析不用全?#30452;?#37327;的情况 float aver(int a[],int n) { int i; float s=0; int max,min; for(i=0;i<n;i++) { if(a[i]>max)max=a[i]; if(a[i]<min)min=a[i]; s+=a[i]; } return (s/n); } main() { int x[100],i; float av; int max,min; for(i=0;i<100;i++) scanf(%d,&x[i]); av=aver(x,100); printf(

%d,%d,%f,max,min,av); }

int max,min; float aver(int a[],int n) max min { int i; float s=0; for(i=0;i<n;i++) { if(a[i]>max)max=a[i]; if(a[i]<min)min=a[i]; s+=a[i]; 分析全?#30452;?#37327;与局 } return (s/n); 部变量同名的情况 } main() { int x[100],i; float av; int max,min; max min for(i=0;i<100;i++) scanf(%d,&x[i]); av=aver(x,100); printf(

%d,%d,%f,max,min,av); }

阅读程序给出运行结果 int a=4,b=5,c=6; a b int f(int a,int b) 4 5 { a/=2; c+=b-a; a b return(a+b+c); 2 4 7 } a main() 2 { int a=2,d; d=f(a+2,a+b); printf(

%d,%d,%d,%d, a,b,c,d); }

c 6 11

d 20

87 变量存储类别 1动态存储方式和静态存储方式

变量 变量

局部变量 全?#30452;?#37327; 动态存储方式变量 静态存储方式变量

空间角度 生存期角度

静态存储方式在程序运行期间分配固定存储单 元的方式

动态存储方式在程序运行期间根据需要动态分 配存储单元的方式

用户区

程序区 静态存储区 动态存储区

程序开始运行时分配空间运行结束时释放 主要存放全?#30452;?#37327;和静态局部变量 根据需要动态分配动态释放的区域

主要存放动态局部变量和现场保护等

存放在静态存储区的变量静态存储方式

存放在动态存储区的变量动态存储方式

2局部变量的存储方式 每个局部变量在定义时可以指定其存储方式 即对每个局部变量的定义除定义其数据类型外 还应定义其存储方式

定义存储方?#25509;auto 自动的static静态的

int f1() { auto int a=1; } 缺省时为auto

int f2() {static int b=1; }

动态局部变量在函数调用时分配赋初值 调用结束时释放

静态局部变量在第一次调用时分配赋初 值调用结束时不释放其单元及其值仍保留 下次调用时不重?#36335;?#37197;不重新赋初值

在实际应用中如果希望在函数调用结束 后仍保留某个局部变量的值给下次调用时使用 则可定义该变量为静态的

例编写一个函数计算 2x-1 y= 3x+1 4x+2 第一次计算 第二次计算 其他

float f ( float x) { float y; static int n=1; if(n==1)y=2*x-1; else if(n==2)y=3*x+1; else y=4*x+2; n++; return(y); }

main() { printf(%f,f(2.0)); printf(%f,f(1.0)); printf(%f,f(3.0)); }

3 全?#30452;?#37327;的存储方式

全?#30452;?#37327;都是静态存储方式不允许用auto来 定义全?#30452;?#37327;

可以用static来定义全?#30452;?#37327;

如 static int int y; x; 静态全?#30452;?#37327;内部的 非静态全?#30452;?#37327;外部的

对于非静态全?#30452;?#37327;其它文件中的函数只 要用extern加以外部说明就可以访问

而对于静态全?#30452;?#37327;只局限在本文件中 的所有函数访问其它文件中的函数即使用 extern加以外部说明也不能访问

int y; int y; static main() { y=0; } int f1() { y=2; }

extern int y; int f3() { y=10; } int f4() { y=100; }

88 内部函数和外部函数 用extern加以定义的函数称外部函数

用extern加以定义的函数称外部函数

extern int f1() static int f2() { { } } 缺省为外部函数

外部函数可以为其他文件中的函数所调用

内部函数只为本文件中的函数所调用保护

main() { f3(); 允许 } int f1() { f4(); 不允许 }

extern int f3() { f4(); 允许 }

static int f4() { }

第九章 编译预处理

C语言提供了一些以#开头的指令如

#define

#include

这些指令是在编译以前就事先进行处理的因 而称为编译预处理指令

可以用编译预处理指令实现以下三种功能

1 宏定义

2 文件包含

3 条件编译

91 宏定义

1不带参的宏定义 用一个指定的标识符宏名代表一个字符串

一般形式

#define

标识符

字符串

3.1415926

如#define PI

有了这一宏定义后程序中凡是用到3.1415926 的地方都可以以宏名PI出现

例定义一个宏名来代表一个参数

#define PI 3.1415926 main() { float r=2,c,s; c=2*PI*r; s=PI*r*r; } ?#20302;?#22312;对程序进行编译以前首先将所有的编 译预处理指令进行预处理对本例来说就是 将程序中所有的宏名PI还原成3.1415926 宏展开

例定义一个宏名来代表一个数据个数

#define N 100 main() { int a[N],i; float s=0; for(i=0;i<N;i++)scanf(%d,&a[i]); for(i=0;i<N;i++)s+=a[i]; s/=N; printf(

%f,s); }

宏名N---符号常量可以作为数组说明的长度

方便修改参数

例定义一个宏名来代表一个计算公式

#define PI 3.1415926 #define AREA PI*r*r main() { r=3,s; s=AREA; printf(

%f,s); } 注意?#21512;低?#23545;宏定义的预处理是一个字符串的 还原过程不要把宏名看成一个整体

注意以下程序的运行结果

#define F x+y

main()

{ int x=3,y=5,z;

z=2*F;

printf(

%d,z);

}

2带参的宏定义

宏定义也可?#28304;?#21442;数其一般形式为

#define

宏名参数表 字符串

a*b

如#define S(a,b)

area=S(3,2);

定义宏名S代表矩形面积参数a,b为边长

宏展开过程

92 文件包含处理 在一个源文件中将另一个源文件的内容 包含进来

文件包含指令的一般形式 #include 或 #include 文件名 <文件名>

file1.c

#include file2.c

file2.c

file1.c

B

B A

A

例 #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(

%f,area); }

f.c

#include f.h main() { printf(

%f,f1(3.0)); printf(

%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); }

f.h

#define PI 3.1415926 float f1(float r); float fac(int n);

第十章

指针

指针:C的一个重要概念重要特色

它使C 正确而灵活地运用它就可以方便地处理很多 其它高级语言所不能处理的问题

不掌握指针等于没有掌握C语言的精华

具备了强大的功能使C成为程序设计语言之首

101 指针的概念

简单地说指针就是地址

要掌握指针的概念就必须弄清

内存地址概念? 变量与地址的关系? 如何通过地址进行变量的存取?

说明例 int i,j,k; i=3; j=6; k=i+j;

内存用户数据

程序经编译后 变量名就不 复存在以地 址对应

1000 1002 1004

3 6 9

i j k

对变量值的存取总是按地址进行的----直接访问

也可以采用间接访问方式

先将变量i的地址存放到另一变量p1中 要访问i时先取出p1的内容变量i的地址 再去访问该地址所对应的内存单元中的内容 变量i的值

内存用户数据

int i,j,k; i=3; j=6; k=i+j; int *p1, *p2; p1=&i; p2=&j;

1000 1002 1004

3 6 9

i j k

2000 2004

1000 1002

p1 p2

在以上概念的基础上对指针下定义 变量的地址就是该变量的指针

存放地址的变量称指针变量

1000 1002

i j k

p1是指向变量i的指针变量

1004

1000 p1 1002 p2

102 变量的指针

变量的指针 指针变量 指向变量的指针变量

用*代表指向 如*p1代表它所指向的变量i同一内存单元

以下两个语句等价 i=3; 直接访问 *p1=3; 间接访问

内存用户数据

int i,j,k; i=3; j=6; k=i+j; int *p1, *p2; p1=&i; p2=&j; *p1=3;

1000 1002 1004

3 6 9

i j k

2002 2004

1000 1002

p1 p2

指针变量的定义 指针变量也必须先定义后使用

int *p1; 注意 *表示该变量为指针变量但变量名是p1

一个指针变量只能指向同一类型的变量

int i,*p1; float a; p1=&i; 合法 p1=&a; 不合法

指针变量的引用 两种用法 用地址运算符& p1=&i

用指针运算符* 实行间接访问 *p1=100; k=*p1; 注意指针变量只能放地址指针

p1=100; 不允许

例 main() { int a=100,b=10; int *p1,*p2; 定义指针变量?#24418;?#20855;体指向 p1=&a; p1指向a p2=&b; p2指向b printf(

%d,%d,a,b); printf(

%d,%d, *p1,*p2); }

注意要区别定义和引用中的*

要特别注意以下用法的后果

int *p1; *p1=100;

例输入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<b) { p=p1; p1=p2; p2=p;} printf(

%d,%d,a,b); 2000 1000 p1 1002 printf(

%d,%d,*p1,*p2); 2004 1002 p2 1000 } 2006 p

改变p1和p2的指向

重要概念 只要得到某变量的地址指针就可通 过指针而不需通过逻辑名来访问该变量

手段用指针变量保存变量的地址

指针变量作为函数的参数

可将指针变量作函数的参数接受实参

地址获得具体指向进而通过指针变?#32771;?p>接访问主调函数的变量

swap(int *p1, int *p2) { int t; t=*p1; *p1=*p2; *p2=t; } main() { int a,b; scanf(%d,%d,&a,&b); if(a<b)swap(&a,&b); printf(

%d,%d,a,b);

跨越逻辑上的限制

1000 1002

9 5 5 9

a b

2000 2004 2006

1000 1002

p1 p2 t

swap(int p1, int p2) 不用指针变量情况 { int t; t=p1; p1=p2; p2=t; 1000 5 } 1002 9 main() { int a,b; 5 9 scanf(%d,%d,&a,&b); 2000 if(a<b)swap(a, b); 2004 5 9 printf(

%d,%d,a,b); 2006

a b

p1 p2 t

重要概念使用指针变量作函数参数 被调函数可以通过主调函数给定的地址去 操作主调函数中的局部变量

可用于传递多个结果

例编写函数求一元二次方程的两个实根

#include math.h int root(float a,float b,float c, float *x1, float *x2 ) ? { float d; d=b*b-4*a*c; if(d<0||a==0)return(0); ? *x1 =(-b+sqrt(d))/2/a; *x2 =(-b-sqrt(d))/2/a; ? return(1); 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(

%f,%f,xa,xb); }

例求n个数的最大值最小值和平均值

float aver(int a[], int n, int *max, int *min) { int i; float s=0; *max=a[0]; *min=a[0]; main() for(i=0;i<n;i++) { int a[100],i,max,min; { s+=a[i]; float av; if(a[i]>*max) *max=a[i]; for(i=0;i<100;i++) if(a[i]<*min) *min=a[i]; scanf(%d,&a[i]); av=aver(a,100,&max,&min); } printf( %d,%d,%f,max,min,av); return(s/100); } }

103 数组的指针 数组有一个首地址: 数组的指针

每个数组元素也都有地址: 数组元素的指针

2000 2002 2004 2006

5 3 2 1 6 8 7 4

指向数组元素的指针变量 p int a[10],*p; p=a; 指向数组 p=&a[0]; 指向数组元素

2000 2002 2004 2006

5 1 2 4 7

6 8 0 3 9

通过指针引用数组元素

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而是加一个数据类型单 位

一般地,当p指向a[0]时 p+i a+i &a[i] a[i] p[i]

*(p+i)

*(a+i)

即以下几个语句等效

a[i]=10; *(p+i)=10; *(a+i)=10; p[i]=10;

例从键盘输入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);

累加求和的各种用法 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++; for(p=a;p<a+10;p++)s+=*p; 后两种用法效率高

等效于*(p++)

注意不能使用a++

使用指针变量访问数组时要特别注意指针 变量的当前值

p 注意下例 main() { int a[10],*p=a,i; for(p=a;p<a+10;p++) scanf(%d,p); p=a; for(i=0;i<10;i++)printf(%d,*p++); }

数组名作为函数参数

有了指针概念的基础上重新回顾数组名作 为函数参数时数据的传递情况 例将数组a中的n个数按相反顺序存放

int inv(int x[], int n) { i,j,m,t; x a m=(n-1)/2; { for(i=0;i<=m;i++) { j=n-1-i; t=x[i]; x[i]=x[j]; x[j]=t; } } } a与x共用同一片内存单元

main() int a[10],i; 输入a inv(a,10); 输出a

int inv(int *x, int n) { i,j,m,t; m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i; { j=n-1-i

t=*(x+i); t=x[i]; *(x+i)= *(x+j); x[i]=x[j]; *(x+j)=t; x[j]=t; } 指针法 } 下标法 }

指针变量作函数参数时的传递情况

a

1000

main()

{

int a[10],i; 输入a inv(a,10); 输出a

}

x 1000

进一?#25509;?#21270; int inv(int *x, int n) { int *i=x,*j=x+n-1,t; for(;i<j;i++,j--) i { t=*i; j *i=*j; *j=t; } } 优点简练 效率高

main() { int a[10],i; 输入a inv(a,10); 输出a }

例选择法排序函数

void sort(int *a, int n) void sort(int *a, int n) { {int i,j,t; int i,j,t; for(i=0;i<n-1;i++) for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) for(j=i+1;j<n;j++) if(*(a+i)>*(a+j)) if(a[i]>a[j]){t=a[i]; a[i]=a[j]; a[j]=t;} } {t=*(a+i); *(a+i)= *(a+j); *(a+j)=t;} 只将形参改为指针变量仍按下标法使用 } 按指针法使用

进一?#25509;?#21270; void sort(int *a, int n) { int *i, *j,t; for(i=a;i<a+n-1;i++) for(j=i+1;j<a+n;j++) if(*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]); }

分段排序 main() { int a[10],j; for(j=0;j<10;j++)scanf(%d,a+j);

sort(a,5); sort(a+5,5);

for(j=0;j<10;j++)printf(%5d,a[j]);

}

指向多维数组的指针和指针变量

从本质上说多维数组的指针与一维数组 的指针相同但在概念上和使用上多维数组 的指针要复?#26377;?p>以二维数组的指针为例

二维数组的地址 : 讨论以下用法的效果 讨论以下用法的效果 for(i=0;i<3;i++) scanf(%d,a[1]+i+2); for(i=0;i<3;i++) scanf(%d,a+i+1); scanf(%d,a[1]+i); 一维a&a[i]a+i scanf(%d,a+i); 输入数据1 2 3 输入数据1 2 3 二维

int a[3][4]

a+0

1000 1008 1016

3 2 1

5 4 6

8 6 0

7 9 4

a[0]

a+1

a+2

a[1]

a[2]

a&a[i][j]a+i (行指针)a[i](特殊的一维数 组元素列指针)a[i]+j

注意指针运算符*作用在行指针上的结果仍是指 针----列指针; *作用在列指针上的结果---具体元素

a+0 a+1 a+2 1000 1008 1016 a[0] a[1] a[2] *(a+1)

*(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 ?

如果要通过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)

例求数组a的所有元素之和

可有多种用法

for(i=0;i<3;i++) for(j=0;j<4;j++) s+=a[i][j];

for(i=0;i<3;i++) for(j=0;j<4;j++) s+=*(*(a+i)+j);

for(i=0;i<3;i++) for(j=0;j<4;j++) s+=*(a[i]+j);

指向二维数组的指针变量 同样可使一个指针变量p指向二维数组a再 通过p访问数组元素

main() { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int i,k,*p; 1 2 3 4 p=a; 5 6 7 8 k=*p;? k=p[1][2]; k=p[1*4+2]; 不合法 合法 9 10 11 12 k=*(*(p+1)+2); k=*(p+1*4+2); k=*(p+2);? for(p=a;p<a+2;p++)printf(%3d,*p); ? }

例求矩阵的上三角元素之和

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(

%d,s); }

指向由m个元素组成的一维数组的指针变量

可以这样定义一个指针变量 int (*p)[4] 表示p为指向由4个元素组成的行指针变量

当p=a时可通过p引用a[i][j]

p[i][j] 或 *(*(p+i)+j)

例求矩阵的上三角元素之和

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(

%d,s); }

多维数组的指针作函数参数 用于接受实参数组地址的形参可用两种行指 针和列指针

以方阵转臵为例 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; } }

用列指针 void at(int *a,int n) { int i,j,t; for(i=0;i<n;i++) for(j=i+1;j<n;j++) { t=a[i*n+j]; a[i*n+j]=a[j*n+i]; a[j*n+i]=t; } } 优点?#21644;?#29992; 在编通用函数时一般使用列指针

104 字符串的指针和指向字符串的指针变量

字符串的表示形式

可用两种方法访?#39318;?#31526;串

用字符数组存放字符串

用字符指针指向一个字符串

main() { char c[5]=abc; 定义字符数组并将字符串存入 char *p1=c,*p=abc;定义指针变量指向字符串 printf(%s,c); 通过数组名访?#39318;?#31526;串 printf(%s,p); 通过指针变量访?#39318;?#31526;串 printf(%-c,*(p+3)); 通过指针变量访?#39318;?#31526; }

c

1000

a

b c \0 p 1000

a

b

c \0

与其它一维数组的指针相比字符串的指 针有其独特之处

对字符串的操作依赖于结束符

可以整体赋初值

有各种字符串处理函数

可以通过指针对字符串进行整体访问

本节重点掌握 通过数组和通过指针操作字符串的 基本方法

常用的字符串处理方法

例字符串拷贝操作

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); }

将拷贝操作编成一函数 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; }

字符串合并函数 void append_string(char *from,char { for(;*to; to++); for(; *from;) *to++=*from++; *to=0; }

*to)

阅读程序 void f (char *c) { c+=2; (*c)++; c++; *c=0; }

main() { char c[20]=abcdef; f(c+1); printf(%s,c); }

内存空间的动态分配

在程序设计中对于要处理的批量数据 我们往往是选用数组作为存放这些数据的数据 结构然而数组有一个明显的缺点就是在 定义数组时其长度必须是常值无法根据需 要动态地定义

这样在很多情况下不是定 义的数组长度不够就是定义太长以至于浪?#36873;?p>采用动态分配可以克服这一缺点并且可 ?#36816;?#26102;释放

动态分配内存空间步骤 定义一指针变量

申请一片内存空间并将其首地址赋给指 针变量

此时便可通过指针变量访问这片内存

用完后释放这片内存空间

int *p; p p=malloc(byte); free(p);

以上函数的原形在stdio.h?#23567;?p> 例对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<n;j++) scanf(%d,a+j); sort(a,n); for(j=0;j<n;j++) printf(%5d,a[j]); free(a); }

105 函数的指针和指向函数的指针变量

用函数指针变量调用函数

可以用指针变量指向一个函数一个函数在 编译时被分配给一个入口地址这个入口地 址就称为函数的指针

可以用指针变量指向 函数然后通过该指针变量调用此函数

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(

%d,c); }

把指向函数的指针变量作为函数参数

指向函数的指针变量最常见的用途是把它 作为函数的参数用于接受主调函数传来的某 一函数的入口地址从而在被调函数中可以通 过该指针变量调用它所指向的函数这样被 调函数中就可实现非固定函数的调用?#28304;?#21040; 编写通用函数的目的

关键问题如何处理?#25442;?#20989;数是未知的

例用矩形法编写一个通用的求定积分的函数

double {

intgral (double double 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);

a,double b,int (*f)( ) )

n,

double f1(double x) { return(3*x*x+2*x-1); } main() { double s; s=integral(1.0,2.0,100,f1); }

}

第十一章

11.1 概述

结构体

在实际应用中有不少应用问题如果只采 用已学的变量和数组作为数据结构显得很不方 便

例输入100个学生的学号姓名和考试成 绩编写程序?#39029;?#39640;分者和低分者

用变量和数组作数据结构可编写程序如下

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<minscore) {minscore=score; minnum=num; strcpy(minname,name);} } 输出 }

明显缺点

变量过多同一学生的各个数据无联系 没有整体概念不便管理

操作不便如更新过程

显然选用一种能把一个学生的数据构造成 一个整体的构造型数据结构更合适但不能是数 组

对于这种情况可以将一个学生的数据定义 为一个结构体类型

struct student { int num; char name[20]; int score; };

类型名

成员表

定义了一个结构体类型它包含三个成员

11.2 定义结构体类型变量的方法

前面定义的结构体类型只是一种模型?#20445;??#36129;?#39035;定义结构体变量后才能存放数据

定义结构体变量有三种方法

1先定义结构体类型再定义结构体变量

定义了结构体类型后

struct

student

st, stmax, stmin; 变量名

类型符

定义了三个结构体变量每个变量包含三个成 ?#20445;?#27599;个变量可存放一个学生的数据

2在定义结构体类型的同时定义结构体变量

struct student

{

int num;

char name[20];

int score;

}st, stmax, stmin;

3直接定义结构体类型变量 struct 不出现类型名 { int num; char name[20]; int score; }st, stmax, stmin;

常用第一种方法

说明 类型与变量不同只对变量分配空间与操作

对成员可以单独使用相当于普通变量

成员也可以是一个结构体变量

struct date struct student { int month; { int num; int day; char name[20]; int year; struct date birthday; }; }st1, st2;

成员名可以与程序中的变量名相同两

者代表不同的对象

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) ;

可以引用成员的地址

如果成员本身?#36136;?#19968;个结构体类型则 要用若干个成员运算符一级一级地找到最 低一级的成?#20445;?#21482;能对最?#22270;?#30340;成员进行存 取与运算

st1.birthday.year=1960; st1.birthday.month=5; st1.birthday.day=15;

整体引用

可以对结构体变量进行整体赋值

stmax=st; 将st中的所有内容赋值给stmax

对结构体变量的整体操作只限于赋值操作 和参数传递而且要求类型一致

不能对结构 体变量进行整体输入输出

结构体应用举例

编写程序输入100个学生的学号姓名和 考试成绩?#39029;?#39640;分者和低分者

struct student { int num; char name[20];

int score;

};

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<stmin.score) stmin=st; } printf(

%5d%15s%5d,stmax.num,stmax.name, stmax.score); printf(

%5d%15s%5d,stmin.num,stmin.name, stmin.score); }

11.4 结构体变量的初始化

对结构体变量可以在定义时指定初始值 struct student { int num; char name[20]; int score; }st={1001,wang,95};

11.5 结构体数组 可以定义结构体数组来存?#25490;?#37327;数据

结构体数组的定义 struct student { int num; char name[20]; int score; }

struct student a[100]; 定义a数组可?#28304;?#25918;100个学生的数据

a数组的每个元素又是一个结构体变量

结构体数组的初始化 在定义结构体数组的同时指定初值

struct student { int num; char name[20]; int score; }

struct student a[2]= {{1001,LiLi,85},{1002,wang,90}};

或 struct student { int num; char name[20]; int score; } a[2]= {{1001,LiLi,85},{1002,wang,90}};

结构体数组元素的引用 成员引用 a[0].num=1001; strcpy(a[0].name,wang); a[0].score=85; 整体引用 a[1]=a[0]; 与普通数组元素的引用相同

结构体数组的应用

输入100个学生的学号姓名和考试成绩

然后按从高分到低分的顺序排列后输出

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);

for(i=0;i<99;i++)

for(j=i+1; j<100; j++)

if(a[i].score<a[j].score)

{t=a[i]; a[i]=a[j]; a[j]=t;} 整体引用

for(i=0;i<100;i++)

printf((

%5d%15s%5d,a[i].num,

a[i].name,a[i].score);

}

例p266例11.2

对候选?#35828;?#31080;的统计程序

设有三个候选人 ?#30475;问?#20837;一个得票候选?#35828;?#21517;字要求最后输 出各候选?#35828;?#24471;票结果

#include string.h struct person { char int name[20]; count;

}leader[3]={Li,0,zhang,0,wang,0};

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++;

}

printf(

);

for(i=0;i<3;i++)

printf(

%15s%5d,

leader[i].name,leader[i].count);

}

11.6 指向结构体类型数据的指针

指向结构体类型变量的指针 struct student st, st1

struct student *p;

p=&st;

定义指向结构 体类型数据的 指针变量p

通过指针变量引用结构体变量 成员引用

(*p).num=1001;

(*p).score=85;

或 p->num=1001;

或 p->score=85;

strcpy((*p).name,wang);

或 strcpy(p->name,wang);

整体引用

st1=*p; 等效于 st1=st;

指向结构体数组的指针 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);

一般地 (*(p+i)).num=1001; (*(p+i)).score=85; 或 (p+i)->num=1001; 或 (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];

用结构体变量和指向结构体的指针作函数参数

用结构体变量作函数参数时对应的实参应 该是同类型的结构体变量或数组元素参数 传递是值传递

用指向结构体的指针作函数参数时对应的 实参应该是同类型的结构体变量的地址或数组 的地址参数传递是地址传递

main() { struct student st={1001,LiLi,70}; f(st); printf(

%5d%10s%5d, st.num,st.name,st.score); } f(struct student a) { a.score=90; printf(

%5d%10s%5d, a.num,a.name,a.score); }

st 1001 LiLi 70

a 1001 LiLi 70 90

main() 2000 { struct student st={1001,LiLi,70}; f(&st); } f(struct student *a) { a->score=90; }

st 1001 LiLi 70 90

printf(

%5d%10s%5d,st.num,st.name,st.score);

a 2000

printf(

%5d%10s%5d,a->num,a->name,a>score);

通过指针变量a可以访问它所指向的结构体

11.7 用指针处理链表

链表概述 链表是一种重要的数据结构动态数据结构

以具体例子来说明链表的概念及其应用

例选择合?#23454;?#25968;据结构来存放一批学生的学号 及考试成绩以便进一步处理

由于学生人数未知用静态数据结构不合适

用链表处理较恰当

用链表处理该问题的基本思路 将各学生的数据进行离散存放来一个学生 就分配一小块内存结点

并将各结点用指针 ?#26469;?#36830;接起来链表

head 学号 成绩 指针 学号 成绩 指针 学号 成绩 指针 学号 成绩 指针 学号 成绩

NULL

每结点应包含下一结点的开始地址

最后一个结点中的指针为空

链?#20998;?#38024;指向第一个结点,是访问链表的重要依据

这样的链表?#39057;?#21521;链表

一个结点可用如下结构体描述 typedef struct student { int num; 学号 int score; 成绩 struct student *next; 下一结点的首地址 } STU;

typedef : 自定义类型符见11.10

单向链表的建立 输入一个学生的数据

分配结点空间数据存入

将该结点的首地址赋给上一结点的next若 该结点是第一个结点则赋给?#20998;?#38024;

将该结点的next臵为空表示该结点为当前 的最后结点

head 学号 成绩 next 学号 成绩 next 学号 成绩 next 学号 成绩 next

学号 成绩

NULL

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; 学号 学号 head 学号 } 成绩 成绩 成绩 NULL return head; next next }

单向链表的访问

以输出为例 通过?#20998;?#38024;找到第一个结点. 输出当前结点的内容并通过next找到后 继结点直到next为空.

void output(STU *head) { STU *p=head; while(p) { printf("

%d %d",(*p).num,(*p).score); p=(*p).next; } }

head 学号 成绩 next 学号 成绩 next 学号 成绩 next 学号 成绩

NULL

删除结点操作 按链表的访问方法找到相应结点

若该结点是第一个结点则将后继结点指针 赋给?#20998;?#38024;

若该结点是最后一个结点则将前缀结点的 next臵为空

若该结点是中间结点则将后继结点指针赋 给前缀结点的next

释放该结点所占的内存单元

head 学号 成绩 next 学号 成绩 next 学号 成绩

NULL

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; }

插入操作

head 学号 成绩 next 学号 成绩 next 学号 成绩

NULL

学号 成绩 next

假定将结点p 插入到结点p0的后面, 则插入操作 的关键为 p->next=p0->next; p0->next=p;

第十三章

文件

3.1 文件概述 文件概念 所谓文件就是存储在外部介质上 的信息集合

根据存储的介质不同可分为 ?#25490;?#25991;件磁带文件等

根据内容的不同可分为 程序文件数据文件等

使用文件输入输出的必要性

这里主要讨论数据文件的输入输出即 如何将文件中的数据输入到程序的数据 结构中如何将程序的数据结构中的数据 输出到文件?#23567;?p> 以往的输入输出方法 键盘输入 屏幕输出

这种方法不适用于数据量大的情况

键盘输入和屏幕输出例 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]); }

缺 点 可能出现重 复输入

输出的数据 不能保存不 便于进一步使 用

}

采用文件输入输出可以克服这些缺点

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]);

文件

文件

C文件分类 按在?#25490;?#19978;存储的形式不同可分为 文本文件 以ASCII字符存放可见可编辑占 空间大

二进制文件 以二进制形式存放不可见不可编辑 占空间小

使用时可根据需要选择

13.2 文件类型指针 每个被使用的文件都在内存中开辟一 个区用来存放文件的有关信息

这些信 息保存在一个FILE类型的结构体变量?#23567;?p> 若 FILE *fp; 则fp就称为指向文件类 型的指针变量

访问文件通过文件指针进 行

FILE结构体类型是?#19978;低?#23450;义的

具体定义如下

typedef struct { short level; 程度 unsigned flags; char fd; unsigned char hold; short bsize; unsigned char *buffer; unsigned char *curp; unsigned istemp; short token; }FILE;

缓冲区满或空的

文件状态标志 文件描述符 如无缓冲区不读取字符 缓冲区的大小 缓冲区的位臵 当前读写指针 临时文件指示器 用于有效性检验

13.3 文件的打开与关闭 对文件的读写之前应?#25353;?#24320;该文件

使用结束后关闭此文件

文件的打开fopen 函数 用fopen函数实现对文件的打开

fopen函数调用的一般形式 FILE *fp; fp=fopen(文件名读写方式)

例如 fp=fopen(a1.txt, r); 以只读方式打开文件a1.txt

fopen函数返回指向a1.txt文件的指 针即fp是指向a1.txt文件的指针变 量往后就可以通过fp访问a1.txt文 件

文件读写方式 r 按只读方式打开一个文本文件

w a 按只写方式打开一个文本文件 按追加方式打开一个文本文件

rb 按只读方式打开一个二进制文

wb 按只写方式打开一个二进制文

r+ w+ a+

按读写方式打开一个文本文件 按读写方式建立一个新的文本文件 按读写方式打开一个文本文件

rb+ 按读写方式打开一个二进制文件

wb+ 按读写方式建立一个新的二进制文

ab+ 按读写方式打开一个二进制文件

说明 1不能用r方式打开一个不存在 的文 件r方式只读不能写

2w 方式只写不能读具有建立 和 覆盖功能

3调用fopen函数时如果返回NULL 则 表?#25964;?#24320;不成功

文件的关闭fclose 函数

在使用完一个文件后应用fclose 函数关 闭文件形式为 fclose文件指针

如 fclose(fp);

关闭后fp不再指向该文件

13.4 文件的读写 文件打开后就可以对它进行读写了

文本文件的读写 即如何将以文本方式存放的文件输入到程 序的数据结构?#23567;?p>如何将程序的数据结构中 的数据以文本方式输出到文件?#23567;?p> 用于对文本文件读写的函数有 fscanf

fgetc, getc fgets

fprintf

fputc, putc fputs

以例子说明fscanf和fprintf的使用

例 已 知 文 本 文 件 f1.txt 中 存 放 有 100个学生的分数要求读入这些数据 并按从高到低的顺序排序后输出到另一 文件?#23567;?p> #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

for(i=0;i<100;i++) fscanf(fp,%d,a+i);

从fp所指 的文件中 读数据

fclose(fp);

sort(a,100); fp=fopen(f2.txt, w); for(i=0;i<100;i++) fprintf(fp,%4d,a[i]); fclose(fp); }

注意文本文件的输入格式要与 文件中的数据格式匹配

关闭fp所 指的文件

二进制文件的读写 即如何将以二进制方式存放的文件输入 到程序的数据结构?#23567;?p> 如何将数据结构中的数据以二进制方式 输出到文件?#23567;?p>读写函数 fread getw

fwrite putw

例将前例中的排序结果改用二进制方式 输出到文件f3.dat?#23567;?p> #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);

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); }

数据的开 始地址 数据的每一 项的长度 数据的 项数 文件的指 针

如果要将二进制文件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); }

13.5 文件的定位 文件中有一个位臵指针指向当前读写 位臵

如果顺序读写一个文件每次读写完 一个字符后该位臵指针自动指向下一个字 符位臵

如果想改变这样的规律强制使位 臵指针指向指定位臵可以用有关函数

rewind函数

rewind函数的作用是使位臵指针重返 回文件的开头

例对文本文件f1.txt中的100个分数 求超过平均分的人数

#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(

%d,n); }

fseek函数和随机读写 使用fseek函数可以将位臵指针指向所需 的位臵

fseek函数调用的一般形式

fseek文件指针位移量参考点;

以起始点为基 准向前移动 的字节数 0 或 SEEK_SET 1 或 SEEK_CUR 2 或 SEEK_END 文件开始 当前位臵 文件末尾

例如果fp是指向一个存放100个整数的二进 制文件要读取第50个数到变量n时

fseek(fp,sizeof(int)*(50-1),SEEK_SET); fread(&n, sizeof(int), 1,fp);

例如果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);

例已知文本文件f5.txt中存放有100个学 生的学号姓名和考试成绩

要求从键盘输入 ?#25105;?#23398;号检索出相应学生的数据

说明

1文件f5.txt中每?#24418;?#19968;个学生的数据按定 长格式存放?#26469;?#20026;学号整数占5格姓名 占10格成绩整数占4格

2按学号?#26377;?#21040;大的顺序连号存放起始学号 为1001

#include stdio.h typedef struct { int num; char name[20]; int score; }STU; main() { int no; STU st; FILE *fp;

fp=fopen ( f5.txt , r ); scanf ( %d , &no ); fseek ( fp, (no-1001)*19, 0 );

1001 LiLi 90 1002 WangPing 100 1003 HuHeng 75

fscanf ( fp, %5d%10s%4d ,&st.num ,st.name, &st.score );

printf (

%5d%10s%4d , st.num ,

st.name, st.score ) ;

fclose ( fp ) ;

}

如果是二进制 文 件呢

上例的检索方法称为定位检索

如果是非定长格式则需要用遍历检索

while ( !feof ( fp ) ) { fscanf ( fp, %d%s%d, &st.num, st.name, &st.score ); if ( st.num==no ) { printf (

%5d%10s%4d, st.num, st.name, st.score ); break; } } 速度慢但不受限制

综合例 已知文本文件f1.txt中存放有武汉市所有 公民的有关性别和年龄的数据请编写程序分 别?#39029;?#20854;中10名男寿星和10名女寿星并将20

名寿星的数据以文本文件的方式存入到文件

f2.txt中先男后女

说明

文件f1.txt中每?#24418;?#19968;个公民的数据共有3项 ?#26469;?#20026;姓名不超过10个字符性别0表示 男1表示女和年龄整数项间以空格分隔

未给出公民个数将文件中的数据读完为止

算法思想 开辟一个存放20名寿星数据的结果表 a结构体数组然后逐个读取公民 数据每读取一个就向a?#23567;?#21028;断插入 一个?#34892;?#20844;民往前?#23614;?#22899;性公民 往后?#23614;?p> 寿星表a

读一个公民 的数据到p

?#34892;圆?#20837;

女性插入 N 读 完 否

Wanghao 0 100 Liming 0 98 wudan 1 99 xiaofang 1 95

Y

#include <stdio.h> typedef struct { char name[10]; int sex; int age; } PEP;

插入函数,将一个公民的数据插入到寿星表

void insert (PEP *a, int n, PEP t ) { int i,j; if ( t.age < a[n-1].age ) return ; for ( i =0; i<n; i++) if (t.age>a[i].age) break; for ( j=n-1; j>i; j--) a[j]=a[j-1]; 移位 a[i]=t; 插入 }

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);

fp=fopen(f2.txt,w); for(j=0;j<20;j++) fprintf(fp,

%15s%2d%5d, a[j].name, a[j].sex,a[j].age ); fclose ( fp ); }

C语言入门自学教程[傲梦]

C语言入门自学教程[傲梦] - C 语言入门自学教程 C 语言是一种通用的面向过程式的计算机程序设计语言1972 年,为了移植与开发 UNIX 操作?#20302;?丹尼斯里奇在...

单片机C语言入门教程

单片机 C 语言入门教程来源:21ic 关键字:单片机 C 语言 作者: 入门教程 学习一种编程语言,最重要的是建立一个练习环境,边学边练才能学好Keil 软件是目前最...

C语言编程:vc++6.0入门教程及习题

C语言编程:vc++6.0入门教程及习题 - vc++6.0 入门教程 步骤是:(先工程后文件编译连接---运行) 1 桌面上的快捷方式如下:双击即可运行 2 在所有程序...

C语言开发入门教程习题答案

C语言开发入门教程习题答案 - 博学?#21462;?#35753; IT 教学更简单,让 IT 学习更有效 C 语言开发入门教程习题答案 第 1 章 初识 C 语言 一填空题 1...

(1小时学会C语言51单片机)C语言入门教程

(1小时学会C语言51单片机)C语言入门教程 - 相信很多爱好电子的朋友,对单片机这个词应该都不会陌生了吧不过有些朋友可能只听说 他叫单片机,他的全称是什么也许并...

零基础入门嵌入式最完整嵌入式C语言基础教程_...

嵌入式入门学员学生应掌握C语言的基本语法,还应掌握程序设计的基本思想并通过本课程的学习,使学生掌握传统的结构化程序设计的一般方法,以C为语言基础,培养学生严谨...

零基础入门C语言系列视频教程_基础_教学视频大全

汇编C语言C++从原理的角度?#20302;?#30340;讲解汇编C语言C++,从汇编的角度详细讲解C语言和C++,直接讲本质原理,简洁而精辟独一无二的经典教程学习编程从汇编开始才是...

论文C语言入门教程0十一1函数参数的传递和值返回

[论文]C 语言入门教程 (十一) 函数参数的传递和值返回 C 语言入门教程 (十一) 函数参数的传递和值返回 C 语言入门教程 (十一) 函数参数的传递和值返回 前面...

零基础入门C++视频教程_基础_教学视频大全

汇编C语言C++从原理的角度?#20302;?#30340;讲解汇编C语言C++,从汇编的角度详细讲解C语言和C++,直接讲本质原理,简洁而精辟独一无二的经典教程学习编程从汇编开始才是...

C语言学习入门视频教程网址

C语言学习入门视频教程网址 - C 语言学习视频教程 九江 新起点 c 语言 习题 1 和 2.mp4 九江 新起点 c 语言 习题 3 九江 新起点 c 语言 第四章 选择结构 ...

51单片机 C语言学习杂记

51单片机 C语言学习杂记 - 51单片机 C语言入门教程(磁动力工作室) 51单片机 C语言学习杂记 学习单片机实在不是件易事,一来要购买高价格的编程器,仿真器,二来要...

  • C语言入门教程相关
  • C语言入门教程基础
  • C语言编程入门教程
  • C语言入门教程概述图文
  • C语言入门教程第2章_算法
  • C语言编程入门教程精简版
  • (1小时学会C语言51单片机)C语言入门教程
  • c语言基础入门
  • C语言入门教程第1章_概述_图文.ppt
  • C语言c语言入门
  • C语言入门及教学详解1
  • 本站网站首页首页教育资格全部考试考试首页首页考试首页职?#24213;?#26684;考试最近更新儿童教育综合综合文库22文库2文库作文总结建筑资料库考研建筑专?#24213;?#26009;考试首页范文大全公务员考试首页英语首页首页教案模拟考考试pclist爱学首页?#21344;?/a>语文古诗赏析教育教育资讯1高考资讯教育头条幼教育儿知识库教育职场育儿留学教育高考公务员考研考试教育资讯1问答教育索引资讯综合学习网站地图学习考试学习方法首页14托福知道备考心经冲刺宝典机经真题名师点睛托福课程雅思GREGMATSAT留学首页首页作文
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助?#34892;?/a> - 频道?#24049;?/a>
    Copyright © 2017 www.uvof.tw All Rights Reserved
    º齫׿
    <ins id="h3npf"><span id="h3npf"></span></ins>
    <cite id="h3npf"><span id="h3npf"></span></cite><progress id="h3npf"></progress>
    <cite id="h3npf"></cite>
    <listing id="h3npf"><dl id="h3npf"><progress id="h3npf"></progress></dl></listing>
    <cite id="h3npf"><video id="h3npf"></video></cite>
    <cite id="h3npf"><video id="h3npf"><menuitem id="h3npf"></menuitem></video></cite>
    <ins id="h3npf"></ins><var id="h3npf"><video id="h3npf"></video></var><cite id="h3npf"></cite><cite id="h3npf"></cite>
    <ins id="h3npf"><span id="h3npf"></span></ins>
    <cite id="h3npf"><span id="h3npf"></span></cite><progress id="h3npf"></progress>
    <cite id="h3npf"></cite>
    <listing id="h3npf"><dl id="h3npf"><progress id="h3npf"></progress></dl></listing>
    <cite id="h3npf"><video id="h3npf"></video></cite>
    <cite id="h3npf"><video id="h3npf"><menuitem id="h3npf"></menuitem></video></cite>
    <ins id="h3npf"></ins><var id="h3npf"><video id="h3npf"></video></var><cite id="h3npf"></cite><cite id="h3npf"></cite>