从标题中得知,本系列文章是围绕 “C语言” 展开学习的笔记总结。且目的很明确,笔记内容偏应试,适用于计算机等级考试、考研专业课 ( C语言 ) 等的复习使用。文章推崇总结性、对比性的学习方法,对于模糊的知识模块需自行查阅参考书目,深化理解以取得理想效果。
针对 C 语言程序,推荐几本辅导复习的书目:
基础篇
《谭浩强: C 语言程序设计》
: 必不可少的经典教程,权威性的标准答案源 ( 当然仅限于应试范围 )。例如,因讨论条件而异,如编译系统不同,部分题目的答案就具有了争议性。《明解 C 语言》
: 入门基础教学。值得称赞的是,每个知识模块都附有实例,且实例的源码结构清晰,代码规范及注释到位,适合入门使用。
进阶篇
《征服 C 指针》
: C 语言的学习过程中,指针的运用是最大的难关。无论是在实际应用、应试中都是不可忽视的。对于作者前桥和弥,其一针见血的文风,在掌握一定基础之后,是深入了解 C 语言的一位不可多得 “良师益友” ( 书中有不少作者交谈式的独白,别有一番阅读风味 )。
内容总览
壹 程序设计和 C 语言
计算机程序与语言
- 程序:计算机能识别和执行的指令。
- 语言:人和计算机交流、人和计算机能识别的语言。
- 计算机语言发展阶段:
机器语言 | 符号语言 | 高级语言 ( 面向过程、面向对象 ) |
---|---|---|
0和1指针 | 英文、数字表示指令 | 人类自然语言和数字语言 |
C语言
- 特点:
- 语言简洁、紧凑,使用方便、灵活;
- 运算符丰富 ( 单目、双目、三目运算符 );
- 数据类型丰富,其中包括:整型、浮点型、字符型、数组类型、指针类型、结构体类型、共用体类型、枚举型 );
- 结构体控制语句;
- 直接访问物理地址 ( 对硬件直接操作 );
- 可移植性好.
- 结构:
- 以程序由一个或着多个
源文件
组成, 源文件中包括:- 预处理命令 ( #include、#define、#typedef 等 );
- 全局声明 ( 全局变量、局部变量 );
- 函数定义 ( 参考函数原型 ).
- 函数是 C 程序的主要组成部分。
- 函数包括
函数首部
和函数体
。 函数体包括声明部分
和执行部分
。 - 程序总是从 main() 函数开始执行的。且 main() 函数有且仅有一个。
- C 程序对计算机的操作有 C 语言完成。
- 数据声明和语句必须有分号 ( 作为结束 )。
- C 本身不提供输入输出语句。
- 以程序由一个或着多个
程序设计的任务
- 程序设计的任务:
- 问题分析;
- 设计算法;
- 编写程序;
- 对源文件编辑、编译 ( *.obj ) 和连接 ( *.exe );
- 运行程序并分析结果;
- 编写程序文档.
- 对于编译,预编译和连接的概念及比对:
- 编译:检索语言错误;把源程序转为二进制形式的目标程序。
- 预编译:通过预处理得到的信息与程序其他部分一起,组成完整的、可以正式编译的源程序。
- 连接:与函数库相连接。
贰 程序之魂:算法
引入
对数据的描述:所用数据的类型和数据的
组织形式
。组织形式
:数据结构,特定关系的数据元素的集合。对操作的描述:计算机进行操作的步骤 —
算法
。- 从简理解:
数据结构 + 算法 = 程序
。
算法
- 概念:对特定问题求解的方法和描述。
- 特征:
- 有穷性:
有穷时间
执行结束; - 确定性:算法
唯一执行路径
,既相同输入执行相同路径; - 可行性:
有限次
; - 零或一个以上的
输入
; - 一个或以上的
输出
;
- 有穷性:
- 要求:
- 正确性;
- 可读性;
- 健壮性;
- 效率与低存储量需求 ( 时间复杂度和空间复杂度 )
- 时间复杂度:
- 时间复杂度:也称渐进时间复杂度,即算法执行时间的增长率和 f(n) 的增长率相同。
- 渐进时间复杂度:$T(n) = O(f(n))$
- $f(n)$ 为问题规模 n 的某个函数。
- 算法中的基本运算 ( 最深层循环内的语句 ) 的频度与 T(n) 同数量级。
- 空间复杂度:
- 空间复杂度:算法所需存储空间的量度。
- 渐进空间复杂度:$S(n) = O(f(n))$
- 原地工作:额外空间相对输入的数据量来说是常数。
三种基本结构和改进流程图
- 三种基本结构:
- 顺序结构;
- 选择结构;
- 循环结构:当型循环结构 / 直到型循环结构;
- 改进的流程图:N-S 流程图
结构化程序设计方法
- 自顶向下;
- 逐步细化;
- 模块化设计:
分而治之
;注意模块独立性
- 结构化编码;
叁 简单的 C 语言程序设计
数据的表现形式及运算
常量
- 概念:程序运行期间,其值不能改变。
类型:
- 整型常量
- 字符常量 ( 与常变量作比对 [ 注释1 ] )
- 普通字符
- 转移字符:
\n
,\t
,\012
( 8 进制 ),\x41
( 16 进制 ) - 符号常量:
#define PI 3.14159
实型常量
- 10 进制小数形式:
3.14L
指数形式 ( 科学计数法 ):
1
2
3
48.7e-25; // 正确
8.7e-2.5; // 错误
87e+25; // 正确
87e-25; // 正确
- 10 进制小数形式:
[ 注释1 ] 符号常量与常变量的比较。
符号常量 | 常变量 |
---|---|
不占内存单元,预编译后符号不复存在 | 占存储单元 |
不能重新赋值 | 不能改变其值 |
变量
先定义,后使用。
- 概念:程序运行期间,其值可以改变。
- 包含属性:
- 数据类型 ( 整型、浮点型、字符型 )
- 存储类别 ( 自动变量,静态变量 )
类型:
- 常变量:变量存在期间其值不能改变。
const int a = 10
- 自动变量与静态变量
全局变量与局部变量
从存储位置、生存周期、作用区域讨论差异性。[ 注释2 ]
- 常变量:变量存在期间其值不能改变。
标识符:一个对象的名称。除关键字外,字符、数字和下划线组成。且要求只能是字母或下划线开头。
[ 注释2 ] 局部变量与全局变量,自动变量与静态变量,内部函数与外部函数的比较。
局部变量 | 全局变量 | |
---|---|---|
存放于动态存储区 | 存放于静态存储区 | 位置 |
在定义函数内起作用 | 自定义位置开始,本文件起作用 | 作用域 |
函数调用完释放内存 | 程序结束时释放内存 | 生存期 |
- 静态的局部变量,存放于静态存储区,程序结束时释放内存。
- 静态的全局变量,不是因声明 static,而误解全局变量才存放于静态存储区。
- 局部变量,声明存储类型指变量存储区以及产生的生存期问题。
- 全局变量,声明存储类型指变量作用域的扩展问题。
自动变量 | 静态变量 |
---|---|
1. 声明该变量的语句块被执行结束释放内存(栈) | 1. 程序结束时才释放内存 |
2. 每次函数调用时赋值 | 2. 保留上一步的赋值 |
3. 在编时赋予初值0或’\0’ |
- 对比 malloc() 函数分配的内存,需调用 free() 函数释放内存 ( 堆 )。
内部函数 | 外部函数 (default) |
---|---|
本文件内使用(不限位置) | 可供其他文件使用(不限位置) |
定义:static 函数类型 函数名 | 定义:(extern) 函数类型 函数名 |
数据类型
- 基本类型:
关键字 | 字节 | 取值范围 | |
---|---|---|---|
整型 | int | 2/4 | $-2^{15}$ ~ $-2^{15}-1$ / $-2^{31}$ ~ $2^{31}-1$ |
unsigned int | 2/4 | 0 ~ $-2^{16}-1$ / 0 ~ $-2^{32}-1$ | |
字符型 | char | 1 | $-2^7$ ~ $2^7-1$ |
unsigned char | 1 | 0 ~ $2^8-1$ | |
单浮点 | float (有效小数:6) | 4 | — |
双浮点 | double (有效小数:15) | 8 | — |
- 关于基本类型的特别说明:
- 字符是按其 ASCII 形式存储的。
- 单浮点定义:
float a = 3.14f
- 双浮点定义:
double a = 3.14
- 长浮点定义:
long double a = 3.14L
- 派生类型:
- 指针类型:指向函数的指针、多重指针
- 数组类型:指针数组
- 构造类型:详情见第玖章:构造类型
- 结构体类型
- 共同体类型
- 枚举类型
- 类型转换:
- 低精度向高精度转换;
- 强制转换括号加类型;
int a = (int)3.14
- 多类型变量混合运算,取最高精度的类型;
肆 选择结构程序设计
关系运算符及其优先次序
各类运算符的优先级:
- 单目运算符 > 双目运算符 (算术、关系、逻辑) > 三目运算符
- 优先级由高到低排序:
- 初等运算符:
(),[],->,.
- 单目运算符:
!,++,--,~
- 算术运算符:
*,/,%
,+,-
- 关系运算符:
>,<,>=,<=
,!=,==
- 逻辑运算符:
&&,||
- 条件运算符:
a > b : a : b
- 赋值运算符:
a += 1
- 逗号运算符:
(a,b)
- 初等运算符:
结合方式:
同一级的运算符,由结合方式决定优先级。
- 自左向右:
初等、单目、关系、逻辑、逗号运算符
- 自右向左:
条件、赋值运算符
- 自左向右:
表达式
- 算术表达式:先乘除模,后加减,再由“自左向右”原则运算。
- 混合运算
- 优先级:遵循各运算符的优先次序。
- 结合性:算术运算符 (自左向右);赋值运算符 (自右向左)。
- 不同类型的混合运算:结果的类型为 最高精度 的数据类型。
运算符与表达式
关系运算符和关系表达式 ( a+b>c ) -> True or False?
0 表示假,!0 表示真。
逻辑运算符和逻辑表达式
- 逻辑运算:5 && 4 => 1;5 && 0 => 0;
按位逻辑:5 & 4 => 4;
关于逻辑运算与按位逻辑的比较:
优先级:按位逻辑运算 > 逻辑运算
按位逻辑的巧用:取最大max = a | b
;取最小min = a & b
条件运算符和条件表达式:
a > b ? a : b
选择结构的嵌套
if
语句只有两个分支可供选择,else
总是与它上面最近的未配对的if()
配对。1
2
3
4
5
6
7
8
9
10
11
12
13if(express1){
if(express2){
...
} else {
...
}
} else {
if(express3){
...
} else {
...
}
}
switch
语句实现多分支选择结构。1
2
3
4
5
6
7
8
9switch(express1){ // 整型、字符型
case 常量/常量表达式:
语句1;
break; // break 为拦截作用
case 常量/常量表达式:
语句2;
break;
default: 语句3;
}
伍 循环结构程序设计
while
语句实现:1
2
3
4
5express1;
while(express2){
express3;
...
}for
语句实现循环:1
2
3for(express1; express2; express3){
...
}do...while()
语句实现循环:1
2
3
4
5express1;
do{
express3;
} while(express2);break
、continue
与goto
语句:- break:从循环体内跳出循环体。多层嵌套循环,跳出相邻一层循环。
- continue:提前结束本次循环。
- goto:跳出多层循环。
陆 数组
概念
- 一组有序数据的集合。
- 数组中每一元素同属一个数据类型。
- sname[0] <=> *(p+0) <=> 第一个数组元素。
定义
一维数组
定义:
类型符 数组名[常量表达式]
:正确的定义方式。类型符 数组名[变量]
: 错误的定义方式,不能为变量。
初始化:
1
2int array[] = {1, 2, 3, 4, 5};
in array[5] = {0}; // 5个元素都为 0。引用:
1
2
3int *p = &array[0]; // 等同于 int *p = array
p++; // 指针运算
*(p+i); // 取第i位元素
二维数组
定义:类型符 数组名[常量表达式][常量表达式]
初始化:
1
2
3
4
5
6int array[2][2] = { {1, 2}, {3, 4} };
int array[2][2] = { 1, 2, 3, 4 };
int array[][2] = { {1, 2}, {3, 4} }; // 既只允许最外层元素个数定义时为空
int array[][2] = { {0}, {3, 4} }; // 正确
int array[][2] = { {}, {3, 4} }; // 错误
引用:
1
2
3int num = array[1][1];
int *p = array;
*(*(p+j)+j); // 等同于array[i][j];
字符数组
定义:
char array[10]; <=> int array[10];
字符型数组是以整型形式存放的 ( ASCII )。
初始化:
- 字符串常量不可以数组形式取具体位置进行元素修改。
( array == “Hello” ) => True or False ?
False,array 与字符串常量比较的是内存地址。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20char array[0] = 'A';
char array[] = {"Hello"};
// 字符数组的存储情况:| H | e | l | l | o | \0 |
// sizeof() -- 6
// strlen() -- 5
char array[] = {'H', 'e', 'l', 'l', 'o'};
// sizeof() -- 5
// strlen() -- 5
int array[] = {"Hello"};
// sizeof() -- 4
// strlen() -- 1
int array[] = {'H', 'e', 'l', 'l', 'o'};
// sizeof() -- 20
// strlen() -- 4
char *array = "Hello"; // 字符串常量
引用:
- 若字符数组中,存在 ‘\0’ 两个或以上,系统则以第一次出现的位置提前终止字符输出。
stdin 和 gets() 搭配,可获得换行符、空格等字符。 ( 需结束标记符来终止输入 )
1
2
3
4
5scanf("%c", &array[0]);
printf("%c", array[0]);
scanf("%s", array);
printf("%s", array);
应用:
- gets(字符数组) — 输入一字符串到字符数组中
- puts(字符数组) — 输出一字符串到终端
- strlen(字符数组) — 测一字符串的实际长度
strcat(char *src1, const char *src2);
数组src2后接于src1,src1中的’\0’被覆盖。且数组src1必须足够大,以容纳数组src2。
strcpy(char *src1, const char *src2);
数组src1必须足够大,以容纳数组src2。
strcmp(const char *src1, const char *src2);
实际为ASCII的比较,其返回值为 <0、==0,>0 的情况。0、==0,>
strlwr(字符串) — 将字符串中大写字母转为小写字母
- strupr(字符串) — 将字符串中小写字母转为大写字母
- atoi(字符串) — 字符串转int型
- atol(字符串) — 字符串转long型
atof(字符串) — 字符串转double型
有几点需要特别声明:
1) 字符串处理函数需加入头文件:#include <string.h>
.
2) 需掌握字符串函数自定义方法实现。
3) 大部份字符串处理函数多数以标记量’\0’为临界点,若字符数组中含两个或或以上,需注意实际的结果。
4) 引用atoi()、atol()、atof()函数需引用#include <stdlib.h>
柒 函数
为什么要用函数
- 模块化程序设计:每一函数实现一特定的功能,函数的名称既反映功能。
更好地代码复用:使用库函数;使用自己编写的函数。
代码复用
:减少重复编码程序段的工作量。对于所有完成相同功能的组件,应抽象出一个接口,它们都实现该接口。具体在 Java 中,所有完成相同功能的组件都实现该接口
或从该抽象类中的继承
。
定义函数
建立存储空间的声明。
- 定义函数:
函数返回类型
函数名
函数参数
函数体
(变量定义、声明,执行语句)- 函数返回类型:基本数据类型 / void型;
- 函数名:驼峰式命名法;
- 函数参数:实参、形参.
函数声明
不需要建立存储空间的声明。
- 函数原型 (Prototype):函数返回类型、函数名、参数类型、参数个数、参数顺序。
- 函数声明的方法:
- 使用函数原型;
- 同一源文件,在调用该函数的前面定义 (可打包到自定义头文件中)。
函数调用
- 嵌套调用、递归调用 ( 直接或间接调用该函数本身 )。
- 实参和形参:
- 概念:
- 实参:常量、变量或表达式、函数 (返回值)
- 形参:函数调用期间临时分配内存,值从实参中获得,调用结束后释放内存空间。
- 实质:值传递、地址传递
- 概念:
捌 指针
指针是什么
- 指针变量:保存变量地址的变量。
指针类型:
- 指针类型的变量:存放地址
指针类型的值:对应内存地址存放的值
在
swap(int \*a, int \*b);
的案例中可以形象说明两者的区别。
指针移动 (运算:加、减)
- 对指针加一、减一运算,即地址会增加或减少一单位长度。
- 单位长度具体指当前指针所指向数据类型的所占空间大小。
指针类型
空指针
- 确保没有指向任何一个对象的指针,通常以宏定义 NULL(0) 表示空指针的常量值。
关于
NULL
、0
和'\0'
,大部分情况都为零。特别地:1
2int *p = 0; // 正确,编译器将指针指向内存地址为 0 处。
int *p = 3; // 错误,赋值的数据类型不相符。
指针类型的派生
指向函数的指针:
1
void (*func(int));
指向数组的指针 (
多重指针
):1
int (*p)[5];
数组类型的派生
指针数组:
1
int *p[5]; // 存放5个指向int类型的指针。
用英语解读各种各样的C语言声明:
C语言 | 英语表示 | 中文表示 |
---|---|---|
int huge; | huge is int | hoge是int型 |
int huge[10]; | huge is array[10] of int | hoge是int型的数组 |
int huge[2][4]; | huge is array[2] of array[4] of int | hoge是int型的数组的数组 |
int *huge[10]; | huge is array[10] of point to int | hoge是指向int型的指针的数组(存放指针变量) |
int (*huge)[10]; | hoge is pointer to array[10] of int | hoge是指向int型的数组的指针 |
int func(int a); | func is function(int a) returning int | func是返回int型的函数 |
int (*func)(int a); | func is pointer to function(int a) returning int | func是指向返回int型值的函数的指针 |
指针的应用
指针与数组
一维:
1
2
3p[i] // 等同于 *(p+i)
i[p] // 等同于 *(i+p)
&p[i] // 等同于 (p+i),即第i个元素的地址二维:
1
2huge[i] // 等同于 *(huge+i),即第i行的首地址
*(huge+i)[j] // 等同于 *(*(p+j)+j),即 huge[i][j]
指针与字符串
字符指针变量
定义:
1
2
3
4
5
6
7
8char *array = "World";
array = "hello"; // 改变指向
char array[] = "Hello";
array = "World"; // 错误的做法
char *array = "Hello World";
array += 6; // 改变指向 (首地址改变)
字符数组
定义:
int array[] = "Hello"
使用:
printf("%c", array[0])
字符指针变量的值是不能改变的,即已是字符串常量。
1
2char *array = "Hello";
array[0] = 'W';
指针与函数
作为参数
即传递的是指向初始元素的指针。
- 数组名作函数参数:
int func( int array[] )
int func( int \*array )
- 多维数组作函数参数:
int func( int (\*huge)[10] )
int func( int huge[2][4] )
- 指向函数的指针作函数参数:
int func( int (\*p)(int) )
指针数组作main函数形参:
int func( int argc, char \*argv[] )
argv: 文件名 + 其他参数
字符指针作函数参数。
作为返回值
返回指针值的函数,即返回的是地址。比如,返回的指针指向结构体变量、字符变量等。
玖 构造类型
构造类型:用户自己建立数据结构
结构体类型
定义:
1
2
3
4struct Name {
int num;
char word[59];
} *p, name[5];初始化:所有成员一起赋值。
使用:
1
2
3
4
5
6
7name[i].num;
p->word[i];
(*p).num;
struct Name *tmp;
tmp = name;
(tmp++)->num; // 先'++'操作,后'->'操作
- 大小:成员变量所占内存长度总和。
共用体类型
定义:
1
2
3
4
5union Name {
int num;
double digital;
char word;
} *p, name[5];初始化:只允许给一个成员变量赋值。
1
2
3
4union Name tmp = {10};
union Name tmp = {.word = 'Y'};
t.digital = 2.0;
t.word = 'N'; // 最终的赋值使用:
1
2
3name[i].num;
p->word[i];
(*p).num;
大小:成员变量所占内存长度最大者。
关于结构体、共用体类型的内存长度问题,遵循 4 字节倍数的原则进行内存布局对齐。
1
2sizeof(struct Name) // ==> 64 (63)
sizeof(union Name) // ==> 4 (4)
枚举类型
定义:
1
2
3enum Week {
sun, mon, tue, wed, thu, fir, sat // 默认参数从0开始
} week;
初始化:
1
2
3
4enum Week {
mon = 1, tue = 2, wed = 3,
thu = 4, fir = 5, sat = 6,sun = 7 // 默认参数从 0 开始
} week;使用:
week.mon
;
Typedef 声明新类型名
- 含义:引入变量别名,而不是另外地给变量分配空间。
使用:
1
2
3
4typedef int Integer;
// 若编译器中,int 为 2 字节,满足移值需求可以 long 型替换。
typedef long Integer;
Interger num = 1;与
#define
宏定义的区别:#typedef
:编译阶段处理。#define
:预编译阶段处理,实质是字符串替换。
拾 文件处理
文件与流
stdin
:标准输入流,用于读取普通输入的流,在大多数环境中为键盘输入。scanf() 与 getchar() 等函数会从这个流中读取字符。stdout
:标准输入流,用于写入普通输入的流,在大多数环境中为输出至显示器界面。printf()、puts() 与 putchar() 等函数会向这个流写入字符。stderr
:标准错误流,用于写出错误的流,在大多数环境中为输出至显示器界面。
文件分类
- ASCII 文件 ( 文本文件 ):每一字节存放一字符的 ASCII 代码。
二进制文件:
- 优:节约存储空间
劣:精度有限
例如:整数 10000,ASCII 形式存储空间为 5 字节,二进制形式存储空间为 4 字节。
文件类型指针:FILE 型
需引用
#include <stdio.h>
指向文件的指针变量并不是指向外部介质上的数据文件开头,而是指向内存中的文件信息区的开头。
打开文件
- 原型:
FILE *fopen(const char *filename, const char *mode);
- 定义:
FILE *fp = fopen("example.txt", "r");
文件类型 | 文本文件 | 二进制文件 |
---|---|---|
模式 | r w a | rb wb ab |
只读;只写(文件存在,则长度清零);追加 | 只读;只写(文件存在,则长度清零);追加 | |
r+ w+ a+ | rb+ wb+ ab+ | |
读和写(打开文件);读和写(建立文件;文件存在,则长度清零);读和写(打开文件) | 读和写(打开文件;文件存在,则长度清零);读和写(建立文件);读和写(打开文件) |
关闭文件
- 原型:
int fclose(FILE *stream);
- 返回值 ( True:0;False:EOF(-1) )
- 数据存储的过程:数据 —> 缓存区 (充满) —> 文件
- 若不关闭文件,将会造成数据丢失。
- 若突然关闭文件,缓存区传输到文件的过程给中断,造成数据丢失。
顺序读写数据文件
格式化读取文件:
1
2
3
4
5
6
7int fscanf(FILE *stream, const char *format, ...);
// 返回值:
// Ture - 返回成功赋值的输入项数
// False - 返回文件结束标记EOF(-1)
// 使用实例
fscanf(fp, "%s%lf%lf", name, &height, &weight);
格式化写入文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28int fprintf(FILE *stream, const char *format, ...);
// 返回值:
// Ture - 返回发送的字符数
// False - 返回文件结束标记EOF(-1)
// 使用实例:获得当前运行时间,并存入文本中
time_t current = time(NULL);
struct tm *timer = Localtime(¤t);
// 将日历时间time_t型的值转换为分解时间tm结构体类型的值
// 其中,tm结构体为:
struct tm {
int tm_sec; // 秒(0 - 61)
int tm_min; // 分 (0 - 59)
int tm_hour; // 时 (0 - 24)
int tm_mday; // 日 (1 - 31)
int tm_mon; // 月 (0 - 11)
int tm_year; // 从1900至今,经历了多少年
int tm_wday; // 星期 (0 - 6)
int tm_yday; // 经历天数 (从1月1日计起)
int tm_tm_isdst; // 夏时令 (夏季时间将提前1小时)
};
fprintf(fp, "%d %d %d %d %d",
timer->tm_year + 1900, timer->tm_mon + 1,
timer->tm_day, timer->tm_hour,
timer->tm_min, timer->tm_sec);
fclose(fp);读入/写入一个字符:
1
2
3
4
5
6int fgetc(FILE *stream); // 读入一个字符
int fputc(FILE *stream); // 写入一个字符
// 返回值:
// Ture - 返回所读的字符数
// False - 返回文件结束标记EOF(-1)
用二进制方式向文件读写一组数据:
1
2
3
4
5size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
// 从ptr指向的数组中将最多nmemb个长度为size的元素写入stream指向的流中。
size_t fread(const void *ptr, size_t size, szie_t nmemb, FILE *stream);
// 从stream流中读取nmemb个长度为size的元素写入到ptr数组。
随机读写数据文件
文件位置标记及其定位
- 文件位置标记:文件头、读写当前位置、文件尾。
- 文件位置标记的定位:
fseek(文件类型指针, 位移量, 起始点);
。- 文件开始位置 -> SEEK_SET -> 0
- 文件当前位置 -> SEEK_CUR -> 1
- 文件末尾位置 -> SEEK_END -> 2
随机读写
结合
fseek()
与fread()
函数实现。例如,读取第 1,3,5,7,9 个学生数据并输出。1
2
3
4for(i = 0; i<10; i+=2){
fseek(fp, i*sizeof(struct Student), 0);
fread(&student[i], sizeof(struct Student), 1, fp);
}