C 程复习重点知识回顾
第一章 引言
程序与数据共同存储的思想就是目前绝大多数计算机采用的冯·诺伊曼模型的存储程序概念。
3 种基本控制结构:顺序控制结构,分支控制结构,循环控制结构。这 3 种控制方式称为语句级控制。
C 语言的主要“单词”:
-
标识符。C 语言的标识符由字母、数字和下划线组成,且第一个字符必须是字母或者下划线。标识符中英文字母的大小写形式是有区别的。最主要的标识符是保留字和用户自定义标识符。
-
常量。常量是数据类型的(整数常量,浮点数常量,字符常量,字符串常量……)。
-
运算符。运算符表示对各种数据类型数据对象的运算。运算一般多为双目运算符,也有单目运算符和三目运算符。
-
分隔符。
;、[]、()、#等都是分隔符。
C 语言的主要语法单位:
- 表达式。运算符与运算对象(可以是常量、函数、变量等)的有意义组合就形成了表达式。
- 变量定义。
类型名 变量名。 - 语句。语句是程序最基本的执行单位。语句有多种形式:表达式语句,分支语句,循环语句,符合语句。
- 函数定义与调动。
- 输入与输出。
C 语言特点:
- C 语言是一种结构化语言。
- C 语言语句简洁紧凑,使用方便灵活。
- C 语言程序易于移植。
- C 语言有强大的处理能力,
- 生成的目标代码质量高,运行效率高。
C 语言的不足:数据类型检查不严格,表达式出现二义性,不能自动检查数据越界,初学者较难掌握运算符的优先级与结合性的概念等。
可以用多种方式来描述算法,包括用自然语言、伪代码或流程图。
第二章 用 C 语言编写程序
注释:一行内,两个斜杠 \\ 之后的内容是注释。一对 /* 和 */ 之间的内容是注释。编译时,将忽视注释里的内容。
函数 printf 的简单用法是 printf(字符串),如果字符串中要输出 "",需要使用反斜杠 \ 对其进行转义。
常量有整数常量,双精度浮点数常量,单精度浮点数常量,字符常量,字符串常量等。数据类型有整数类型 int,双精度浮点数类型 double,单精度浮点数类型 float,字符类型 char。
变量的定义方式是:类型名 变量名称列表;,带初值的定义方式是:类型名 变量名称=初值;。
算数运算符有加 +,减 -,乘 *,除 /,求余 %,相反数 -。运算优先级是相反数最高,乘、除、求余同级,加和减最低。算数运算的操作数类型必须相同,且运算结果的类型也相同。如果类型不同,则进行自动转换。int/int 时会舍弃余数,结果依然是 int。
赋值运算:把一个表达式的值赋给一个变量,即 变量名=表达式,执行过程是计算右边表达式的值,把结果赋值给左边的变量,如果表达式结果类型与变量类型不同,则会对结果的类型进行类型转换后再赋值。
格式化输出函数 printf()
用法是 printf(格式控制字符串,输出列表)。格式控制字符串中包含两种字符,普通字符直接输出,格式控制字符则按格式输出列表中对应的值。输出列表是要被输出的元素列表,元素之间以逗号隔开。
格式控制字符串中格式控制字符的数量应等于输出列表中元素的数量。若控制字符数量少,则输出列表后面多余的元素不会被输出。若控制字符数量多,则多余的格式字符对应的输出结果不可预计。
格式控制字符与其对应的数据类型为:整数类型 %d,字符类型 %c,字符串类型 %s,浮点数类型 %f。
为了控制整数和浮点数的位数,可以使用 %md 和 %m.nf,第一个表示控制输出宽度是 m 个字符,第二个表示控制浮点数的输出宽度是 m,小数部分输出 n 位。当实际字符宽度不足时,左边填充空格。实际字符宽度超过时,按照实际字符宽度输出。另外也可以为控制整数的位数,也可使用 %0md 和 %-md,前者是用 0 替换原本要输出的空格,后者则是在右边填充空格。
\n 是换行字符,会为后面的输出新建一行。
格式化输入函数 scanf()
用法是 scanf(格式控制字符串,输入地址列表),格式控制字符串由格式控制字符和普通字符组成,整数 %d,单精度浮点数 %f,双精度浮点数 %lf,字符 %c。地址列表元素之间用逗号隔开。
格式控制字符串中的普通字符需要按照原样输入。
关系表达式
两个操作数和关系运算符组成的表达式,比较两个数之间的大小关系。关系运算符有小于 <,小于等于 <=,大于 >,大于等于 >=,等于 ==,不等于 !=。关系运算的操作数是数值类型,运算结果是逻辑值类型,结果为真是 1,为假是 0。
关系运算符中优先级高的是小于,小于等于,大于和大于等于,结合是满足从左到右结合。
if-else 语句
if-else 是一种分支语句,一般形式为:
if (表达式) 语句1;
else 语句2;
也可以嵌套,即在语句 1 或语句 2 的地方再加上新的 if-else 语句。
for 语句
形式为:
for (表达式 1;表达式 2;表达式 3)
循环体语句
表达式 1 最先执行且仅执行一次,表达式 2 是循环条件表达式,值为假时 for 语句结束。循环体语句和表达式 3 在每次循环条件为真时先后执行。一般而言,表达式 1 初始化循环变量,表达式 2 判断循环变量是否超过执行范围,表达式 3 将循环变量更新到下一步。
第三章 分支结构
if-else 语句
if (表达式)
语句 1
else
语句 2
没有 else 也可以。
多路分支
if (表达式1)
语句 1
else if (表达式2)
语句 2
else ……
else if (表达式 n-1)
语句 n-1
else 语句 n
else 会和前面最近的未结合的 if 结合。
字符类型
字符类型 char,包括小写字母、大写字母、数字、括号等。每一个字符都有一个唯一的整数值,称为该字符的 ASCLL 码,代表该字符在 ASCLL 码表中的位置。计算机用一个 byte 存储一个 ASCLL 码,最高位固定为 0,用以区分 ASCLL 码和其他的非标准字符码。用 ASCLL 码最多可以表示 127 个不同的字符,ASCLL 码值为 0 的字符表示一个特殊字符(字符串结束标志)。对字符的四则运算对应对其 ASCLL 码的四则运算。
使用 scanf 和 printf 进行输入输出时,控制字符是 %c。也可以使用 getchar() 读入一个字符,用 putchar() 输出一个字符。
逻辑值和逻辑运算
逻辑值只有真和假,关系成立时是真,不成立时是假,C 语言中逻辑值用整数表示,1 表示真,0 表示假。
逻辑运算符有逻辑非 !,逻辑与 &&,逻辑或 ||,优先级为逻辑非高于逻辑与高于逻辑或。关系运算优先于逻辑运算。
switch 语句
switch (表达式)
{
case 常量 1: 语句段 1;
case 常量 2: 语句段 2;
case 常量 3: 语句段 3;
……
case 常量 N: 语句段 N;
default: 语句段 N+1;
}
每个语句段可以包含 0 到多条语句。
表达式和常量必须是整数(包括 char,int 等可自动转换成整数的类型),case 中的常量也可以是常量表达式。
执行流程:首先计算表达式的值,并将其与 case 的常量进行比较,如果表达式的值等于常量 k,则执行从语句段 k 开始的所有语句段,如果不等于任何 case 的常量值,那么仅执行语句段 N+1。
可以使用 break 语句跳出 switch 语句。
第四章 循环结构
while 语句
while (表达式)
循环体语句
多用于无法准确判断循环次数的循环。
while 语句可以和 for 语句相互转换,比如:
for (表达式1;表达式2;表达式3)
可以变成
表达式1;
while (表达式2)
{
循环体语句
表达式3;
}
而
while (表达式)
循环体语句
可以变成
for (;表达式;)
循环体语句
do-while 语句
do
{
循环体语句
}while(表达式);
会先执行循环体语句,再判断表达式是否成立。循环体语句至少被执行 1 次。
break 语句
break 语句是一种跳转语句,在 switch 中 break 可以跳出 switch,在循环中可以提前结束本次循环且不再进入下一次循环。
随机数相关函数
如何产生随机数:
- time() 获取当前时间作为随机种子。
- srand() 设置随机数种子。
- rand() 产生一个随机数。
srand(time(0));
num=rand();
continue 语句
在循环体中使用 continue 语句后会提前结束本次循环,但不结束循环语句。
循环嵌套
循环可以嵌套,而且可以不同种循环语句嵌套。也可以使用函数来代替循环嵌套。
第五章 函数
函数是一个完成特定工作的独立程序模块,包括库函数和自定义函数两种。自定义函数一般形式为:
函数类型 函数名(形式参数表)
{
函数实现语句
}
第一行称为函数首部或函数头,末尾不能加分号,否则变成了 函数申明语句。
函数类型是函数返回值的类型,可以是 int,float,double,char 等类型,也可以是结构、指针等。如果函数没有返回值,那么类型名应写作 void。如果没有类型名则默认为 int。
函数名是函数的名称,命名要满足标识符的要求。
形式参数表:类型1 参数1,类型2 参数2,类型3 参数3……类型n 参数n。参数个数可以是 0 个。普通函数的参数类型是固定的,也支持参数可变的函数形式。
函数返回值是利用 return 语句,如 return 表达式;。如果没有返回值就直接 return;。
函数申明:函数类型 函数名(形式参数表);,注意这里有 分号,且形式参数表可以 省略参数名,只保留参数类型。函数定义和函数申明的名称、函数类型、形式参数表的类型和个数都必须相同。
函数调用类似于数学上的函数调用,可以以赋值形式调用、直接调用、以实参形式调用、以表达式形式调用。
一个函数被调用执行时,计算机会为每一个形式参数创建一个同名同类型的内部变量,并将实际参数的值赋值给形式参数的同名变量,这种传递方式叫值传递。形参是变量,用于接收实参的值。实参可以是变量、常量或表达式。如果实参是变量,那么它与对应的形参是 两个不同的变量。实参和形参可以同名也可以不同名,如果形参的值在函数内部修改,不会影响实参的值。
局部变量和全局变量
在函数内部定义的变量,使用范围是在所在的函数内。在复合语句内部定义的变量,使用范围是所在的复合函数内。局部变量都要定义在所处范围的开始。
全局变量是定义在函数外,不属于任何函数的变量。
变量
变量用于保存数据,对应着计算机内的若干存储单元。对于 C 语言中定义的变量,计算机会按照变量的类型 适时地 创建该变量,销毁该变量。变量从创建到销毁的整个过程称为变量的 生命周期。
全局变量在程序运行时创建,在程序结束后销毁,全局变量的生命周期是程序执行期间;函数局部变量在函数被调用时创建(包括形式参数),函数返回时销毁,生命周期是函数执行期间;复合语句的局部变量在进入复合语句时创建,退出语句时销毁。变量的作用范围和生命周期是两个不同维度上的概念,作用范围对应 空间,生命周期对应 时间。
静态变量 static 类型名 变量名。静态的局部变量可以改变局部变量的生命周期,将其改为程序执行期间。静态的全局变量可以改变变量的作用范围,改为从文件开始到文件结束。静态全局变量不能被申明为外部变量,不能被其他文件的代码使用。在多文件程序编码时,静态变量可以将一些全局变量设定为 该文件私有。
系统存储区存储操作系统、语言系统,用户存储区分为程序区(代码)和数据区,程序区包括主函数及其他自定义函数等。数据区分为静态存储区和动态存储区,静态存储区存放全局变量和静态局部变量,动态存储区存储主函数变量和其他函数变量。
定义变量时可以同时给出初值,如果没有初值,那么全局变量和静态局部变量初值为 0,程序启动时被创建,所有 bit 值清 0。而局部变量的初值是不确定的。
变量的作用范围:全局变量是整个程序,静态全局变量是从定义开始到文件结束,函数局部变量是所在函数,复合语句局部变量是所在的复合语句。
C 语言允许不同作用范围的变量重名。当有重名变量时,内部变量屏蔽外部变量,小范围变量屏蔽大范围变量。全局变量 不可以重名。
外部变量即外部的全局变量,是定义在其他文件中的全局变量。使用外部变量时要使用关键字 extern 来申明外部变量。注意:申明并不是定义,所申明变量必须在 对应文件 有定义。
第六章 回顾数据类型和表达式
C 语言的数据类型有:
- 基本数据类型:整形,字符型,(单/双精度)实型(浮点型)。
- 构造数据类型:数组,结构,联合(
union),枚举(enum)。 - 指针类型。
- 空类型(
void)。
整数的表示和存储
计算机用 0 和 1 表示所有数据。
原码:最高位表示符号(0 为整数,1 为负数),其余的表示数值。
反码:正数的反码同原码,负数的反码为对原码除符号位外全部取反。
补码:正数的补码同原码,负数的补码是反码加 1,符号位保持不变。
计算机用补码表示整数。若一整数的 bit 数为 N,那么最大的正整数是 \(2^{N-1}-1=(01111111\dots1111)_2\),最小的负整数是 \(-2^{N-1}=(10000000\dots0000)_2\),被表示的整数总数为 \(2^{N-1}-1+1+2^{N-1}=2^N\),没有浪费任何的 bit。可以用 sizeof 运算符来获取数据类型的字节数。
无符号整数是没有符号位的整数,所有的字节都用来存储正整数的原码,N 个 bit 的无符号整数的取值范围是 \([0,2^N-1]\)。定义时往往有 unsigned。
C 语言语法不支持用 2 进制表示常量,10 进制常量直接表示,但不能以 0 开头,8 进制常量以 0 开头,16 进制常量以 0x 或者 0X 开头。进制的数值前面可以加上正负号来表示数值的正负。
可以采用后缀的方式表示扩展的整数类型。无符号:U 和 u,长整型 L 和 l,无符号长整形 UL 或 LU,ul 或 lu。
整数溢出时保留低位数据。整数运算是在补码上进行的,运算结果溢出则舍去高位。对整数类型赋值时,计算机执行 内存 copy,即将右边值或变量的二进制码直接赋给左侧变量。
实数类型的表示
| 类型 | 符号 S | 阶码 E | 尾数 M |
|---|---|---|---|
| float | 1 | 8 | 23 |
| double | 1 | 11 | 52 |
IEEE 浮点数表示:\(S\times2^E\times 1.M\) 或者 \(S\times 2^E\times 0.M\)。
符号占 1 个 bit,0 是正数,1 是负数。阶码是浮点数的指数,采用 移码表示法。对 float 来说,阶码等于指数(二进制下)加 127,对 double 则是指数加 1023。尾数是二进制的小数,当阶码不全为 0 时,尾数 =1.M,阶码全为 0 时尾数 =0.M。
以 \(0.15625\) 为例,\((0.15625)_{10}=(0.00101)_2,0.00101=1.01\times 2^{-3}\),因此符号是 0,阶码是 \(127-3=124=(01111100)_2\),尾数是 \(01000000000000000000000\),因此 \(0.15625\) 最终表示为 \(00111110001000000000000000000000\)。
特殊的浮点数:
| 指数段 | 尾数段 | 数值 | 注解 |
|---|---|---|---|
| 有 0 有 1 | - | \(1.M\times 2^E\) | 一般情况 |
| 全 0 | 全 0 | \(0\) | 符号位除外,所有 bit 为 0 |
| 全 0 | 不全为 0 | \(0.M\times 2^{E_{min}}\) | 非常接近 0 的小数 |
| 全 1 | 全 0 | \(\infty\) | 无穷大 |
| 全 1 | 不全为 0 | \(\text{NaN}\) | 非数值(Not any Number) |
float 长度为 32 位,取值范围约为 \(\pm[10^{-38},10^{38}]\),只具有 \(7\sim 8\) 个有效数字。double 长度为 64 位,取值范围约为 \(\pm[10^{-308},10^{308}]\),具有 \(15\sim 16\) 个有效数字。
数值精度 和 取值范围 是两个不同的概念,如 float x=1234567.89 虽然在取值范围内,但是无法精确表达。float y=1.2e55 的精度虽然要求不高,但是超出取值范围。并非所有实数都可以在计算机中精确表示。事实上,计算机最多精确表示 \(2^{32}\) 个 float,\(2^{64}\) 个 double。
实数常量的表示可以用普通表示和科学计数法表示,实数常量的类型都是 double,如果要使用 float 可以给实数常量添加后缀 f。
字符的表示和存储
用字符的 ASCLL 码值来表示字符,每个字符占用一个字节,用来存储它的 ASCLL 码。字符类型 char 可以当整数使用,可视为长度为一个字节的短整数,可以像整数那样四则运算。字符类型 char 的取值范围是 \([-128,127]\)。
字符类型常量有数字字符、小写字母字符、大写字母字符和其他可见字符等。C 语言将反斜杠 \ 定义为转义字符,通过转义字符可以表示一些特殊字符。
| 字符 | 含义 |
|---|---|
\\ |
反斜杠字符本身 \ |
\n |
换行符,移动到下一行行首 |
\r |
移动到当前行行首 |
\t |
横向跳格符,或制表符 |
\" |
双引号字符本身 " |
\' |
单引号字符本身 ' |
\0 |
字符串结束标志 |
\ddd |
1~3 位 8 进制数作为 ASCLL 码所代表的字符。例如 \101 表示字符 A |
\xhh |
1~2 位 16 进制数作为 ASCLL 码所代表的字符。例如 \x41 表示字符 A |
C 语言输入输出
printf 进行格式化输出,scanf 进行格式化输入。
整数类型的格式控制符有 %d、%u、%o、%x,分别对应整数、无符号整数、八进制整数、十六进制整数。注意 %x 和 %X 分别对应输入输出时是 a~f 还是 A~F。%u、%o、$x 只能输出非负整数。输入 8 进制和 16 进制时,前导 0 和前导 0x 可以省略。控制输出宽度的方法见前。
实数类型的输入输出时,用 %f 和 %lf 输入输出小数形式,用 %e 和 %le 输入输出指数形式。控制输出宽度的方式见前。
字符类型的输入输出还可以使用 getchar 和 putchar 函数。scanf 和 getchar 会读入任何字符(包括回车)。
键盘缓冲区是保存键盘输入的字符序列,标准输入流(stdin)能为输入库函数(scanf 等)提供字符流。用户通过键盘键入字符,字符存入键盘缓冲区。用户按下回车键后,产生一个换行字符 \n,存入缓冲区。缓冲区收到 \n 后,将存储的字符序列(包括 \n)转移到 stdin 中。scanf 和 getchar 等函数从 stdin 中读取字符和数据。成功读取数据后,所消耗的字符会自动从 stdin 中删除。对于有格式要求的 scanf 函数,如果遇到不满足格式控制字符串所要求的字符,scanf 会终止运行,不满足要求的字符仍然留在 stdin 中。特殊的格式控制字符 %n 表示从 stdin 字符流中消耗的字符个数(包含读取的和跳过的)。使用 scanf 读入时(除 %c 外),会自动跳过数据前面的空白字符(包括空格、换行符、制表符)。格式字符中也会有无前缀 % 的普通字符,一个普通字符匹配 stdin 中的一个字符(空格字符和换行符除外,他们都可以匹配 零个到多个 连续的空白字符)。普通字符匹配不成功时会中止 scanf 的运行。
scanf 的一个小总结
| 格式控制符 | 含义与功能 | 变量类型 | 示例输入 | 示例代码 |
|---|---|---|---|---|
%d |
读取有符号十进制整数 | int * |
123 |
scanf("%d", &num); |
%i |
读取整数(自动识别十进制、八进制、十六进制) | int * |
123(十进制)、0123(八进制)、0x123(十六进制) |
scanf("%i", &num); |
%u |
读取无符号十进制整数 | unsigned int * |
456 |
scanf("%u", &unum); |
%o |
读取八进制整数 | int * |
777 |
scanf("%o", &num); |
%x / %X |
读取十六进制整数 | int * |
1A3 |
scanf("%x", &num); |
%f |
读取单精度浮点数 | float * |
3.14 |
scanf("%f", &fval); |
%lf |
读取双精度浮点数 | double * |
3.1415926 |
scanf("%lf", &dval); |
%Lf |
读取长双精度浮点数 | long double * |
3.1415926 |
scanf("%Lf", &ldval); |
%e / %E |
读取用科学计数法表示的浮点数 | float * / double * |
2.5e-3 |
scanf("%e", &fval); |
%c |
读取一个字符(包括空格、换行) | char * |
A |
scanf("%c", &ch); |
%s |
读取一个字符串(遇到空白符停止) | char * (字符数组) |
Hello |
scanf("%s", str); |
%[abc] |
读取只包含指定字符集合的字符串 | char * |
abacab (若输入ab hello,则只读入ab) |
scanf("%[abc]", str); |
%[^abc] |
读取直到出现指定字符集中任一字符为止 | char * |
Hello w (输入Hello world,遇到w后的o停止) |
scanf("%[^orl]", str); |
%[^\n] |
读取一整行(包含空格,直到换行符) | char * |
Hello World! |
scanf("%[^\n]", str); |
%p |
读取一个指针地址 | void ** |
0x7ffeec6b4a34 |
scanf("%p", &ptr); |
%n |
不消耗输入,记录到此已读入的字符数 | int * |
- | scanf("%d%n", &num, &count); |
%% |
匹配一个百分号字符本身 | - | % |
scanf("%%"); |
关键要点与常见问题
1. 修饰符的使用
在格式控制符前可以添加修饰符来指定读取数据的宽度或长度:
- 宽度指定:例如
%5d表示最多读取5位数字。对于字符串%10s可以防止数组越界,更安全的写法是%9s(为结尾的\0留出空间)。 - 长度指定:
hd:读取short型整数,如%hd。ld:读取long型整数,如%ld。ll:读取long long型整数,如%lld。
2. 处理输入缓冲区
这是 scanf 使用时最常遇到的问题。
- 换行符陷阱:当使用
%c读取字符时,如果前面有输入(比如按了回车键),缓冲区里残留的换行符\n会被直接读入。解决方法是在%c前加一个空格,即" %c",这个空格会忽略之前的空白字符。 - 清除缓冲区:当输入格式错误导致程序异常时,可以使用
fflush(stdin)(注意标准未完全支持)或循环while(getchar() != '\n');来清空缓冲区。
3. 读取包含空格的字符串
普通的 %s 遇到空格就会停止,要读取整行文本,应使用 %[^\n],它会一直读取直到遇到换行符。另一种更推荐的方法是使用 fgets 函数,它在处理输入时更安全、更可控。
4. 检查输入是否成功
scanf 函数的返回值是成功读取并赋值的数据项个数。利用这一点可以检查用户输入是否符合预期。
int a, b;
if (scanf("%d %d", &a, &b) == 2) {
printf("成功读取两个整数:%d 和 %d\n", a, b);
} else {
printf("输入错误!\n");
}
数据类型转换
强制类型转换可以通过类型转换运算符强制转换数据的类型。(类型名)表达式,类型转换运算符是单目的,优先级高于一切双目运算符。自动类型转换是指当不同类型的数据进行混合运算时,编译系统按照一定的规则,将它们都转换成相同类型的值,在进行运算。注意,无论哪种转换,转换的都是 数据值 的类型,而不是变量的类型。
printf 函数是不会根据格式控制字符来对要输出的变量进行类型转换的,因此需要手动加入强制类型转换。只有当格式控制字符和输出变量的类型一致时才能保证输出正确结果。
在赋值运算中,当赋值号两边的数据类型不同时,右边的类型会转换为左边的类型,然后再赋给左边。如果右边数据类型的长度比做左边长,那么就可能丢失数据。函数调用中参数传递时,系统会将实参转换成形参的类型后赋给形参。函数有返回值时,系统将返回表达式的类型转换为返回值类型,赋值给调用函数。当参与运算的数据的类型不同时,编译系统会自动先将他们转换成同一类型,然后再进行运算,类型转换方向是按照数据类型长度增加的方向转换,以保证精度不降低。
所有的浮点数运算都是以 double 类型进行。整型和浮点型混合运算时,整形先转换成浮点型,运算的结果是浮点型。char 型和 short 型参与运算时,必须先转换成 int 型。有符号整型和无符号整型混合运算时,有符号型要转换成无符号型,运算的结果是无符号型。
表达式
算数表达式
双目运算符 + - * \ %,单目运算符 + - ++ --。
自增运算符 ++ 有前置和后置的用法。x++ 是取 x 的值作为表达式的值,然后再执行 x=x+1。++x 是先执行 x=x+1,然后取 x 的值作为表达式的值。
运算符具有优先级,其中括号 () 的优先级最高。单目 > 双目 > 多目。算数运算 > 关系运算 > 逻辑运算 > 赋值运算。同级的多个运算符具有结合性,单目运算符和赋值运算符是从右到左结合,其余大部分都是从左到右结合。
赋值表达式
首先计算右侧表达式的值,然后将这个值赋给左侧变量。如果类型不匹配,那么在赋值之前先将这个值转换成变量的类型。
赋值表达式可与运算结合,变成复合赋值表达式。如 += -= *= \=。
赋值表达式的优先级非常低,从右开始结合:x=y=z=3 等价于 x=(y=(z=3))。
关系表达式
通过关系运算符(> < >= <= == !=)连接起来的表达式,优先级仅次于加减乘除。
逻辑运算
逻辑与 &&,a&&b 为真当且仅当 a 为真且 b 为真。
逻辑或 ||,a||b 为真只需要 a 和 b 中至少有一个为真。
逻辑非 !,a 为真则 !a 为假,a 为假则 !a 为真。
逻辑非由于是单目运算符,因此其优先级最高。逻辑与优先级高于逻辑或。从左开始结合。
逻辑运算时,如果可以提前确定表达式的值时,将停止后面的计算。逻辑与运算时一旦运算到假,则停止后面的计算。逻辑或运算时一旦运算到真,则停止后面的运算。
条件表达式和三目运算符
exp1?exp2:exp3。等价于
if (exp1 为真) 条件表达式的值为 exp2 的值;
else 条件表达式的值为 exp3 的值;
条件运算符从右往左结合。
逗号表达式
expr1,expr2,expr3,……,exprN。依次计算 expr1 到 exprN,并取最后的子表达式 exprN 的值作为逗号表达式的值。逗号表达式的优先级最低。
位运算
位运算是定义在整数类型数据上的一种运算,包括按位反 ~,按位与 &,按位或 |,按位异或 ^,左移 <<,右移 >>。按位与、或、异或都具有交换律和结合律。
左移运算,右侧补 0。右移运算对于无符号数,左侧补 0;对于有符号整数,左侧既可以填充 0 也可以填充符号位。一般来说是填充符号位。应该避免对有符号整数进行右移运算。
使用异或运算可以交换两个数的值。
a=a^b;
b=b^a;//b=b^(a^b)=a;
a=a^b;//a=(a^b)^a=b;
位运算还可以用来查看浮点数的符号位、阶码和尾数。
长度运算符 sizeof
用于计算常量、变量以及数据类型的长度的运算符,长度以字节为单位。
运算符优先级
可以查看:C++运算符优先级。
第七章 数组
一维数组
定义:类型名 数组名[数组长度]。数组长度是整数类型常量。
系统根据数组中元素的类型及个数在内存中分配了一段连续的存储单元用于存放数组中的各个元素,并对这些单元进行连续编号,即下标,以区分不同的单元。
C 语言规定,只能引用单个的数组元素,而不能一次引用整个数组。引用形式:数组名[下标]。数组下标从 0 开始,下标不能越界。下标的合理取值范围是 [0,数组长度-1]。
初始化:类型名 数组名[数组长度]={初值表};。若只对部分数据初始化,则剩下数据为 0。有初值表时可以省略数组长度,此时数组长度为初值表数值个数。
二维数组
定义:类型名 数组名[行长度][列长度]。行长度和列长度都是整型常量表达式。
引用:数组名[行下标][列下标]。行下标的合理取值范围是 [0,行长度-1],列下标的合理取值范围是 [0,列长度-1]。
二维数组的元素在内存中按行/列方式存放,即先存放第 0 行的元素,再存放第 1 行的元素……其中每一行的元素再按照列的顺序存放。
初始化有两种方式:
- 分行赋初值。一般形式为:
类型名 数组名[行长度][列长度]={{初值表 0},……,{初值表 k},……}。且初始化也可以只针对部分元素,其余未初始化的元素赋值为 0。 - 顺序赋初值。一般形式为:
类型名 数组名[行长度][列长度]={初值表}。这种方式为根据数组元素在内存中的存放顺序,把初值表中的数据依次赋给元素。
赋初值时,如果对全部元素都赋予了初值,则可省略行长度。
一维字符数组
一维字符数组用于存放字符型数据。定义、初始化和引用同其他类型的一维数组一样。
字符串
字符串常量就是用一对双引号括起来的字符序列,即一串字符,它有一个结束标志 '\0'。字符串的有效长度是有效字符的个数。
C 语言将字符串作为一个特殊的一维字符数组来处理。
字符串的存储:数组初始化。字符串可以存放在一维字符数组中。例如:static char s[6]={'H','a','p','p','y','\0'}。字符数组的初始化还可以使用字符串常量,如static char s[6]={"Happy"}或static char s[6]="Happy"。
字符串操作一般用结束符 '\0' 来控制循环。
字符串赋值时可以一个个字符赋值,也可以直接赋值为一个字符串。
第八章 指针
计算机内存的每一个字节都有编号,称为内存地址或字节地址。内存地址是一个无符号整数,以字节为单位。计算机程序通过指针访问内存。
指针是一种数据类型,用于保存变量地址。类型名 *指针变量名。在变量名前添加 * 表示所定义的变量是指针,指针变量也是区分类型的。所有 指针变量都具有相同的字节大小,都是 4 个字节。所有类型的指针变量的值都代表了内存的地址值。
指针的值是无符号整数,指针的基类型是所指向的变量的类型。
获取变量指针的运算符 &,只能作用在拥有内存的存储单元或变量上。表达式 &a 的类型是指针,值为 a 的地址。访问变量时使用 * 运算符。* 运算符作用在指针上,作用是访问指针所指向的变量。
使用指针的注意事项:
- 使用
*p前,要保证p已经被赋值,并且指向了一个变量或实际的内存单元。 - 地址值可以是 0,称为空指针。空指针不指向任何实际的内存单元。
- 如果指针
p的地址值是 0,或者没有指向合法的变量,那么执行*p时将会发生严重运行错误。 - C 语言标准库中定义了空指针
NULL。在使用通过函数参数传进来的指针前,通常需要判断它是否为空指针。
指针可以自加,其结果是下一个指针。
定义指针变量时可以同时赋初值。初值可以是已有变量的地址,可以是已有的指针,也可以是常量。
数组与指针
数组名具有两重含义。作为变量,代表整个数组本身。作为常量,也是一个指针,指向数组的首元素。地址值不能改变,但是所指向的变量的值是可以改变的。
指针运算(加减)代表指针之前(之后)的若干元素。指针的比较比较的是地址值。指针相减得到二者间的元素个数,指针的地址相减得到指针间字节数。
当指针指向一片连续的函数时,可以像数组那样通过下标访问元素。
数组可以作为函数的实际参数,方法是将数组作为指针传输给函数。因此函数对应的形参必须是指针类型。数组作为实际参数时,其身份是首元素的指针。
字符串和字符指针
字符串存储在连续空间的字符序列,结尾必须添加 \0 作为结束标志。因此,表示一个字符串,只需给出指向字符串首元素的指针。字符串在 C 语言中的类型是 char *。字符串常量是一个指向字符串首地址的字符指针常量。
用字符数组处理字符串时,字符串内容可以修改。但用指针处理时,字符串内容不可修改。
scanf 函数
scanf("%s",s); 要求 s 是一个字符数组,或者指向了实际内存单元的字符指针。碰到空格、制表符、回车时停止读入。在读入的字符串末尾添加 \0 作为结束标志。
printf 函数
printf("%s",p);。p 可以是字符数组或字符指针,但都要求 p 指向了实际字符串(以 \0 结尾)
gets 函数
定义为 char * gets(char *s),作用是从标准输入 stdin 中读取一行字符,并把它存储在 str 所指向的字符串中。当读取到换行符时停止,并将换行符替换为 \0。指针 s 传递一个字符指针给 gets,gets 将读入的字符串写入以 s 为起始地址的一段连续的内存,gets 的返回值为指针 s。调用 gets 时,传给 s 的实际参数必须保存了一段连续内存的起始地址,保证所指向的内存具有足够多的空间能够容得下所读取的字符串,经常使用一个字符数组作为 s 的实际参数。
puts 函数
定义为 int puts(char *s)。作用是输出字符串 s 并在结束后换行。参数 s 为被输出的字符串指针,字符串必须以 \0 结尾。返回值为非负表示成功,返回 EOF(-1) 为失败。
strcpy 函数
字符串复制函数。char * strcpy(char *s1,char *s2)。参数要求:s2 指向一个以 \0 结尾的字符串,s1 指向一段可写入字符串的内存的起始地址,s1 所指向的内存足以容纳字符串 s2,返回值是字符串指针 s1。该函数相当于赋值操作 s1=s2。
strcat 函数
字符串连接函数。char * strcat(char *s1,char *s2)。参数要求:s1 和 s2 都指向以 \0 为结尾的字符串,字符串 s1 之后要有空间容纳 s2,返回值是字符串指针 s1,相当于复合赋值操作 s1+=s2。
strcmp 函数
字符串比较函数。字符串比较是从第 0 个字符开始,逐个字符进行比较,直到出现不一样的字符或者两个字符串都结束了。出现不一样的字符就比较这两个字符的大小,否则两个字符串相等。
int strcmp(char * s1,char * s2),参数要求 s1 和 s2 都指向以 \0 结尾的字符串。返回值为负数时表示 s1<s2,为零时表示 s1=s2,为正数时表示 s1>s2。可以理解为 s1-s2。
strlen 函数
字符串长度函数。int strlen(char * s),参数要求 s 指向以 \0 结尾的字符串,返回值为 s 指向的字符串的长度(不包括 \0)
动态内存管理
动态内存分配函数 void * malloc(size_t nsize) 和 void * calloc(size_t nitems,size_t size),动态内存释放函数 void free(void *p)。size_t 是一种数据类型,表示任何对象所能达到的最大长度,是无符号整数。
calloc 函数
void * calloc(size_t nitems,size_t size),功能是申请一段连续的内存空间,并将其中的字节置为 0。内存大小为 \(\text{nitems}\times \text{size}\)。参数中 nitems 表示单元的总数,相当于数组长度,size 表示单元的字节数。如果申请成功,返回内存的起始位置。否则返回空指针。
malloc 函数
void * malloc(size_t nsize)。申请一段连续的内存空间,但是保留其中的垃圾值。内存大小是 nsize。申请成功返回内存起始位置,失败返回空指针。
free 函数
void free(void *p)。功能是归还内存。该内存必须是之前动态申请的内存,且归还内存时必须全部归还。参数表示所归还内存的起始地址。
realloc 函数
void * realloc(void *p,size_t nsize)。功能是调整内存块的大小,参数分别是指向待调整的内存块的指针和新的内存块大小。成功返回新内存块的起始地址,失败返回空指针。
如果是将分配的内存调小,函数将返回原指针。如果将分配的内存调大,则分为该指针后是否还有足够空间。如果有,则返回原指针。如果没有,则重新申请一个内存块,并将当前的数据复制到新的位置,并将原来的内存块释放掉,然后返回新的内存块地址。此时原来的指针将会失效,即不能再使用它所指向的内存块。如果重新分配失败,那么原来的指针仍然有效。
第九章 结构
结构是一种自定义的、构造数据类型(或叫派生数据类型)。一个结构可以包含多个、不同类型的成员,结构类型将具有内在联系的数据汇聚在一起,形成一个整体。每个结构成员可以单独使用。
结构的定义:
struct 结构类型名称
{
结构成员变量表
};
结构在嵌套定义时,需先定义成员的结构类型,再定义主结构类型。
C 语言中结构变量的定义有 3 种方式。
单独定义
先定义一个结构类型,再定义结构变量。如
struct node a,b,c;
注意 struct 和结构类型名称要一起使用。
混合定义
在定义结构类型的同时定义结构变量。
一般形式为:
struct 结构名
{
类型名 结构成员名 1;
类型名 结构成员名 2;
……
类型名 结构成员名 n;
}结构变量名表;
无类型名定义
在定义结构变量时省略结构名。
struct
{
类型名 结构成员名 1;
类型名 结构成员名 2;
……
类型名 结构成员名 n;
}结构成员名表;
在该语句后无法再次定义这个类型的结构变量。
对结构变量的初始化可采用初值表的方法,可将初值表内的数值按顺序赋值给结构变量的各个成员。未被赋初值的成员的初值是 0。
一个结构变量所占的内存空间大于等于其各个成员的所占内存空间之和(由于存在边界对齐要求)。
结构变量的使用:使用操作符 . 来引用结构成员,形式为 结构变量名.结构成员名。结构变量也可以整体赋值,前提是两个结构变量有相同的结构类型。
结构数组是结构与数组的结合体,每个数组元素都是一个结构类型的数据。
结构指针是指向结构变量的指针。结构指针的值实际上是结构变量的首地址,即第一个成员的地址。可以使用 (*p).num 和 p->num 来引用结构成员。
结构指针可作为函数参数,由于只需要传递一个地址值,可以极大地提高参数传递的效率。
第十章 函数与程序结构
宏定义是 C 语言的常用功能,作用范围是从定义开始到文件结束。语法格式为 #define 宏名 宏体。宏名必须是合法的 C 语言的标识符,宏体可以是任意的字符串。C 语言允许宏定义嵌套。
宏定义可以带参数,格式为 #define 宏名(参数 1,参数 2,……,参数 n) 宏体。宏替换在程序编译预处理时完成,将变量名代替宏定义中的参数,再将宏替换为对应的语句。注意此处往往出现运算优先级导致的问题,因此需要多加括号保证运算结果正确。
文件包含的作用是把制定的文件模块内容插入到 #include 所在的位置。文件包含必须以 # 开头,这表示编译预处理命令。文件包含的格式为 #include<需包含的文件名> 或 #include"需包含的文件名"。前者编译程序将到 C 系统中设置好的 include 文件夹把制定的文件包含进来,后者编译程序首先到当前工作文件夹寻找被包含的文件,若找不到再到系统 include 文件夹中寻找文件。
条件编译支持根据实际的条件,选择需要被编译的代码。
如:
#if 条件判断语句1
程序段1
#elif 条件判断语句2
程序段2
#else
程序段3
#endif
ifdef 宏名 判断宏名是否被定义,ifndef 宏名 判断宏名是否未被定义。
多文件模块
实际的应用程序一般由多个 .c 文件和 .h 文件,编译器逐个编译 .c 文件,成功后生成对应的目标文件 .o,将所有的 .o 文件链接成一个可执行程序文件。
编写 .c 文件时
- 局部变量:先定义,后使用。
- 本文件的全局变量:先定义,后使用。
- 其他文件的全局变量:先声明,后使用。声明使用
extern关键字。 - 库函数:先包含对应的头文件,再使用。
- 本文件定义的函数:先定义(或先声明),后调用。
- 其他文件定义的函数:先声明,后调用。
- 自定义的数据类型:先定义,后使用。
注意:
- 不要在头文件中定义变量和函数。
- 在
.h文件中定义数据类型,不要在多个文件中重复定义同一个数据类型。 - 避免在多个头文件中重复声明同一个函数。
- 避免在多个头文件中重复声明同一个全局变量。
- 全局变量和函数只能被定义一次。
- 不要在多个文件中重复定义同一个全局变量。
- 不要在多个文件中重复定义同一个函数。
对于只在本文件中使用的全局变量和函数,建议定义为静态的全局变量和函数。
第十一章 指针进阶
指针数组:数组的各个元素的类型为指针,定义的一般格式为:类型名 *数组名[数组长度];,类型名指定数组元素所指向的变量的类型。
二级指针:指向指针的指针。此处操作比较复杂,需要明确指针指向的对象。
二维数组的指针形式:将二维数组看成数组元素为一维数组的一维数组,因此数组名类似于二级指针。以数组 a[3][4] 为例,a 的值是 a[0] 的地址,a+1 是第一行的地址,*(a+1) 是第一行首元素的地址,**(a+1) 是第一行首元素的值。
处理多个字符串时,可以使用二维数组或者指针数组。
命令行参数:生成可执行文件后,可在操作系统环境下以命令方式运行。输入命令时,在可执行文件(命令)名的后面可以跟一些参数,即一个命令行可以包括命令和参数,这些参数成为命令行参数。命令名和各个参数之间用空格分隔。C 语言中主函数 main() 可以有两个参数,用于接收命令行参数。带有参数的函数 main() 习惯上书写为 int main(int argc,char *argv[])。第一个参数接受命令行参数(包括命令)的个数;第二个参数接受以字符串常量形式存放的命令行参数(命令本身也作为一个参数)。
指针也可以作为函数的返回值。但不能返回在函数内部定义的自动变量的地址,因为所有的自动变量在函数返回时会消亡,其值不再有效。
指针也可以指向函数,成为函数指针。通过函数指针可以调用函数,函数指针也可以作为函数的参数。函数指针定义的一般格式为 类型名( * 变量名)(参数类型表)。调用函数可以使用 函数 或者使用 *函数指针。
链表
链表由若干个同一结构类型的“结点”依次串接而成的。链表变量一般用指针 head 表示,用于存放链表首结点的地址;链表中每个结点由数据部分和下一个结点的地址部分组成。通常使用结构的递归定义来定义结点的数据类型。
链表的建立
struct node *head,*tail,*p;
head=tail=NULL;
while (读入不空)
{
p=(struct node *)malloc(sizeof(struct node));//申请新空间
……//赋 p 的初值
p->next=NULL;
if (head==NULL) head=p;
else tail->next=p;
tail=p;
}
链表的遍历
while (now!=NULL)
{
……//操作
now=now->next;
}
插入结点
先找到正确的位置,引入 3 个辅助指针 p1,p2,p3,分别指向要插入的结点,结点 i,结点 i+1(新结点插在结点 i 和结点 i+1 之间)。
p1->next=p3;
p2->nest=p1;
删除结点
引入辅助指针 p1,p2,分别指向要被删除的结点的前一个结点和要被删除的结点。
p1->next=p2->next;
free(p2);
注意如果删除的是头结点,头结点需要后移。
第十二章 文件
文件系统是计算机操作系统的重要功能和组成部分,管理计算机系统和用户的文件,是管理文件的工具。
文件可分为系统的程序和资料和用户的程序和资料,也可分为可执行文件和不可执行文件、文本文件和二进制文件。
文件路径的构成:盘符 + 冒号 + 反斜杠 + 目录名 + 反斜杠 + 文件名。在命令行中,如果使用带空格的文件路径作为命令参数,需要用双引号将路径扩起来。
文件是保存在外存储器上的一组数据的有序集合。文件数据长久保存,数据长度不定。按序存储文件数据,同时支持定位文件存取位置。
C 语言中的文件是数据流(字节流)。文本文件中的字节都是字符的 ASCLL 码,二进制文件中的字节既可能是字符的 ASCLL 码,有可能是二进制的整数、浮点数或其他类型数据(如图像、语音、视频等)。无论是文本文件还是二进制文件都是字节流,纯文本文件的每个字节都与一个字符对应。
文件缓冲区是在磁盘和内存之间的缓冲区(因为磁盘的速度很慢,而内存传输的速度很快),是内存中开辟的一块区域。读文件时,从缓冲区中读取数据,一旦检测到缓冲区空了,整块地读入文件内容填充到缓冲区。写文件时,将内容写入缓冲区,一旦检测到缓冲区空了,将缓冲区的数据一次性写入磁盘。
在 C 语言的标准输入输出库中,定义了一个结构体(FILE),表示了文件缓冲区及其读写和运行状态,同时定义了一组函数来实现文件缓冲区的创建、读、写、关闭以及状态和错误检测。所有读写文件的函数,都是缓冲区函数,即 C 语言中的文件操作都是缓冲区函数。由于文件缓冲区和文件是一一对应的,所以在编程时可以不加区分的看待他们。
typedef struct{
short level; // 缓冲区使用量
unsigned flags; // 标志状态
char fd; // 文件描述符
short bsize; // 缓冲区大小
unsigned char * buffer; // 缓冲区指针
unsigned char * curp; // 工作指针
unsigned char hold; // 其他信息
unsigned istemp;
short token;
}FILE;
typedef 是给一种数据类型定义新的名称,而不是定义新的数据类型,格式为 typedef 原类型名 新类型名;。
具体用法是
- 按照原本的方式写出一个变量的定义。
- 把变量名改成目标名。
- 在语句前面添加
typedef。
文件操作的相关函数
fopen()
定义为 FILE* fopen(const char* filename, const char* mode);,作用是从磁盘中找到指定文件,然后再内存中分配保存一个 FILE 类型结构的单元,再在内存中分配文件缓冲区单元,最后返回 FILE 结构地址。函数的第一个参数表示文件名字,第二个参数表示对该文件的操作方式,具体如下
文本文件:
| 使用方式 | 含义 |
|---|---|
| "r" | 打开文本文件进行只读 |
| "w" | 建立新文本文件进行只写 |
| "a" | 打开文本文件进行追加 |
| "r+" | 打开文本文件进行读/写 |
| "w+" | 建立新文本文件进行读/写 |
| "a+" | 打开文本文件进行读/写/追加 |
二进制文件:
| 使用方式 | 含义 |
|---|---|
| "rb" | 打开二进制文件进行只读 |
| "wb" | 建立新二进制文件进行只写 |
| "ab" | 打开二进制文件进行追加 |
| "rb+" | 打开二进制文件进行读/写 |
| "wb+" | 建立新二进制文件进行读/写 |
| "ab+" | 打开二进制文件进行读/写/追加 |
fopen 函数有返回值,如果打开文件成功就返回地址,失败就返回 NULL。
fclose()
定义为:int fclose(FILE *stream);,第一个参数代表文件指针,返回一个整数,该数为 0 表示正常关闭文件,否则表示无法正常关闭文件。关闭文件操作除了强制把缓冲区中的数据写入磁盘外,还将释放文件缓冲区单元和 FILE 结构,使文件指针和具体文件脱钩。
fgetc()
字符方式文件读函数。定义为:int fgetc(FILE *stream);。参数是文件指针,作用是从文件指针所指向的磁盘文件读入一个字符。
fputc()
字符方式文件写函数。定义为:int fputc(int ch, FILE * stream);,第一个参数是要写入的字符,第二个参数是文件指针,作用是把一个字符写入到文件指针所指示的磁盘文件上。如果写入成功返回被写入的字符,失败返回 EOF。
fgets()
字符串方式文件读函数。定义为:char* fgets(char* str, int num, FILE * stream);。第一个参数表示读入的字符串的存储地址,第二个表示要读入的字符数,第三个是文件指针。当函数读取的字符达到了指定的个数(num-1),或接收到换行符,或接收到文件结束标志 EOF 时,将在读取的字符后面自动添加一个 '\0';若有换行符,则将换行符保留(换行符在 '\0' 字符之前);若有 EOF,则不保留 EOF。该函数如果执行成功,则返回读取的字符串;如果失败,则返回空指针,此时 str 的内容不确定。
fputs()
字符串方式文件写函数。定义为:int fputs(const char* str, FILE * stream);。第一个参数是要被写入的字符串,第二个参数是文件指针。被写入的字符串的结尾 '\0' 不会被写入文件。函数执行成功则返回所写的最后一个字符;否则,函数返回 EOF。
fscanf()
格式化方式文件读函数。定义为:int fscanf(FILE *stream, const char *format, ...);,第一个参数是文件指针,第二个参数是格式字符串,省略号表示输入表,具体用法与 scanf() 函数类似。使用 fscanf() 进行输入时,系统会自动根据规定的格式把输入的代表数值的字符串转换成数值。
fprintf()
格式化方式文件写函数。定义为:int fprintf(FILE *stream, const char* format, ...);。第一个参数表示文件指针,第二个是格式字符串,省略号表示输出表,具体用法同 printf() 函数。使用 fprintf() 进行输出时,系统会根据规定的格式,把输出的二进制数值转换成字符串写到文件中。
fread()
数据块方式文件读函数。fread() 用于读写数据块(制定字节数量),可用来读写一组数据。可从二进制文件中读入一个数据块到变量。定义为:size_t fread(void* ptr, size_t size, size_t nmemb, FILE *stream);,第一个参数表示存放输入数据的首地址,第二个参数表示数据块的字节数,第三个参数表示要读入的数据块数,第四个参数是文件指针。
fwrite()
数据块方式文件写函数。定义为:size_t fwrite(const void*ptr, size_t size, size_t nmemb, FILE *stream);。第一个参数表示存放输出数据的首地址,第二个参数表示数据块的字节数,第三个参数表示要读入的数据块数,第四个参数是文件指针。
rewind()
重定位文件首函数。定义为:void rewind(FILE *stream);,参数是文件指针。作用是定位文件读写位置指针,使其指向读写文件的首地址,即打开文件时文件读写位置指针所指向的位置。
fseek()
指针移动控制函数。定义为 int fseek(FILE *stream, long offset, int whence);。第一个参数是文件指针,第二个参数是移动偏移量(使用常量时需在结尾加 L),第三个参数时起始位置,可取文件首部,当前位置和文件尾部,分别对应值 0、1、2,或常量 SEEK_SET,SEEK_CUR,SEEK_END。
ftell()
获取指针当前位置函数。定义为:long ftell(FILE *stream);,参数是文件指针,返回值为相对于文件开头的位移量(字节数)。函数出错时返回 -1L。
feof()
文件末尾判断函数。定义为:int feof(FILE *stream);。返回值非 0 表示到达文件末尾,返回 0 表示未到达,检测对象是 EOF。
ferror()
读写错误检查函数。定义为:int ferror(FILE *stream);,参数是文件指针。返回值为 0 表示未出错,否则表示有错。
clearerr()
出错标记清除函数。定义为:void clearerr(FILE *stream);,参数是文件指针。用于清除出错标志和文件结束标志,使它们为 0 值。
fflush()
刷新文件缓冲区函数。定义为:int fflush(FILE *stream);,参数是文件指针。用于强制刷新文件缓冲区,将缓冲区中的数据立即写入磁盘,成功返回 0,失败返回非 0。
文件数据的访问顺序分为:顺序访问,随机访问。顺序访问从文件头部开始,按照字节顺序依次访问,直到文件结束。是常见的文件访问顺序,适用于文本文件和二进制文件。随机访问利用函数 fseek,首先定位到目标位置,然后进行读写。适用于读写特定的二进制文件。

浙公网安备 33010602011771号