笔记 | C 语言复习框架 ( 应试篇 )

序言

从标题中透漏的信息可知,本系列文章是围绕 “C语言” 展开学习的笔记总结,且目的很明确,笔记内容偏应试,适用于计算机等级考试、考研专业课(C语言)等的复习使用。文章推崇总结性、比对性的学习方法,对于模糊的知识模块需自行查阅参考书目,深化理解或可达到理想的效果。

针对 C 语言程序,推荐几本辅导复习的书目:

基础篇

  • 《 谭浩强: C 语言程序设计 》: 必不可少的经典教程,权威性的标准答案源 ( 当然仅限于应试范围 )。( 因讨论条件而异,如编译系统不同,部分题目的答案就具有了争议性 )
  • 《 明解 C 语言 》: 入门基础教学。值得称赞的是,每个知识模块都附有实例,且实例的源码结构清晰,代码规范及注释到位,适合入门使用。

进阶篇

  • 《 征服 C 指针 》: C 语言的学习过程中,指针的运用是最大的难关。无论是在实际应用、应试中都是不可忽视的。对于作者前桥和弥,其一针见血的文风,在掌握一定基础之后,是深入了解 C 语言的一位不可多得 “良师益友” ( 书中有不少作者交谈式的独白,别有一番阅读风味 )。

总览

1 壹 程序设计和 C 语言

1.1 计算机程序与语言

程序:计算机能识别和执行的指令。
语言:人和计算机交流、人和计算机能识别的语言。
计算机语言发展阶段:

机器语言 符号语言 高级语言 ( 面向过程、面向对象 )
0和1指针 英文、数字表示指令 人类自然语言和数字语言

1.2 C语言

  • 特点

    • 语言简洁、紧凑,使用方便、灵活
    • 运算符丰富 ( 单目、双目、三目运算符 )
    • 数据类型丰富 ( 整型、浮点型、字符型、数组类型、指针类型、结构体类型、共用体类型、枚举型 )
    • 结构体控制语句
    • 直接访问物理地址 ( 对硬件直接操作 )
    • 可移植性好
  • 结构

    • 以程序由一个或着多个 源文件 组成。

      源文件中包括:
      预处理命令 ( #include、#define、#typedef 等 )
      全局声明 ( 全局变量、局部变量 )
      函数定义 ( 参考函数原型 )

    • 函数是 C 程序的主要组成部分。

    • 一函数包括 函数首部函数体

      函数体包括:
      声明部分和执行部分。

    • 程序总是从main函数开始执行的。

      main 函数有且仅有一个。

    • C 程序对计算机的操作有 C 语言完成。

    • 数据声明和语句必须有分号 ( 作为结束 )。
    • C 本身不提供输入输出语句。

1.3 程序设计的任务

  1. 问题分析
  2. 设计算法
  3. 编写程序
  4. 对源文件编辑、编译 ( *.obj ) 和连接 ( *.exe )
  5. 运行程序并分析结果
  6. 编写程序文档

    [注] 对于编译,预编译和连接的概念及比对:

    • 编译:检索语言错误;把源程序转为二进制形式的目标程序。
    • 预编译:通过预处理得到的信息与程序其他部分一起,组成完整的、可以正式编译的源程序。
    • 连接:与函数库相连接。

2 贰 程序之魂:算法

2.1 引入

  • 对数据的描述:所用数据的类型和数据的 组织形式

    组织形式:数据结构 — 特定关系的数据元素的集合

  • 对操作的描述:计算机进行操作的步骤 — 算法

  • 从简理解:数据结构 + 算法 = 程序

2.2 算法

  • 概念:对特定问题求解的方法和描述。

  • 特征

    • 有穷性:有穷时间 执行结束;
    • 确定性:算法 唯一执行路径,既相同输入执行相同路径;
    • 可行性:有限次
    • 零或一个以上的 输入
    • 一个或以上的 输出
  • 要求

    • 正确性;
    • 可读性;
    • 健壮性;
    • 效率与低存储量需求 ( 时间复杂度和空间复杂度 )
  • 时间复杂度

    • 时间复杂度:也称渐进时间复杂度,即算法执行时间的增长率和 f(n) 的增长率相同。
    • 渐进时间复杂度:T(n) = Big O(f(n))
    • $f(n)$ 为问题规模n的某个函数。
    • 算法中的基本运算( 最深层循环内的语句 )的频度与T(n)同数量级。
  • 空间复杂度

    • 空间复杂度:算法所需存储空间的量度。
    • 渐进空间复杂度:S(n) = Big O(f(n))
    • 原地工作:额外空间相对输入的数据量来说是常数。

2.3 三种基本结构和改进流程图

  • 三种基本结构
    • 顺序结构;
    • 选择结构;
    • 循环结构:当型循环结构 / 直到型循环结构;
  • 改进的流程图:N-S 流程图

2.4 结构化程序设计方法

  • 自顶向下;
  • 逐步细化;
  • 模块化设计:分而治之注意模块独立性
  • 结构化编码;

3 叁 简单的 C 语言程序设计

3.1 数据的表现形式及运算

3.1.1 常量

  • 概念:程序运行期间,其值不能改变。
  • 类型
    • 整型常量
    • 字符常量 ( 与常变量作比对 [注释1] )
      • 普通字符
      • 转移字符:\n, \t, \012 (8进制), \x41 (16进制)
      • 符号常量:#define PI 3.14159
    • 实型常量
      • 10进制小数形式:3.14L
      • 指数形式(科学计数法):
        8.7e-25(正确);
        8.7e-2.5(错误);
        87e+25(正确);

3.1.2 变量

先定义,后使用

  • 包含属性
    • 数据类型 ( 整型、浮点型、字符型 )
    • 存储类别 ( 自动变量,静态变量 )
  • 概念:程序运行期间,其值可以改变。
  • 类型

    • 常变量:变量存在期间其值不能改变。 const int a = 10
    • 自动变量与静态变量
    • 全局变量与局部变量

      从存储位置、生存周期、作用区域讨论差异性。[注释2]

  • 标识符
    一个对象的名称。除关键字外,字符、数字和下划线组成。且要求只能是字母或下划线开头。

[注释1] 符号常量与常变量的比较。

符号常量 常变量
不占内存单元,预编译后符号不复存在 占存储单元
不能重新赋值 不能改变其值

[注释2] 局部变量与全局变量,自动变量与静态变量,内部函数与外部函数的比较。

局部变量 全局变量
存放于动态存储区 存放于静态存储区 位置
在定义函数内起作用 自定义位置开始,本文件起作用 作用域
函数调用完释放内存 程序结束时释放内存 生存期
  • 静态的局部变量,存放于静态存储区,程序结束时释放内存。
  • 静态的全局变量,不是因声明static,而误解全局变量才存放于静态存储区。
  • 局部变量,声明存储类型指变量存储区以及产生的生存期问题。
  • 全局变量,声明存储类型指变量作用域的扩展问题。
自动变量 静态变量
1. 声明该变量的语句块被执行结束释放内存() 1. 程序结束时才释放内存
2. 每次函数调用时赋值 2. 保留上一步的赋值
3. 在编时赋予初值0或’\0’

[注] 对比malloc()函数分配的内存,需调用free()函数释放内存。()

内部函数 外部函数 (default)
本文件内使用(不限位置) 可供其他文件使用(不限位置)
定义:static 函数类型 函数名 定义:(extern) 函数类型 函数名

3.1.3 数据类型

  • 基本类型
关键字 字节 取值范围
整型 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
    • 多类型变量混合运算,取最高精度的类型;

4 肆 选择结构程序设计

4.1 关系运算符及其优先次序

  • 各类运算符的优先级:

    • 单目运算符 > 双目运算符 (算术、关系、逻辑) > 三目运算符

    • 优先级由高到低排序:
      初等运算符:(),[],->,.
      单目运算符:!,++,--,~
      算术运算符:*,/,%+,-
      关系运算符:>,<,>=,<=!=,==
      逻辑运算符:&&,||
      条件运算符:a > b : a : b
      赋值运算符:a += 1
      逗号运算符:(a,b)

    • 结合方式
      自左向右:初等、单目、关系、逻辑、逗号运算符
      自右向左:条件、赋值运算符

      同一级的运算符,由结合方式决定优先级。

4.2 表达式

  • 算术表达式:先乘除模,后加减,再由“自左向右”原则运算。

  • 混合运算

    • 优先级:遵循各运算符的优先次序。
    • 结合性:算术运算符 (自左向右);赋值运算符 (自右向左)。
    • 不同类型的混合运算:结果的类型为最高精度的数据类型。

4.3 运算符与表达式

  • 关系运算符和关系表达式 ( 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

4.4 选择结构的嵌套

  • if语句只有两个分支可供选择,else总是与它上面最近的未配对的if()配对。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    if(express1){
    if(express2){
    ...
    } else {
    ...
    }
    } else {
    if(express3){
    ...
    } else {
    ...
    }
    }
  • switch语句实现多分支选择结构

    1
    2
    3
    4
    5
    switch(express1){ // 整型、字符型
    case 常量/常量表达式:语句1break; // break为拦截作用
    case 常量/常量表达式:语句2break;
    default: 语句3;
    }

5 伍 循环结构程序设计

5.1 while 语句实现

1
2
3
4
5
express1;
while(express2){
express3;
...
}

5.2 for 语句实现循环

1
2
3
for(express1; express2; express3){
...
}

5.3 do…while() 语句实现循环

1
2
3
4
5
express1;

do{
express3;
} while(express2);

5.4 break、continue与goto语句

  • break:从循环体内跳出循环体。多层嵌套循环,跳出相邻一层循环。
  • continue:提前结束本次循环。
  • goto:跳出多层循环。

6 陆 数组

6.1 概念

  • 一组有序数据的集合。
  • 数组中每一元素同属一个数据类型。
  • sname[0] <=> *(p+0) <=> 第一个数组元素。

6.2 定义

6.2.1 一维数组

  • 定义
    类型符 数组名[常量表达式] —> 正确
    类型符 数组名[变量] —> 错误,不能为变量

  • 初始化

    1
    2
    int array[] = {1, 2, 3, 4, 5};  
    in array[5] = {0}; // 5个元素都为0。
  • 引用

    1
    2
    3
    int *p = &array[0];	// 等同于 int *p = array;
    p++; // 指针运算
    *(p+i); // 取第i位元素

6.2.2 二维数组

  • 定义:类型符 数组名[常量表达式][常量表达式]

  • 初始化

    1
    2
    3
    4
    5
    6
    int 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
    3
    int num = array[1][1];
    int *p = array;
    *(*(p+j)+j); // 等同于array[i][j];

6.2.3 字符数组

  • 定义: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
      20
      char 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
      5
      scanf("%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 的情况。

    • strlwr(字符串) — 将字符串中大写字母转为小写字母

    • strupr(字符串) — 将字符串中小写字母转为大写字母
    • atoi(字符串) — 字符串转int型
    • atol(字符串) — 字符串转long型
    • atof(字符串) — 字符串转double型

      [注]
      1) 字符串处理函数需加入头文件:#include <string.h>.
      2) 需掌握字符串函数自定义方法实现。
      3) 大部份字符串处理函数多数以标记量’\0’为临界点,若字符数组中含两个或或以上,需注意实际的结果。
      4) 引用atoi()、atol()、atof()函数需引用#include <stdlib.h>

7 柒 函数

7.1 为什么要用函数

  • 模块化程序设计:每一函数实现一特定的功能,函数的名称既反映功能。
  • 更好地代码复用:使用库函数;使用自己编写的函数。

    代码复用:减少重复编码程序段的工作量。

    [说明]

    对于所有完成相同功能的组件,应抽象出一个接口,它们都实现该接口。
    具体在Java中,所有完成相同功能的组件都 实现该接口从该抽象类中的继承

7.2 定义函数

建立存储空间的声明

  • 定义函数:函数返回类型 函数名 函数参数 函数体 (变量定义、声明,执行语句)
    • 函数返回类型:基本数据类型 / void型;
    • 函数名:驼峰式命名法;
    • 函数参数:实参、形参.

7.3 函数声明

不需要建立存储空间的声明

  • 函数原型 (Prototype):函数返回类型、函数名、参数类型、参数个数、参数顺序

  • 函数声明的方法

    • 使用函数原型;
    • 同一源文件,在调用该函数的前面定义 (可打包到自定义头文件中).

7.4 函数调用

  • 嵌套调用、递归调用 (直接或间接调用该函数本身)
  • 实参和形参
    • 概念
      实参:常量、变量或表达式、函数 (返回值)
      形参:函数调用期间临时分配内存,值从实参中获得,调用结束后释放内存空间。
    • 实质:值传递、地址传递

8 捌 指针

8.1 指针是什么

  • 指针变量:保存变量地址的变量。
  • 指针类型

    • 指针类型的变量:存放地址
    • 指针类型的值:对应内存地址存放的值

      在 swap(int *a, int *b); 的案例中可以形象说明两者的区别。

8.2 指针移动 (运算:加、减)

对指针加一、减一运算,即地址会增加或减少一单位长度。单位长度具体具体指当前指针所指向数据类型的所占空间大小。

8.3 指针类型

8.3.1 空指针

确保没有指向任何一个对象的指针。通常以宏定义NULL(0)表示空指针的常量值。

关于NULL、0和’\0’,大部分情况都为零。特别地,

1
2
int *p = 0;	// 正确,编译器将指针指向内存地址为0处。 
int *p = 3; // 错误,赋值的数据类型不相符。

8.3.2 指针类型的派生

  • 指向函数的指针

    1
    void (*func(int));
  • 指向数组的指针(多重指针)

    1
    int (*p)[5];

8.3.3 数组类型的派生

  • 指针数组

    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型值的函数的指针

8.4 指针的应用

8.4.1 指针与数组

  • 一维

    1
    2
    3
    p[i]	// 等同于 *(p+i)
    i[p] // 等同于 *(i+p)
    &p[i] // 等同于 (p+i),即第i个元素的地址
  • 二维

    1
    2
    huge[i]		// 等同于 *(huge+i),即第i行的首地址
    *(huge+i)[j] // 等同于 *(*(p+j)+j),即 huge[i][j]

8.4.2 指针与字符串

8.4.2.1 字符指针变量
  • 定义

    1
    2
    3
    4
    5
    6
    7
    8
    char *array = "World";
    array = "hello"; // 改变指向

    char array[] = "Hello";
    array = "World"; // 错误的做法

    char *array = "Hello World";
    array += 6; // 改变指向 (首地址改变)
8.4.2.2 字符数组
  • 定义: int array[] = “Hello”;

  • 使用:printf(“%c”, array[0]);

    字符指针变量的值是不能改变的,即已是字符串常量。

    1
    2
    char *array = "Hello";
    array[0] = 'W';

8.4.3 指针与函数

8.4.3.1 作为参数

即传递的是指向初始元素的指针。

  • 数组名作函数参数
    • 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: 文件名 + 其他参数

  • 字符指针作函数参数

8.4.3.2 作为返回值

返回指针值的函数,即返回的是地址。

[如] 返回的指针指向结构体变量、字符变量等。

9 玖 构造类型:用户自己建立数据结构

9.1 结构体类型

  • 定义

    1
    2
    3
    4
    struct Name {
    int num;
    char word[59];
    } *p, name[5];
  • 初始化:所有成员一起赋值。

  • 使用

    1
    2
    3
    4
    5
    6
    7
    name[i].num;
    p->word[i];
    (*p).num;

    struct Name *tmp;
    tmp = name;
    (tmp++)->num; // 先'++'操作,后'->'操作
  • 大小:成员变量所占内存长度总和。

9.2 共用体类型

  • 定义

    1
    2
    3
    4
    5
    union Name {
    int num;
    double digital;
    char word;
    } *p, name[5];
  • 初始化:只允许给一个成员变量赋值。

    1
    2
    3
    4
    union Name tmp = {10};
    union Name tmp = {.word = 'Y'};
    t.digital = 2.0;
    t.word = 'N'; // 最终的赋值
  • 使用

    1
    2
    3
    name[i].num;
    p->word[i];
    (*p).num;
  • 大小:成员变量所占内存长度最大者。

[注] 关于结构体、共用体类型的内存长度问题,遵循4字节倍数的原则进行内存布局对齐。
[如]
sizeof(struct Name) = 64 (63)
sizeof(union Name) = 4 (4)

9.3 枚举类型

  • 定义

    1
    2
    3
    enum Week {
    sun, mon, tue, wed, thu, fir, sat // 默认参数从0开始
    } week;
  • 初始化

    1
    2
    3
    enum Week {
    mon = 1, tue = 2, wed = 3, thu = 4, fir = 5, sat = 6,sun = 7 // 默认参数从0开始
    } week;
  • 使用:week.mon;

9.4 Typedef 声明新类型名

  • 含义:引入变量别名,而不是另外地给变量分配空间。
  • 使用

    1
    2
    3
    typedef int Integer;
    typedef long Integer; // 若编译器中,int为2字节,满足移值需求可以long型替换。
    Interger num = 1;
  • 与 #define 宏定义的区别

    • #typedef:编译阶段处理
    • #define:预编译阶段处理,实质是字符串替换

10 拾 文件处理

10.1 文件与流

  • stdin — 标准输入流 — 用于读取普通输入的流,在大多数环境中为键盘输入。scanf()与getchar()等函数会从这个流中读取字符。

  • stdout - 标准输入流 — 用于写入普通输入的流,在大多数环境中为输出至显示器界面。printf()、puts()与putchar()等函数会向这个流写入字符。

  • stderr — 标准错误流 — 用于写出错误的流,在大多数环境中为输出至显示器界面。

10.2 文件分类

  • ASCII文件 (文本文件):每一字节存放一字符的ASCII代码。
  • 二进制文件

    • 优:节约存储空间
    • 劣:精度有限

      [如] 整数10000
      ASCII形式存储空间为 5 字节
      二进制形式存储空间为 4 字节

10.3 文件类型指针:FILE型

  • 需引用 #include <stdio.h>

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

10.4 打开文件

  • 原型: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+
读和写(打开文件);读和写(建立文件;文件存在,则长度清零);读和写(打开文件) 读和写(打开文件;文件存在,则长度清零);读和写(建立文件);读和写(打开文件)

10.5 关闭文件

  • 原型:int fclose(FILE *stream);
    • 返回值 ( True:0;False:EOF(-1) )
    • 数据存储的过程:数据 —> 缓存区 (充满) —> 文件
    • 若不关闭文件,将会造成数据丢失。
    • 若突然关闭文件,缓存区传输到文件的过程给中断,造成数据丢失。

10.6 顺序读写数据文件

  • 格式化读取文件

    1
    2
    3
    4
    5
    6
    7
    int 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
    28
    int fprintf(FILE *stream, const char *format, ...);
    // 返回值:
    // Ture - 返回发送的字符数
    // False - 返回文件结束标记EOF(-1)

    // 使用实例:获得当前运行时间,并存入文本中
    time_t current = time(NULL);
    struct tm *timer = Localtime(&current);

    // 将日历时间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
    6
    int fgetc(FILE *stream);	// 读入一个字符

    int fputc(FILE *stream); // 写入一个字符
    // 返回值:
    // Ture - 返回所读的字符数
    // False - 返回文件结束标记EOF(-1)
  • 用二进制方式向文件读写一组数据

    1
    2
    3
    4
    5
    size_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数组。

10.7 随机读写数据文件

10.7.1 文件位置标记及其定位

  • 文件位置标记:文件头、读写当前位置、文件尾

  • 文件位置标记的定位:fseek(文件类型指针, 位移量, 起始点);

    • 文件开始位置 -> SEEK_SET -> 0
    • 文件当前位置 -> SEEK_CUR -> 1
    • 文件末尾位置 -> SEEK_END -> 2

10.7.2 随机读写

结合fseek()与fread()函数实现。

[如] 读取第1,3,5,7,9个学生数据并输出。

1
2
3
4
for(i = 0; i<10; i+=2){
fseek(fp, i*sizeof(struct Student), 0);
fread(&student[i], sizeof(struct Student), 1, fp);
}