woodman

1 程序设计和C语言
所谓程序,就是一组计算机能识别和执行的指令。
程序设计是指从确定任务到得到结果、写成文档的全过程。
软件是计算机程序和程序文档的总称。
程序调试:将编制的程序投入实际运行前,用手工或编译程序等方法进行测试,修正语法错误和逻辑错误的过程。
程序测试:指对一个完成了全部或部分功能、模块的计算机程序在正式使用前的检测,以确保该程序能按预定的方式正确地运行。
#include <stdio.h>
stdio 是系统提供的一个文件名,stdio 是 standard input & output 的缩写。
h 意为 头文件。
%d 表示 整型
%c 表示 字符型
scanf 和 printf 都是 C 的标准输入 输出函数。
&在C中表示地址符,
&a是“变量a的地址”,执行scanf函数,从键盘读入整数 送到变了a的地址处,然后把这个整数赋给变量a.


C语言的主要特点:
1) 语言简洁、紧凑,使用方便灵活。
37个关键字、9种控制语句;源程序短,因此输入程序时工作量少。
C是一个很小的内核语言,不直接提供 输入和输出语句、有关文件操作的语句、动态内存管理的语句等。
2) 运算符丰富。 34种运算符。
C把 括号、赋值和强制类型转换 等作为运算符处理。
3)数据类型丰富。
包括:整型、浮点型、字符型、数组类型、指针类型、结构体类型 和 共用体类型 等。
4)具有结构化的控制语句。
用函数作为程序的模块单位,便于实现程序的模块化。
5)语法限制不太严格,程序设计自由度大。
6)C语言允许直接访问物理地址,能进行位操作,实现汇编语言的大部分功能,可直接对硬件进行操作。
7) 程序的可移植性好。
8)生成目标代码质量高,程序的执行效率高。

c语言程序的结构有以下特点:
1)一个程序有一个或多个源程序文件组成。
1.1)预处理指令。
1.2)全局声明。
1.3) 函数定义。
2)函数是C程序的主要组成部分。是C程序的基本单位。
一个C语言程序必须包含一个main函数(有且仅有1个)。
3)一个函数包括两个部分。
3.1)函数首部,即函数的第一行,包括 函数名、函数类型、函数属性、函数参数名、参数类型。
3.2)函数体。即花括号内的部分。 包括:声明部分 和 执行部分。
4) 程序总是从main函数开始执行的。
5) 程序中对计算机的操作是由函数中的C语句完成的。
6)每个数据声明和语句的最后必须有一个分号。
7)C语言本身不提供输入输出语句。
8)程序应当包含注释。

编写好一个C源程序后,怎样上机进行编译和运行呢? 一般经历以下步骤:
1)上机输入和编辑源程序。
2)对源程序进行编译。
先用C编译系统提供的“预处理器”对程序中的预处理指令进行编译预处理。
检测源程序判断有无语法错误。
编译时 自动包括了 预编译 和 正式编译 两阶段,一气呵成,不用分别发出指令。
3)进行连接处理。
4)运行可执行程序,得到运行结果。

程序设计任务几个工作阶段:
1)问题分析。
2)设计算法。
3)编写程序。
4)对源程序进行编辑、编译和连接,得到可执行程序。
5)运行程序,分析结果。
6)编写程序文档(用户文档)。
内容包括:程序名称、程序功能、运行环境、程序的装入和启动、需要输入的数据,以及使用主要事项等。


2 算法——程序的灵魂

程序=算法+数据结构
程序主要包括两个方面的信息:
1)对数据的描述。
2)对操作的描述。
数据是操作的对象,操作的目的是对数据进行加工处理,以得到期望的结果。
广义地说:为解决一个问题而采用的方法和步骤,就称为算法。
算法分为:数值运算算法 和 非数值运算算法

2.4 算法的特性
有穷性
确定性
有零个或多个输入
由一个或多个输出
有效性

2.4.2 表示算法的常用方法
自然语言、传统流程图、结构化流程图和伪代码等。

流程图的优缺点
优点:表示算法直观形象,比较清晰地显示出各个框之间的逻辑关系。
缺点:对流程线的使用没有严格限制,使用者可以使流程线随意的转来转去,
使得算法不易阅读且难以理解算法的逻辑。

2.5.3 三种基本结构和改进的流程图
顺序结构
选择结构
循环结构

2.6 结构化程序设计方法
采取以下方法来保证得到结构化的程序:

自顶向下;
逐步细化;
模块化设计;
结构化编码。

结构化程序设计是进行以 模块功能和处理过程 设计为主的详细设计的基本原则。
对写入的程序使用逻辑结构,使得理解和修改更有效更容易。

主要内容:把一个复杂问题的求解过程分段进行,每个阶段处理的问题都控制在人们容易理解和处理的范围内。

3 最简单的C程序设计——顺序程序设计
3.2 数据的表现形式及其运算
3.2.1
数据的表现形式:常量和变量

1.常用的常量:
(1)整型常量。
(2)实型常量。
1)十进制形式,由数字和小数点组成。
2)指数形式。如0.145E-25(代表0.145 * 10^-25)
注意: e或E之前必须有数字,且e或E后面必须为整数。
(3)字符常量。字面常量是没有名字的不变量.
1)普通字符,用 单撇号 括起来的字符,如 ASCII中 \'a\'代码是97,\'2\'代码是50.
2)转义字符,\'\t\' 跳到下一个tab位置。
(4)字符串常量。用双撇号,如"CHINA","1"。
(5)符号常量。
用#define PI 3.1415926 (无分号),指定用一个符号名称代表一个常量。

2.使用符号常量的好处:
1)含义清楚。
2)在需要改变程序中多处用到的同一个常量时,能做到“一改全改”。

注意:要区分符号常量和变量,不要把符号常量误认为变量。
符号常量不占内存,只是一个临时符号,代表一个值,
在预编译后这个符号就不存在了,故不能对符号常量赋新值。
为与变量名相区别,习惯上符号常量用 大写表示,如PI,PRICE等。

3.常变量与常量的异同:
常变量具有变量的基本属性:有类型,占存储单元,只是不允许改变其值(const int a=3;)。
可以说,常变量是有名字的不变量,而常量是没有名字的不变量。

4.常变量与符号变量有什么不同?
二者在程序中都能使用。
但二者性质不同:指令不同,符号常量代表一个字符串,编译后就不存在了,不分配存储单元。
常变量要占存储单元,有变量值,只是该值不改变而已。 从使用的角度看,常变量具有符号常量的优点,而且使用更方便。有了常变量以后,可以不必多用符号常量。

5.标识符
  C语言规定标识符只能由字母、数字和下画线3种字符组成,且第1个字符必须为字母或下画线。

注意:编译系统认为大写字母和小写字母是两个不同的字符。

3.2.2 数据类型

 


(1)基本类型
1)整型类型:基本整型、短、长、双长、字符型、布尔型。
2)浮点类型:单精度浮点型,双、复数浮点型。
(2)枚举类型(enum) 、空类型(void)
(3)派生类型:指针类型、数组、机构体、共用体、函数类型。

3.2.3
1整型数据

(1)基本整形(int型)
Turbo C 为每一个整型数据分配2个字节(16个二进位),
而VC6为每一个整型数据分配4个字节(32位)。
在存储单元中的存储方式是:用整数的补码(complement)形式存放。

求负数的补码的方法是:先将此数的绝对值写成二进制形式,
然后对其所有二进位按位取反,再加1。(正数的补码是它自已。)

如果给整型变量分配4个字节(Visual C++ ),
其能容纳的数值范围为−2^31~(2^31−1),即−2147483648~2147483647。

(2)短整型(short int)
Visual C++ 编译系统分配给短整型2个字节。范围是−32768~32767。

(3)长整型(long int)
类型名为long int或long。Visual C++对一个long型数据分配4个字节(即32位),
因此long int型变量的值的范围是−2^31~(2^31−1),即−2147483648~2147483647。

(4)双长整型(long long int)
类型名为long long int或long long,一般分配8个字节。
这是C 99新增的类型,但许多C编译系统尚未实现。

说明:
C 标准没有具体规定各种类型数据所占用存储单元的长度,这是由各编译系统自行决定的。
C标准只要求long型数据长度 不短于 int型, short型 不长于 int型。即
sizeof(short)≤sizeof(int)≤sizeof(long)≤sizeof(long long)

2 整型变量的符号属性

可以在类型符号前面加上修饰符unsigned,表示指定该变量是“无符号整数”类型。
如果加上修饰符signed,则是“有符号类型”。
如果既未指定为signed也未指定为unsigned的,默认为“有符号类型”。

有符号整型数据存储单元中最高位代表符号(0为正,1为负)。
说明:只有整型(包括字符型)数据可以加signed or unsigned
%u 表示无符号的十进制数的格式输出。

3.2.4 字符型数据
1 字符与字符代码

  ASCII代码最多用7个二进位就可以表示。所以在C语言中,指定用一个字节(8位)存储一个字符(所有系统都不例外)。此时,字节中的第1位置为0。

在使用有符号字符型变量时,允许存储的值为-128~127,但字符的代码不可能为负值,所以在存储字符时实际上只用到0~127这一部分,其第1位都是0。

3.2.5 浮点型数据:用来表示具有小数点的实数。
实数以指数的形式存放在存储单元。
由于小数点位置可以浮动,所以实数的指数形式称为浮点数。

(1)float型

小数部分占的位(bit)数愈多,数的有效数字愈多,精度也就愈高。
指数部分占的位数愈多,则能表示的数值范围愈大。float型数据能得到6位有效数字,
数值范围为0以及−1.2×10^38~3.4×10^38。

(2)double型

在C语言中进行浮点数的算术运算时,将float型数据都自动转换为double型,然后进行运算。
数值范围为0以及−2.3×10^308~1.7×10^308。

(3)long double型

而Visual C++则对long double型和double型一样处理,分配8个字节。请读者在使用不同的编译系统时注意其差别。

3.2.6 怎样确定常量的类型

从常量的表示形式即可判定其类型。
单撇号 括起来的 单个字符 或 转义字符 就是 字符常量。

1. 整型常量:不带小数点的数值是整型常量,但应该注意其有效范围。

在一个整数的末尾加大写字母L或小写字母l,表示它是长整型(long int)。
但在Visual C++中由于对int和long int型数据都分配4个字节,因此没有必要用long int型。

2. 浮点型常量:凡以小数形式或指数形式出现的实数。
在常量的末尾加专用字符,强制指定常量的类型。
如加字母F或f,就表示是float型常量,分配4个字节。
如果在实型常量后面加大写或小写的L,则指定此常量为long double型。

注意:要区分类型与变量。
有些读者容易弄不清类型和变量的关系,往往把它们混为一谈。
应当看到它们是既有联系又有区别的两个概念。
每一个变量都属于一个确定的类型,类型是变量的一个重要的属性。
变量是占用存储单元的,是具体存在的实体,在其占用的存储单元中可以存放数据。
而类型是变量的共性,是抽象的,不占用存储单元,不能用来存放数据。

3.2.7 运算符和表达式
1 基本的算术运算符
如果除数或被除数中有一个为负值,则舍入的方向是不固定的。
多数C编译系统(如Visual C++ )采取“向零取整”的方法,取整后向零靠拢。

%运算符要求参加运算的 运算对象(即操作数)为整数,结果也是整数。

2 自增(++)、自减(–)运算符
建议谨慎使用++和--运算符,只用最简单的形式,即i++,i--。
而且把它们作为单独的表达式,不要在一个复杂的表达式中使用++或--运算符。
int i=3;
1)j=++i; // 先i=4,再赋值给j,j=4.
2)j=i++; // 先赋值给j,j=3,再i=4.
注意:两个运算符只能用于变量,而不能用于常量或表达式。
错误表述,如5++ 或 (a+b)++ 等。

3 算术表达式和运算符的 优先级与结合性
用算术运算符和括号将运算对象连接起来的、符合C语法规则的式子,称为 C 算术表达式。
运算对象:常量、变量、函数等。

4 不同类型数据间的混合运算
(1)四则运算的两个数中有一个为float 或 double 型,结果为double型。
(2)int型 与 float或 double型 进行运算,统一转为double型后再进行运算。
(3)字符型数据与整型数据进行运算,就把字符的ASCII代码与整型数据进行运算。

5 强制类型转换运算符
一般形式:(类型名)(表达式)

注意,表达式应该用括号括起来.
需要说明的是,在强制类型转换时,得到一个所需类型的中间数据,而原来变量的类型未发生变化。
a = (int)x
如已定义X为float型,a为整型变量,运算后x的值和类型都未变化,临时值复制后就不在存在了。

6 C 运算符 P56


3.3.1 C语句的作用和分类

C语句分为5类

1.控制语句。
  C语言只有9种控制语句:
1)if() ...else..  (条件语句)
2)for()...  (循环语句)
3)while()...  (循环语句)
4)do...while()  (循环语句)
5)continue  (结束本次循环语句)
6)break  (中止执行switch或循环语句)
7)switch  (多分支选择语句)
8)return  (从函数返回语句)
9)goto  (转向语句,在结构化程序中基本不用goto语句)
2 函数调用语句。
printf("This is a c statement.");
3 表达式语句。
a=3 是赋值表达式, a=3;是赋值语句。
4 空语句。
;
5 复合语句
{
float pi=3.14159, r=2.5, area;
area=pi * r * r;
printf("area=%f",area);
}
注意:复合语句中最后一个语句末尾的分号不能忽略不写。

3.3.2 最基本的语句——赋值语句

C 程序中最常用的语句: 赋值语句 和 输入输出语句。

1.赋值运算符:=就是赋值运算符,它的作用是将一个数据赋给一个变量。

2 复合的赋值运算符 : 在 = 之前加上其他运算符可以构成复合的运算符。
x%=y+3 // x=x%(y+3)
注意:凡是二元(二目)运算符,都可以与赋值符一起组合成复合赋值符。
有关算术运算的复合赋值运算符有+=,-=,*=,/=,%=。

3 赋值表达式:具有计算和赋值的双重功能。
一般形式: 变量 赋值预算法 表达式

左值的意思是它可以出现在赋值运算符的左侧,它的值是可以改变的。
(a+b)不能作为左值,常量也不能作为左值。
凡是左值都可以作为右值

4 赋值过程中的类型转换

1)将浮点型数据(包括单、双精度)赋给整型变量时,
先对浮点数取整,即舍弃小数部分,然后赋予整型变量。

2)将整型数据赋给单、双精度变量时,数值不变,但以浮点数形式存储到变量中。

3)将一个double型数据赋给float变量时,先将双精度数转换为单精度,
即 只取6~7位 有效数字,存储到float型变量的4个字节中。
应注意双精度数值的大小不能超出float型变量的数值范围。

4) 字符型数据赋给整型变量是,将字符的ASCII代码赋给整型变量。
i=\'A\'; // int i=65.

5)将一个占字节多的整型数据赋给一个占字节少的整型变量或字符变量时,
只将其 低字节 原封不动 地送到被赋值的变量(即发生“截断”)。

5 赋值表达式和赋值语句

在if的条件中可以包含赋值表达式,但不能包含赋值语句。
1) if ( (a=b) >0) max=a; //先b的值赋给a,然后a > 0 ,成立则 a的值赋给max.
2) if ( (a=b;) >0) max=a; //错误。

注意:要区分赋值表达式和赋值语句。
赋值表达式的末尾没有分号,而赋值语句的末尾必须有分号。
在一个表达式中可以包含一个或多个赋值表达式,但绝不能包含赋值语句。

6变量赋初值
int a,b,c =5; //可以。
不能写成int a= b=c= 3;

3.4 数据的输入输出

3.4.1 输入输出举例
  printf在使用格式声明,输出小数位小于实际小数位时按四舍五入处理。
如果输出多个数据,各占一行,而用同一个格式声明(如%7.2f),
即使输出的数据整数部分值不同,但输出时上下行必然按小数点对齐,使输出数据整齐美观。

3.4.2 有关数据输入输出的概念

讨论输入输出时首先要注意以下几点:
1)所谓输入输出是 以计算机为主体而言的。
2)C语言本身不提供输入输出语句,输入和输出操作有c标准函数库的函数来实现。

没有输人输出语句就可以避免在编译阶段处理与硬件有关的问题,
可以使编译系统简化,而且通用性强,可移植性好,
在各种型号的计算机和不同的编译环境下都能适用,便于在各种计算机上实现。
3)在使用系统库函数时,要在程序文件的开头用 预处理指令 #include 把有关头文件放入本程序。
#include <stdio.h> 或 #include "stdio.h"

这两种#include指令形式的区别是:
用尖括号形式(如<stdio.h>)时,编译系统从存放C编译系统的子目录中去找所要包含的文件(如stdio.h),这称为标准方式。
如果用双撇号形式(如"stdio.h"),在编译时,编译系统先在用户的当前目录(一般是用户存放源程序文件的子目录)中寻找要包含的文件,若找不到,再按标准方式查找。
如果用#include指令是为了使用系统库函数,因而要包含系统提供的相应头文件,这时以用标准方式为宜,以提高效率。如果用户想包含的头文件不是系统提供的相应头文件,而是用户自己编写的文件(这种文件一般都存放在用户当前目录中),这时应当用双撇号形式,否则会找不到所需的文件。

3.4.3 用printf函数输出数据

1 printf函数的一般格式为:
printf(格式控制,输出列表) ,如:printf("%d,%c\n",i,c)
括号内 包括 两部分
(1)"格式控制"是双撇号括起来的一个字符串,称“转换控制字符串”,简称“格式字符串”。
1)格式声明。 总是以 %字符开始。
2)普通字符。 原样输出。
(2)输出列表是程序需要输出的一些数据,可以是常量、变量或表达式。

2 格式字符
常用以下几种格式字符。
(1)d格式符,输出一个有符号的十进制整数。
(2)c格式符用来输出一个字符。
如果整数比较大,则把它的最后一个字节的信息以字符形式输出。
(3)s格式符。用了输出一个字符串。
(4)f格式符用来输出实数(包括单、双精度、长双精度),以小数形式输出。
1)基本型,用%f
不指定输出数据的长度,由系统根据数据的实际情况决定数据所占的列数。
系统处理的方法一般是:实数中的整数部分全部输出,小数部分输出6位。

2)指定数据宽度和小数位数,用%m.nf。
如%7.2符,数据占7列,其中包括2位小数。对其后一位采取四舍五入方法处理。
如果把小数部分指定为0,则不仅不输出小数,而且小数点也不输出。

3)输出的数据向左对齐,用%-m.nf。
在m.n的前面加一个负号,其作用与%m.nf形式作用基本相同,
但当数据长度不超过m时,数据向左靠,右端补空格。

(5)e格式符。用格式声明%e指定以指数形式输出实数。
格式符e也可以写成大写E形式,此时输出的数据中的指数不是以小写字母e表示而以大写字母E表示。

不常用格式符:
  i格式符。作用与d格式符相同,按十进制整型数据的实际长度输出。
  o格式符。以八进制整数形式输出。将内存单元中的各位的值(0或1)按八进制形式输出,因此输出的数值不带符号,即将符号位也一起作为八进制数的一部分输出。
  x格式符。以十六进制数形式输出整数。
  u格式符。用来输出无符号(unsigned)型数据,以十进制整数形式输出。
  g格式符。用来输出浮点数,系统自动选f格式或e格式输出,选择其中长度较短的格式,不输出无意义的0。

综上,格式声明一般形式可以表示为
% 附加字符 格式字符

说明:p75 除了X,E,G外,其他格式字符必须用小写字母,如%d不能写成%D。
如果想输出字符“%”,应该在“格式控制字符串”中用连续两个“%”表示。

3.4.4 用scanf函数输入数据

1. scanf 函数的一般形式
scanf (格式控制,地址表列)

2. scanf函数中的格式声明
以%开始,以一个格式字符结束,中间可以插入附加的字符。P76 格式字符表.

3. 使用scanf函数 应注意的问题

*,本输入项在读入后不赋给相应的变量

(1)scanf函数中的"格式控制"后面应当是变量地址,而不是变量名。
如scanf("%f%f",&a,&b);
(2) 如果在“格式控制字符串”中除了格式声明以外还有其他字符,
则在输入数据时在对应的位置上应输人与这些字符相同的字符。
如:scanf("a=%f,b=%f",&a,&b); //输入格式 a=1,b=2
//(逗号与函数中ab直间的逗号对应,也可用其他符合)
(3)在用“%c”格式声明输人字符时,空格字符和“转义字符”中的字符都作为有效字符输入。
(4)在输人数值数据时,如输人空格、回车、Tab键或遇非法字符(不属于数值的字符),
认为该数据结束。
如scanf("%d%c%f",&a,&b,&c); /* 输入12a12o.12,则a=12,b=a,c=12.000000

3.4.5 字符输入输出函数
1.用putchar函数既可以输出能在显示器屏幕上显示的字符,也可以输出屏幕控制字符。

2. getchar 函数没有参数,只能接收1个字符,按Enter键后,字符才送到计算机中。

说明:执行getchar函数不仅可以从输入设备获得一个可显示的字符,
而且可以获得在屏幕上无法显示的字符,如控制字符。

4 选择结构程序设计
4.1 选择结构和条件判断
C语言有两种选择语句:
(1)if语句。实现两个分支的选择结构。
(2)switch语句。实现多个分支的结构选择。
4.2 用if语句实现选择结构
4.2.2 if语句的一般形式
if语句可以写在一行,也可以写在多行。
可以有N个“内嵌语句”,每个内嵌语句的末尾都应当有分号。
  if语句无论写在几行上,都是一个整体,属于同一个语句。
可以是简单语句,也可以是复合语句。
内嵌语句也可以是一个 if语句。
对给定的条件进行检查,判断给定条件是否成立。
4.3 关系运算符和关系表达式
4.3.1 关系运算符优先级
"==" "!=" 优先级较低。
优先级高到低:算术运算符 > 关系运算符 > 赋值运算符
如:c > a + b // a > (a+b)
a > b==c //(a>b)==c
a=b>c // a= (b>c)

4.4 逻辑运算符和逻辑表达式
4.4.1 逻辑运算符及其优先次序
  “!“为三者中最高的。
优先级高到低:!> 算术运算符 > 关系运算符 > && 、 || > 赋值运算符

说明:关系表达式和逻辑表达式的值是 0 和 1,0假 1真。
定义逻辑性变量: _Bool

4.5 条件运算符和条件表达式
#include <stdbool.h>
条件运算符由(?和 :)组成,比赋值运算符优先级 高。
一般形式:表达式1 ? 表达式2 :表达式3
max=(a>b) ? a :b; //若条件为真,则把a的值赋给max, 反之则将b的值赋给max.

4.6 选择结构的嵌套 p99
  else总是与它上面最近的未配对的if配对。
可以用{}限定内嵌if语句的范围。

4.7 用switch语句实现多分支选择结构

在执行完一个case标号后面的语句后,就从此标号开始执行下去,不再进行判断。

说明:
1)switch后面括号内的"表达式",其值的类型应为整数类型(包括字符型)。
2)花括号内为一个复合语句。可包含多个case和最多一个default.
3)可以没有default,此时未匹配则不执行任何语句。
4)case标号的顺序不影响结果。
5)每个case常量必须互不相同。
6)标号只起标记作用。
7)在case子句中虽然包含了一个以上执行语句,但可以不必用花括号括起来,
会自动顺序执行本case标号后面所有的语句。当然加上花括号也可以。
8)多个case可以共用一组执行语句。

注:函数fabs(a) ,用于判断a的绝对值是否小于一个很小的数(如10^-6),是则认为a=0。

算术运算:用于各类数值运算.
包括加(+)、减(-)、乘(*)、除(/)、求余(或称模运算,%)、自增(++)、自减(–)共七种.算术运算简称运算。

逻辑运算又称布尔运算。布尔用数学方法研究逻辑问题,成功地建立了逻辑演算。
他用等式表示判断,把推理看作等式的变换。这种变换的有效性不依赖人们对符号的解释,只依赖于符号的组合规律
。这一逻辑理论人们常称它为布尔代数。

5 循环结构程序设计
while 语句的一般形式: while (表达式) 语句。
特点:先判断表达式,后执行循环体语句。

do...while 语句的一般形式: do {}; while ();
特点:先执行循环体语句,在检查条件是否成立。若成立,在执行循环体。

for 语句的一般形式:
for(表达式1;表达式2;表达式3) 等价于 表达式1; while 表达式2 { 语句 表达式3}
3个表达式的作用:
表达式1:设置初始条件,只执行一次。
表达式2:是循环条件表达式,用来判定是否继续循环。
表达式3:作为循环的调整,是在执行完循环体后才进行。
即 for (循环变量赋初值;循环条件;循环变量增值)

注意:
1)“表达式1”中所定义的变量的有效范围只限于for循环中,在循环外不能使用此变量。
2)表达式皆可以省略[小心死循环]。
如:for (;i<=100;) {sum++;} , for (;;) { printf("%d\t",i);}
3)表达式1和3 的多个语句用 逗号间隔。

用break语句提前终止循环,用continue语句 结束本次循环。
  break语句只能用于循环语句和switch语句之中,而不能单独使用。

6 利用数组处理批量数据
1)数组是一组有序的数据的集合。
2)用一个数组名和下标来唯一地确定数组中的元素。
3)数组中的每一个元素都属于同一个数据类型。

6.1 怎样定义和引用一维数组
6.1.1 怎样定义一维数组
1)数组名 遵循标识符命名规则
2)定义数组时,需要指定数组中的元素的个数,用【】中的常量表示
3)常量表达式中可以包括常量和符号常量,如“int a[3+5];“是合法的。不能包含变量,如“int a[n];“是不合法的。
也就是说,C语言不允许对数组的大小作动态定义,即数组的大小不依赖于程序运行过程中变量的值。

6.1.3 一维数组的初始化
定义数组的同时,给各数组元素赋值,称为数组的初始化。
1)在定义数组时对全部数组元素赋予初值。
2)可以只给数组中的一部分元素赋值。
3)如果想使一个数组中全部元素值为0,可以写成int a[10]= {0};,
未赋值的部分元素自动设定为0。
4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度。
注意:
如果在定义数值型数组时,指定了数组的长度并对之初始化,凡未被“初始化列表”指定初始化的数组元素,系统会自动把它们初始化为0如果是字符型数组,则初始化为’\0‘,如果是指针型数组,则初始化为NULL,即空指针。

6.2 怎样定义和引用二维数组
二维数组定义的一般形式为:
类型说明符 数组名 【】 【】;
如:float a [3] [4],b[5] [10]; //定义a 为 3x4(3行4列)的数组。
二维数组中元素排列的顺序是按行存放。
多维数组 float a [2][3][4]; //定义a 为 2页、3行、4列。

引用时要注意,数组的 行 和 列 的 序号 均从0起算。

6.2.3 二维数组的初始化
1)分行给二维数组赋初值。
int a[3] [4] = {{1,2,3,4},{},{}};
2)可以将所有数据写在一个花括号内,按数组元素在内存中的排列顺序对各元素赋初值。
int a[3] [4] = {1,2,3,...,11,12};
3)可以对部分元素赋初值。
int a[3] [4] = {{1},{3},{6}};
4)如果对全部元素都赋初值(即提供全部初始数据),
则定义数组时对第1维的长度可以不指定,但第2维的长度不能省。
int a[] [4] = {1,2,3,...,11,12};
在定义时也可以只对部分元素赋初值而省略第1维的长度,但应分行赋初值。

6.3 字符数组
define:用来存放字符数据的数组是字符数组。

6.3.2 字符数组的初始化
初始化列表最易被理解:
char c[10]={\'I\',\'d\',.....,\'m\'};
若定义是不进行初始化,则数组中的各元素的值是不可预测的。
如果花括号中提供的初值个数(即字符个数)大于数组长度,则出现语法错误。
如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,
其余的元素自动定为空字符(即’\0‘)。

6.3.4 字符串结束标识符
 C系统在用字符数组存储字符串常量时会自动加一个’\0‘作为结束符。
在定义字符数组时应估计实际字符串长度,保证数组长度始终大于字符串实际长度。

char c[]={"I am happy");
也可以省略花括号,直接写成

char c[]= "I am happy";
注意字符串的两端是用双撇号而不是单撇号括起来的。字符串常量的最后由系统加上一个’\0‘。

字符数组并不要求它的最后一个字符为’\0‘,甚至可以不包含’\0‘。
为了使处理方法一致,便于测定字符串的实际长度,以及在程序中作相应的处理,
在字符数组中也常常人为地加上一个’\0‘。

如果一个字符数组中包含一个以上’\0‘,则遇第一个’\0‘时输出就结束。
scanf函数中系统会自动在字符串后面加一个’\0‘结束符。数组中未被赋值的元素的值自动置’\0‘。

6.3.5 字符数组的输入输出
两种方法
1)逐个字符输入输出。 %c
2)将整个字符串一次输入或输出。 %s

说明:
输入的字符不包括结束符; 用%s 格式输出字符串时,printf的输出项是字符数组名.p159
scanf 中如果输入是字符数组名,则不用在加地址符 &。

6.3.6 使用字符串处理函数
#include<string.h>
1 puts函数--输出字符串的函数;puts(str); //str 为已定义的字符数组。
2 gets函数--输入字符串的函数: gets(str);
3 strcat函数--字符串连接函数:strcat(str1,str2);
//将两个字符数组中的字符串连接起来,把2接到1后面,结果放在字符数组1中。
//字符数组1必须足够大 , 连接时去掉1后的结束符,在新串最后保留‘\0’.

4 strcpy 和 strncpy 函数 -- 字符串复制函数
strcpy(str1,str2); //将2复制到1中。
strncpy(str1,str2,2); //将2中最前面2个字符 覆盖到str1最前2个字符。

5 strcmp 字符串比较函数:将两个字符串自左向右逐个比较,直到出现不同的字符或者结束符。
if (strcmp(str1,str2)>0) printf("yes");

6 strlen--测字符串长度的函数 : strlen(str1);

7 strlwr -- 转换为 小写的函数 : 将字符串中大写字母换成小写字母。

8 strupr -- 转换为 大写的函数。

7 用函数实现模块化程序设计
7.1 为什么要用函数
函数就是功能,需要时调用即可。
精简代码,实现模块化程序设计。
注意:
所有函数是平行的,不能嵌套定义
函数间可以互相调用,但不能调用main函数。main函数是被操作系统调用的。
C 程序的执行是从main函数开始 到 main 函数 结束。

7.2 定义函数应包括以下几个内容:
1)指定函数的名字;
2)指定函数的类型;即函数的返回值的类型。
3)指定函数的参数 的 名字和类型;
4)指定函数应当完成什么操作,即函数的功能。

函数体:声明部分 和 语句部分
定义函数的一般形式:
类型名 函数名(形式参数表列)
{函数体}

说明:
1)实参可以是常量、变量或表达式,在调用时 实参的值赋给形参。
2)实参和形参的类型应相同或赋值兼容。

函数的返回值:
1)通过return语句获得。
2)函数值的类型。
3)在定义函数时指定的函数类型一般应与return语句中的表达式类型一致。
4)不带返回值,应定义函数为“void类型”。

7.4 对被调用函数的声明和函数原型
首先,被调用的函数必须是已定义的函数。
其次,使用库函数用#include<>

函数的首行(即函数首部)称为函数原型(function prototype)。
实际上,在函数声明中的形参名可以省写,而只写形参的类型。
编译系统只关心和检查参数个数和参数类型,而不检查参数名,
因为在调用函数时只要求保证实参类型与形参类型一致,而不必考虑形参名是什么。

注意:对函数的“定义”和“声明”不是同一回事。函数的定义是指对函数功能的确立,包括指定函数名、函数值类型、形参及其类型以及函数体等,它是一个完整的、独立的函数单位。而函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包含函数体。

写在所有函数前面的外部声明在整个文件范围中有效。

调用一个函数的过程中又出现 直接或间接 地调用该函数本身,称为函数的递归调用。

7.7 数组作为函数参数
数组元素的作用与变量相当,仅可作为函数实参。
因为数组是一个整体,不可能为一个数组元素单独分配存储单元。
注意:用数组元素作为实参,向形参变量传递的是数组元素的值,
而用数组名作为实参时,向形参(数组名或指针变量)传递的是数组 首元素的地址。

7.7.3 多维数组名作函数参数
可以省略第一维的大小说明。但是不能把第2维以及其他高维的大小说明省略。

7.8 局部变量和全局变量
定义变量可能有3种情况:
1)在函数的开头定义;
2)在函数内的复合语句内定义;
3)在函数的外部定义。

形式参数也是局部变量。
全局(外部)变量有效范围为从定义变量的位置开始到本源文件结束。
建议不要在非必要时使用全局变量:
1)全局变量在程序的全部执行过程中都占用存储单元。
2)它使函数的通用性降低了。
3)使用全局变量过多,会降低程序的清晰性。
若局部变量和全局变量 重名,则在局部变量生效的范围内,全局变量失效。

7.9 变量的存储方式和生存期

(全局变量存放)静态存储方式 和 (局部变量存放)动态存储方式
每一个函数和变量都有两个属性:数据类型 和 数据的存储类别。
存储类别:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)

7.9.2 局部变量的存储类别

auto int b,c=3 ; // auto 可以省略。
局部静态变量在函数调用结束后仍然存在,但是其他函数不能引用它。

将局部变量的值放到CPU中的寄存器中,需要时直接从寄存器取出参与运算,不必在到内存中去存取。
这种变量叫做寄存器变量,用关键字register作声明。
由于现在的计算机的速度愈来愈快,性能愈来愈高,优化的编译系统能够识别使用频繁的变量,从而自动地将这些变量放在寄存器中,而不需要程序设计者指定。

7.9.3 全局变量的存储类别
外部变量不在文件的开头定义,起有效范围只限定于定义处到文件结束。在定义点之前的函数不能引用。
如果由于某种考虑,在定义点之前的函数需要引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”,表示把该外部变量的作用域扩展到此位置。有了此声明,就可以从“声明”处起,合法地使用该外部变量。

用extern声明外部变量时,类型名可以写也可以省写。例如,“extern int A,B,C;”也可以写成“extern A,B,C;“。因为它不是定义变量,可以不指定类型,只须写出外部变量名即可。

在编译时遇到extern时,先在本文件中找外部变量的定义,如果找到,就在本文件中扩展作用域;如果找不到,就在连接时从其他文件中找外部变量的定义。如果从其他文件中找到了,就将作用域扩展到本文件;如果再找不到,就按出错处理。

static int A;
int main(){}
这种加上static声明、只能用于本文件的外部变量称为静态外部变量。

对于局部变量来说,声明存储类型的作用是指定变量存储的区城(静态存储区或动态存储区)以及由此产生的生存期的问题,而对于全局变量来说,由于都是在编译时分配内存的,都存放在静态存储区,声明存储类型的作用是变量作用城的扩展问题。

用static声明一个变量的作用是:

对局部变量用static声明,把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在。
对全局变量用static声明,则该变量的作用域只限于本文件模块(即被声明的文件中)。

P212 存储类别小结*

7.10 关于变量的声明和定义

对”int a;“而言,它既是声明,又是定义;
而对“extern a;”而言,它是声明而不是定义。
前者称为定义性声明(defining declaration),或简称定义(definition);
后者称为引用性声明(referencing declaration)。
把建立存储空间的声明称定义,而把不需要建立存储空间的声明称为声明。

7.11 内部函数和外部函数
7.11.1 内部函数
如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static,即:
static 类型名函数名(形参表);
内部函数又称静态函数,因为它是用static声明的。
使用内部函数,可以使函数的作用域只局限于所在文件。
这样,在不同的文件中即使有同名的内部函数,也互不干扰,不必担心所用函数是否会与其他文件模块中的函数同名。

7.11.2 外部函数
如果在定义函数时,在函数首部的最左端加关键字extern,则此函数是外部函数,可供其他文件调用。
extern int fun (int a, int b)
C语言规定,如果在定义函数时省略extern,则默认为外部函数。

8 善于利用指针

地址被形象化地称为"指针",意思是通过它能找到以它为地址的内存单元。
一个变量的地址被称为该变量的“指针”。

8.2 指针变量
一个变量专门用来存放另一个变量的地址(即指针),则它称为“指针变量”。
例如:i_index=&i; //将i的地址存放的i_index中

8.2.2 怎样定义指针变量
一般形式:类型名 * 指针变量名
例如:int * pointer_1, * pointer_2;
//*表示该变量的类型为指针型变量, 指针名是 pointer_1 .

一个变量的指针的含义包括两个方面:
一是以存储单元编号表示的纯地址(如编号为2000的字节),
一是它指向的存储单元的数据类型(如int,char,float等)。

引用指针可能有3种情况:
1)给指针变量赋值:p=&a; //把a的地址赋给指针变量p
2)引用指针变量 指向的变量 :printf("%d",* p);
//以整数的形式输出p所指向的变量的值,即变量a 的值。
3)引用指针变量的值: printf("%o",p);
// 以八进制数形式输出指针变量p 的值,即输出a的地址 (&a).

8.3 通过指针引用数组
8.3.1 数组元素的指针
int a[10]={1,3,5,....,17,19};
int * p;
p=&a[0]; 或 p=a; //把a[0]元素的地址赋给指针变量p.
注意:数组名仅代表数组首元素的地址。
使用指针法能使目标程序质量高(占内存少,运行速度快)。

8.3.2 在引用数组元素时指针的运算
当指针 指向数组元素的时候可以进行以下运算:
加一个整数(用+或+=),如p+1; //表示指向下一个元素 a[1].
减一个整数(用-或-=),如p-1;
自加运算
自减运算
两个地址不能相加,如p1+p2是无实际意义的。

8.3.3 通过指针引用数组元素
引用一组元素
1) 下标法: a[i]
2) 指针法: *(a+i) 或 * (p+i).
//a 为数组名,p是指针变量,其初值 p=a.

指向数组元素的指针变量也可以带下标。
引用数组中各元素的值有3种方法:p233
1)下标法:
2)通过数组名计算数组元素地址,找出元素的值。
3)用指针变量指向数组元素。

使用指针变量指向数组元素时,有以下几个注意:
1)可以通过改变指针变量的值 指向不同的元素。
2) 要注意指针变量的当前值。

当p=a[0]时,P[i]代表 a[i]。
由于++和*同优先级,结合方向为自右而左,因此它等价于* (p++)。
先引用p的值,实现* p的运算,然后再使p自增1。

8.3.5通过指针引用多维数组
1 多维数组元素的地址
p247 二维数组的有关指针
二维数组名 a 是指向的行, 而 *a 指向的是列的指针,& a[0]或 & * a 指向行的指针。
即 &a[i] 或 a+i 指向行的指针;在二维中不是指向具体的存储单元而指向行。
a[i] 或 *(a+i) 指向列的指针。

2 指向多维数组元素的指针变量 p249
1)指向数组元素的指针变量。
2)指向由m个元素组成的一维数组的指针变量。
3)用指向数组的指针作为函数参数

要注意指针变量的类型:”int(*p)[4]“表示定义p为一个指针变量,它指向包含4个整型元素的一维数组。 p252
注意,*p两侧的括号不可缺少。

8.4 通过指针引用字符串
字符串两种引用方式:
1)通过数组名和下标 引用字符串中的一个字符,也可以用数组名和 %s 输出该字符串。
2)用字符指针变量 指向一个字符串常量,通过字符指针变量引用字符串常量。
printf("%s\n",string); // 先输出第1个字符,系统自动使string加1,使之指向下一个字符,在输出;。。。

8.4.3 使用字符指针变量和字符数组的比较
二者之间有区别:
1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址。
2)赋值方式。可以对字符指针变量赋值,但是不能对数组名赋值。
3)初始化的含义。
数组可以在定义时对各元素赋初值,但不能用赋值语句对字符数组中全部元素整体赋值。
4)存储单元的内容。编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,值分配一个存储单元。
5)指针变量的值可以改变的,而数组名代表一个固定的值,不能改变。
6)字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对它们再赋值)。
7)引用数组元素。指针变量没有指向数组时,p[5] 或 *(p+5) 是输出指针变量p所指的字符后面5个字节的内容。
8)用指针变量指向一个格式字符串,可以用它替代printf函数中的格式字符串。
char * format; format="a=%d,b=%f\n"; printf(format,a,b); //可变格式输出函数。
相当于 printf("a=%d,b=%f\n",a,b);

8.5 指向函数的指针
函数在编译时,编译系统为函数代码分配了一段存储空间,
这段存储空间的 起始地址 称 为这个函数的指针

8.5.3 怎样定义和使用指向函数的指针变量
定义指向函数的指针变量的一般形式为

类型名 (*指针变量名)(函数参数表列); // int (*p)(int,int);
由于优先级的关系,“*指针变量名”要用圆括号括起来。
说明:
1)定义指向函数的指针变量,并不意味着这个指针变量可以指向任何函数,它只能指向在定义时指定的类型的函数。
2)用指针调用函数,必须先使指针变量指向函数。 // p = max; 即把max的入口地址赋给指针变量p 。
3)在给函数指针变量赋值时,只需给出函数名而不必给出参数。
4)用函数指针变量调用函数时,只需用(*p)代替函数名即可。// c=(*p)(a,b);
5)对指向函数的指针变量不能进行算术运算。
6)指针变量调用函数比较灵活,可以根据不同情况调用不同的函数。

8.6 返回指针值的函数 p275
一般形式:类型名 * 函数名 (参数列表);

8.7 指针数组和多重指针
一个数组的元素均为指针类型数据,称为指针数组。
定义一维指针数组的一般形式为:
类型名 *数组名【数组长度】;
指向指针的指针: char **p; // char * (*p);

8.7.3 指针数组作main函数的形参
其实,main函数中的形参不一定命名为argc和argv,可以是任意的名字,只是人们习惯用argc和argv而已。

8.8 动态内存分配与指向它的指针变量
通过库函数<stdlib.h>实现,主要有:malloc, calloc,free,realloc;这4个函数。
void * malloc(unsigned int size) ;为指针型函数,函数值为其第一个字节的地址。
void * calloc(unsigned n,unsigned size); 可以为一维数组开辟动态存储空间,n为元素个数,每个元素长度为size.
void free(void *p); 释放指针变量p所指向的动态空间。 // free(p);释放指针变量p所指向的动态空间。
void * realloc(void *p,unsigned int size); //将指针p所指向的动态空间的大小改为size, p值不变。失败返回NULL。

8.8.3 void指针类型p287
不要把“指向void类型”理解为能指向”任何的类型”的数据,
而应理解为“指向空类型”或“不指向确定的类型”的数据。
p3=(void *)p1 //将p1转化为void * 类型,然后赋值给p3.

8.9 有关指针的小结
1)指针就是地址。注意区别 指针 和 指针变量。
指针是地址本身; 指针变量是用来存放地址的变量。

2)什么叫指向?地址就意味着指向,通过地址能找到具有该地址的对象。
3)对数组的操作中正确地使用指针,搞清楚指针的指向。
4)有关指针变量的归纳比较p289. 表8.4 ***
5)指针的运算 p290.
6)指针变量可以有空值。//p=NULL.

虽然在Visual C++中也为指针变量分配4个字节,但不同于整型数据的存储形式。

一个地址型的数据实际上包含3个信息:

表示内存编号的纯地址。
它本身的类型,即指针类型。
以它为标识的存储单元中存放的是什么类型的数据,即基类型。
有关指针变量的归纳比较:

 

在stdio.h头文件中对NULL进行了定义:
# define NULL 0
系统保证使该单元不作它用(不存放有效数据)。
任何指针变量或地址 都可以 与 NULL 作相等或不相等的比较。

指针的优点:
1)提高程序效率;
2)在调用函数时,指针指向的变量的值改变时,可以从函数调用得到多个可改变的值。
3)可以实现动态存储分配。

9 用户自己建立数据类型
9.1 定义和使用结构体变量
用户自己建立由不同类型数据组合的组合型的数据结构,称为 结构体。

9.1.1 自己建立结构体类型
声明一个结构体类型的一般形式为:
struct 结构体名
{成员表列};
另一种形式:
struct 结构体名
{成员表列} 变量名列表;

9.1.2 定义结构体类型变量
计算机对内存的管理是以“字”为单位的(许多计算机系统以4个字节为一个“字”)。
如果在一个“字”中只存放了一个字符,虽然只占一个字节,但该“字”中的其他3个字节不会接着存放下一个数据,而会从下一个“字”开始存放其他数据。
因此在用sizeof运算符测量student1的长度时,得到的不是理论值63,而是64,是4的倍数。
不同的编译系统对结构体变量在内存中分配空间有不同的规定。

  C 99标准允许对某一成员初始化,如:
struct Student b={.name="Zhang Fang"};

其他未被指定初始化的数值型成员被系统初始化为0,
字符型成员被系统初始化为’\0‘,指针型成员被系统初始化为NULL。

引用方式: 结构体变量名.成员名
例如 student1.num=10010;

  “.”是成员运算符,它在所有的运算符中优先级最高。

如果成员本身又属一个结构体类型,则要用若干个成员运算符,
一级一级地找到最低的一级的成员。
例如: student1.birthday.month //student1中的birthday中的month

同类的结构体变量可以互相赋值。
可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
例如:scanf("%d",&student1.num);
printf("%o",&student1);

9.2 使用结构体数组
一般形式
1)struct 结构体名
{成员列表} 数组名[数组长度];
2)先声明 一个结构体类型 struct Person,然后在用此类型定义结构体数组:
结构体类型 数组名[数组长度];
如:struct Person leader[3]; //leader 是数组名
对结构体数组初始化的形式是在定义数组的后面加上: ={初值列表};

9.3 结构体指针
所谓结构体指针就是指向结构体变量的指针,
一个结构体变量的起始地址就是这个结构体变量的指针。

struct Person *pt;

9.3.3 用结构体变量和结构体变量的指针作函数参数
将结构体变量的值传递给另一个函数
1)用结构体变量的成员做参数。
2)用结构体变量作实参。
这种传递方式在空间和时间上开销较大。
3)用指向结构体变量的指针作实参,将结构体变量的地址传给形参。

9.4 用指针处理链表
链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构。
链表可以根据需要开辟内存单元。

链表有一个 “头指针”变量,它存放一个地址,该地址指向一个元素。
链表中的每一个元素称为 结点,每个结点包括两个部分:
1)用户需要用的实际数据;
2)下一个结点的地址。

9.5 共用体类型

9.5.1 什么是共用体类型
几个不同的变量共享同一段内存的结构,称为 共用体 类型的结构。
union 共用体名 {成员表列} 变量表列;
例如 union Data { int i; char ch; float f;}a,b,c;

结构体变量所占的内存长度是各成员占的内存长度之和,每个成员分别占有自己的内存单元。
而共用体变量所占的内存长度等于最长的成员的长度。

9.5.2 引用共用体变量的方式
先定义,后引用。
不能引用共用体变量,而只能引用共用体变量中的成员。
如: 如a.i a.ch a.f

9.5.3 共用体类型数据的特点 p319
1)同一个内存段可以用来存放几种不同类型的成员,
但在每一瞬时只能存放其中一个成员,而不是同时存放几个。

2)可以对共用体变量初始化。但初始化表中只能有一个常量,
例子:
union Data a={16};
union Data a={.ch=\'j\'};

3)共用体变量中起作用的成员是最后一次被赋值的成员,
在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就取代。
4)公用体变量的地址和它的各成员的地址都是同一地址。
5)不能对共用体变量名赋值,也不能企图引用变量名来得到一个值。

  C 99允许同类型的共用体变量互相赋值:b=a //a,b 是同类型的共用体变量。

6)以前的C规定不能把共用体变量作为函数参数,但可以便用指向共用体变量的指针作函数参数。
C 99允许用共用体变量作为函数参数。
7)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之亦然。

9.6 使用枚举类型
枚举就是把可能的值一一列举出来,变量的值只限于列举出来的值的范围内。
enum Weekday{sun,mon,tue,wed,thu,fri,sat};
枚举变量 枚举 常量/元素
说明
1)C编译对枚举类型的枚举元素按常量处理,故称枚举常量。
不要因为它们是标识符(有名字)而把它们看作变量,不能对它们赋值。
2)每一个枚举元素都代表一个整数
也可以人为地指定枚举元素的数值,在定义枚举类型时显式地指定。
enum Weekday{sun=7,mon=1,tue,wed,thu,fri,sat}workday,week_end;

3) 枚举元素的比较规则是按其在初始化时指定的整数来进行比较的。
//默认第一个枚举元素的值为0,即 mon>sun, sat>fri;

9.7 用typedef声明新类型名

typedef int Integer; //指定用Integer 为类型名,作用于int相同。
1)命名一个新的类型名代表机构体类型:
typedef struct (int month; int day; int year;)Date; //用Date 等同于struct.
2)命名一个新的类型名代表数组类型
typedef int Num[ 100]; //声明Num为整型数组类型名
Num a; //定义a为整型数组名,它有100个元素

  #define是在预编译时处理的,它只能作简单的字符串替换,
而typedef是在编译阶段处理的。

10 对文件的输入输出
10.1 C文件的有关基本知识
文件主要用到两种:

程序文件:源程序文件(*.c),目标文件(*.obj),可执行文件(*.exe)。
数据文件。

文件是程序设计中一个重要的概念。
所谓文件,一般指存储在外部介质上的数据的集合。

文件标识(文件名)包括3部分:
1)文件路径
2)文件名主干
3)文件后缀

数据文件:ASCII文件 和 二进制文件(映像文件)。
文件类型指针,简称文件指针。

指向文件的指针变量并不是指向外部介质上的数据文件的开头,
而是指向内存中的文件信息区的开头。

10.2 打开与关闭文件
所谓“打开”是指为文件建立相应的信息区(用来存放有关文件的信息)
和文件缓冲区(用来暂时存放输入输出的数据)。

10.2.1 用fopen函数打开数据文件 , 表10.1,p335
调用方式:fopen (文件名,使用文件方式);

FILE * fp; //定义一个指向文件的指针变量

fp=fopen(" a1","r");  //将fopen函数的返回值赋给指针变了fp

如果不能实现“打开”的任务此时fopen函数将带回一个空指针值NULL。
其实,带b和不带b只有一个区别,即对换行的处理。

输出的数据形式是由程序中采用什么读写语句决定的。例如,用fscanf和fprintf函数是按ASCII方式进行输入输出,而fread和fwrite函数是按二进制进行输入输出。

程序中可以使用3个标准的流文件——标准输入流、标准输出流和标准出错输出
流。系统定义了3个文件指针变量stdin、stdout和stderr。

10.2.2 用fclose函数关闭数据文件

如果不关闭文件就结束程序运行将会丢失数据。
  fclose函数也带回一个值,当成功地执行了关闭操作,则返回值为0;否则返回EOF(-1)。

10.3 顺序读写数据文件
10.3.1 怎样向文件读写字符
在文件的所有有效字符后有一个文件尾标志。当读完全部字符后,文件读写位置标记就指向最后一个字符的后面,即指向了文件尾标志。如果再执行读取操作,则会读出-1(不要理解为最后有一个结束字节,在其中存放了数值-1。它只是一种处理方法)。文件尾标志用标识符EOF(end of file)表示,EOF在stdio.h头文件中被定义为-1。

10.3.2 怎样向文件读写一个字符串
1)fgets 函数的原型为
char * fgets(char* str, int n, FILE* fp);

其中,n是要求得到的字符个数,但实际上只从fp所指向的文件中读入n-1个字符,然后在最后加一个“\0”字符,这样得到的字符串共有n个字符,把它们放到字符数组str中。如果在读完n-1个字符之前遇到换行符“\n”或文件结束符EOF,读入即结束,但将所遇到的换行符“\n”也作为一个字符读入。
2)fputs 函数的原型为
int fputs (char * str, FILE * fp);

10.3.3 用格式化的方式读写文本文件
一般调用方式
fprintf (文件指针,格式字符串,输出表列);
fscanf (文件指针,格式字符串,输入表列);

输出时将ASCII-->二进制形式,输入将二进制形式-->字符,耗时。
在内存与磁盘频繁交换数据的情况下,最好不用fprintf和fscanf函数,
而用下面介绍的fread和fwrite函数进行二进制的读写。


10.3.4 用二进制方式向文件读写一组数据
一般形式:
fread(buffer,size,count,fp);
fwrite(地址,读写的字节数,每个数据项的长度,FILE类型指针);

文本读写函数:用来向文本文件读写字符数据的函数(如fgetc, fgets, fputc,fputs,fscanf,fprintf等)。
二进制读写函数:用来向二进制文件读写二进制数据的函数(如getw, putw,fread,fwrite 等)。

10.4 随机读写数据文件
用rewind函数使文件位置标记指向文件开头。
作用:使文件位置标记重新返回文件的开头,此函数没有返回值。
用fseek函数改变文件位置标记。
调用形式:fseek (文件类型指针,位移量,起始点)

起始点 名字 用数字代表
文件开始位置 SEEK_SET 0
文件当前位置 SEEK_CUR 1
文件末尾位置 SEEK_END 2

用ftell函数测定文件位置标记的当前位置

10.5 文件读写的出错检测
ferror(fp):返回0表示未出错,非0表示出错。
应该注意,对同一个文件每一次调用输人输出函数,都会产生一个新的ferror函数值,
因此,应当在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失。
在执行fopen函数时,ferror函数的初始值自动置为0。

  clearerr的作用是使文件出错标志和文件结束标志置为0。

只要出现文件读写错误标志,它就一直保留,直到对同一文件调用任何其他输入输出函数。

分类:

技术点:

相关文章:

猜你喜欢
  • 2022-12-23
  • 2021-11-17
  • 2021-06-19
  • 2021-12-03
  • 2021-11-01
  • 2021-04-03
  • 2021-06-02
相关资源
相似解决方案