谭浩强C程序设计(第五版)知识点整理

文章目录

一、程序设计和C语言

1.5 运行C程序的步骤与方法

  • 编辑/输入
  • 预处理(预编译)-组成完整源程序/编译-判定语法错误、转换为二进制目标程序
  • 连接-连接所有目标文件及函数库生成可执行文件

二、算法

2.5 怎样表示一个算法

自然语言、流程图、三种基本结构流程图、N-S流程图、伪代码、编程语言

三、顺序程序设计

3.2数据的表现形式及其运算

  • 指数:12.34e3(e/E之前必须有数字,之后必须为整数)
  • 转义字符
  • 符号常量:含义清楚、一改全改;不占内存,代表一个值的符号,编译后即不存在
    #define PI 3.1416
  • 常变量:关键字const
  • 整型/浮点型数据存储空间及值的范围
  • C程序中的浮点型常量都作为double常量,赋值给其他类型需要转换
    常量末尾加专用字符,强制指定常量类型:
    float a=3.14159f;
    long double a=1.23L;

3.4 C语句

  • 赋值表达式中的“表达式”又可作为一个赋值表达式(自右向左结合)
    a=(b=5) 相当于 a=b=5
    b=5;a=b;
    作为表达式出现在其他语句中:
    printf("%d",a=b) 赋值+输出
    if((a=b)>0) ;
    赋值表达式末尾无分号;赋值语句末尾有分号

3.5 数据的输入输出

  • 输入输出语句为库函数提供,C语言本身不提供
    #include <stdio.h>
  • #include 尖括号是从C编译系统子目录里调用文件;双引号先从用户当前目录寻找,再按标准方式
标准输入输出函数

printf(格式控制,输出表列)
printf("%d,%c\n",i,c);

  • 函数参数:
    格式控制字符串-“%d”,包括:格式声明-%+格式字符;普通字符(输出时原样输出)(可包含转义字符)
    输出表列-输出数据(常量、变量、表达式)
  • 域宽 如 "%5d"
  • 格式字符
    • 整数:%d %ld %lld
    • 一个字符:%c
    • 一个字符串:%s
    • 浮点数:%f(%m.nf-指定数据宽度和小数位数,宽度不足时在前面补空格,同格式声明的多行输出按小数点对齐;%-m.nf-输出数据向左对齐,宽度不足时在后面补上空格;%08.3f, 表示最小宽度为8,保留3位小数,当宽度不足时在前面补上0)
      (小数下一位按四舍五入处理)
    • 指数形式浮点数:%e / %m.ne(不指定宽度及小数位数,自动给出数字部分小数位数为6位,指数部分占5列),如:
      printf("%e",123.456); 输出:
      1.234560e+002 (用%E则输出为E)
    • 其他:
      整型数据实际长度输出:%e(同%d)
      八进制整数形式输出:%o 得到存储单元实际的存储情况
      十六进制整数形式:%x/X %lx
      无符号型十进制整数:%u
      自动选择长度较短的f/e:%g/G

scanf(格式控制,地址表列)
scanf("a=%f,b=%f",&a,&b);
有其他字符时,输入数据时要在对应位置上输入相同字符,保持形式一致

putchar© 输出一个字符
可在屏幕显示的字符及屏幕控制字符(如’\n’)(包括转义字符)

getchar() 输入一个字符
可以获得屏幕上无法显示的字符(如控制字符)
可在printf中输出 printf("%c",getchar());

四、选择结构程序设计

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

  • 关系运算符(低于算术运算符、高于赋值运算符)
    高:<、<=、>、>=
    低:==、!=
    自左向右结合
    表达式的值,真=1,假=0

4.4 逻辑运算符和逻辑表达式

  • 逻辑运算符
    !(非)->&&(与)->||(或)
    表达式的值,真=1,假=0
  • 逻辑表达式
    只有在必须执行下一个逻辑运算符才能求出表达式的解时,才执行该运算符

4.5 条件运算符和条件表达式

  • 条件表达式 - 条件运算符?和: (三目运算符)

    max=(a>b)?a:b 条件运算符优先于赋值运算符

4.6 选择结构的嵌套

else总是与它上面最近的未配对的if配对

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

switch(表达式){
    case 常量1 : 语句1
    case 常量2 : 语句2
    ...
    case 常量n : 语句n
    default : 语句n+1
}
  • case+常量和default作为标号,来标志一个位置
  • case出现次序不影响执行结果
  • 执行时从匹配的入口标号开始执行下去,不再判断
  • 利用break语句跳出语句
  • 多个case标号可以共用一组执行语句

五、循环结构程序设计

5.4 用for语句实现循环

for(表达式1;表达式2;表达式3) 语句 改写为while循环:

while 表达式2{
  语句
  表达式3
}
  • 表达式1和表达式3可以是逗号表达式
    for(sum=0,i=1;i<=100;i++) sum=sum+i;
    逗号表达式内按自左至右顺序求解,整个逗号表达式的值为最右表达式的值
  • 表达式2一般为关系表达式/逻辑表达式,也可以是数值表达式/字符表达式(非0即执行循环体)
  • [补]for循环结构含义
    for(初始化变量;循环条件;循环迭代){
      循环语句;
    }
    

5.8 循环举例

#include <math.h>
abs(x)-求整数x绝对值,结果为整型
fabs(x)-x为双精度型

  • 近似值
  • 斐波那契
  • 判定素数
[补]
  • e的n次方
    exp(n)
  • 求对数
    • 以e为底
      double log(double n);
    • 以10为底
      double log10(double n);
    • 其他 by换底公式
  • 求指数 x的y次方
    double pow(double x, double y);
  • 求平方根
    double sqrt(double n);
  • 向下取整
    double floor(double n);
  • 向上取整
    double ceil(double n);
  • max/min
  • 三角函数

[补]跳出循环

break

跳出一层(内)循环

continue

跳过剩余循环体语句,进入下一次循环

标志变量

初始化 改变值 判断

goto

不推荐使用

goto label;
...
label: ...;

六、利用数组处理批量数据

6.1 定义和引用一维数组

  • 类型符 数组名[常量表达式]
    常量表达式包括常量和符号常量,不能是变量
  • 初始化:通过初始化列表
    提供部分初值,即只给前面几个元素赋初值,系统自动给后面元素赋初值为0
    int a[10]={0}; 全部元素值为0
    字符型数组则初始化为'\0',指针型数组初始化为NULL
  • 根据花括号中数据个数确定长度
    int a[]={1,2,3};

6.2 定义和引用二维数组

  • 类型说明符 数组名[常量表达式][常量表达式]
  • 二维数组可被看作一种特殊的一维数组,如
    float a[2][3];
    则a[0]、a[1]可看作一维数组名
  • C语言中二维数组中元素排列顺序是按行存放的
  • 在内存中,各元素是连续存放的,不是二维而是线性的
  • 初始化:初始化列表
    int a[2][3]={{1,2,3},{4,5,6}};
    int a[2][3]={1,2,3,4,5,6};
    int a[2][3]={{1},{4}}; //部分赋值
    int a[2][3]={{1},{0,0,6}}; //对各行中某一元素赋值,非0元素少时比较方便
    
  • 对全部元素赋初值时,对第1维的长度可以不指定,系统会根据总个数和第2维长度计算第1维长度
    int a[][3]={1,2,3,4,5,6};

6.3 字符数组

C语言中没有字符串类型和字符串变量,字符串是放在字符型数组中的

  • 以整数形式(ASCII码)存放,可用整型数组来存放,但浪费空间
  • 若定义字符数组时不进行初始化,数组中各元素是不可预料的
    char c[]={'a','b','c'}; 长度自动定为3
  • 字符串结束标志
    C系统在用字符数组存储字符串常量时会自动加一个’\0’(ASCII码为0)作为结束符
  • 用字符串常量初始化字符数组
    char c[]="abc";
    此时数组长度为4,因为字符串常量最后由系统加上一个’\0’
    故为了使处理方法一致,便于测定字符串的实际长度,在程序中初始化字符数组中常人为加上一个’0’
    char c[]={'a','b','c','\0'};
*字符串处理函数

#include <string.h>

  • puts(字符数组)-输出字符串
    以’\0’结束的字符序列,可以包括转义字符
    用puts输出时将’\0’转换成’\n’,即输出完字符串后换行
  • gets(字符数组)-输入字符串
    送给数组字符数为输入字符串长度+1(末尾添加’\0’)
    返回的函数值为字符数组的第一个元素的地址
  • strcat(字符数组1,字符数组2)-连接字符串
    返回字符数组1的地址
    字符数组1必须足够大以容纳连接后的新字符串
    连接时将字符串1后的’\0’取消
  • strcpy(字符数组1,字符串2)-字符串复制
    字符串2可以是字符数组名/字符串常量
    不能用赋值语句把字符串常量/字符数组给一个字符数组,只能将一个字符赋给一个字符型变量/字符数组元素
    strncpy(字符数组1,字符串2,n)-复制串2前n个字符
  • strcmp(字符串1,字符串2)-字符串比较
  • strlen(字符数组)-字符串长度
    不包括’\0’
  • strlwr(字符串)-转换为小写
  • strupr(字符串)-转换为大写

七、用函数实现模块化程序设计

7.4 对被调用函数的声明和函数原型

  • 函数的首行(即函数首部)称为函数原型,再加一个分号,即为函数的声明
  • 函数声明中的形参名可以省写,只写形参的类型

7.6 函数的递归调用

回溯+递推

  • 汉诺塔问题

7.7 数组作为函数参数

  • 用数组名作为函数参数时,向形参(数组名/指针变量)传递的是数组首元素的地址
  • C语言编译系统并不检查形参数组大小
    float average(float array[])
    在对源程序编译时,编译系统把形参数组处理为指针变量,即转换为:
    float *array
    二者均可作为形参,且二者等价
    对数组元素的访问,用下标法和指针法同样等价
  • 形参数组中各元素的值改变会使实参数组元素的值同时发生变化
    多维数组名作函数参数
  • 对形参数组定义时可以省略第一维的大小说明
    int array [][10]
    不能把第2维及其他高维的大小说明省略(按行存放要指定列数)

7.8 局部变量和全局变量

变量的作用域(空间)

  • 全局变量有效范围为从定义变量的位置开始到本源文件结束
  • 利用全局变量增加函数间的联系渠道,通过函数调用就得到一个以上的值;但使函数的通用性降低了
  • 习惯将全局变量名的首字母大写
  • 在局部变量的作用范围内,同名全局变量被屏蔽

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

变量存在的时间(生存期)

  • 内存供用户使用的存储空间(用户区):
    程序区、静态存储区、动态存储区
  • 静态存储方式:程序运行期间由系统分配固定的存储空间的方式
    动态存储方式:程序运行期间根据需要进行动态的分配存储空间的方式
  • 全局变量全部存放在静态存储区中,程序执行过程中占据固定的存储单元,不是动态地进行分配和释放
  • 动态存储区:堆和栈、局部自动变量(存放在栈区)、自动变量(未加static声明的局部变量)、函数调用时的现场保护和返回地址等、动态变量
  • 栈:存放函数的返回地址、参数和局部变量
    堆:通过new运算符和malloc函数分配得到的空间
局部变量的存储类别

每个变量和函数都有两个属性:数据类型和数据的存储类别(指定变量存储区域及生存期问题)

  • 自动变量(auto变量)
    函数中的形参和在函数中定义的局部变量(包括在复合语句中定义的局部变量)
    对自动变量赋初值不是在编译时进行,而是在函数调用时进行的
  • 静态局部变量(static局部变量)
    函数调用结束后不消失而继续保留原值,即占用的存储空间在程序整个运行期间都不释放,
    但其他函数不能引用它
    编译时赋初值,即只赋初值一次(不赋初值则编译时自动赋初值0-数值型变量/‘\0’-字符变量)
  • 寄存器变量(register变量)
    对于使用频繁的局部变量,为提高执行效率,将其放在CPU中的寄存器中
    register int f;
    编译系统自动识别并将其放在寄存器中
全局变量的存储类别

扩展作用域

  • 在一个文件内扩展
    定义点之前的函数需要引用,则在引用之前用关键字extern对该变量作“外部变量声明”,表示把作用域扩展到此位置
    int main(){
      extern int A; //类型名可以省略
      ...
    }
    int A;
    
  • 扩展到其他文件
    在任一个文件定义外部变量后,在另一个文件中用extern作“外部变量声明”
    在编译和连接时系统会知道该变量有“外部链接”,进行作用域扩展
  • 限制在本文件中
    只限于被本文件引用,不能被其他文件引用,在定义外部变量时用static声明,即静态外部变量
    便于程序的模块化、通用性

7.10 关于变量的声明和定义

  • 是否建立存储空间区分
    定义性声明(定义): int a;
    引用性声明:extern a;
    (广义上)声明+定义
  • 狭义上,把建立存储空间的声明成为定义,把不需要建立存储空间的声明成为声明

7.11 内部函数和外部函数

如果不加声明,一个文件中的函数即可被本文件其他函数调用,也可以被其他文件中的函数调用

  • 内部函数(静态函数)
    static 类型名 函数名(形参表);
    一般放在文件开头
  • 外部函数
    extern int fun(int a,int b);
    省略extern默认为外部函数
    在其他文件调用时需作声明,加extern(也可以省略),即函数原型
  • 利用函数原型扩展函数作用域,最常见的例子是#include指令的应用

八、指针

8.1 指针是什么

  • 指向变量单元的地址
  • C语言的地址包括:位置信息(内存编号,或称纯地址)和它所指向的数据的类型信息
  • 直接访问:直接按变量名进行的访问
    间接访问:将变量的地址存放在另一变量中,通过该变量来找到原变量的地址从而进行访问
  • 指针变量:地址变量,专门用来存放另一变量的地址(即指针)

8.2 指针变量

  • 定义指针变量
    类型名 * 指针变量名;
  • int a,*p; p=&a;
    &a不仅包含变量a的位置,还包括“存储的数据是整型”
    float *p; p=&a;
    编译会出现警告(warning),赋值时系统会把&a的基类型自动改换为float型赋给p,但是p不能用这个地址指向整型变量
  • 指针类型
    指向整型数据的指针类型:int*
  • 在程序中不能用一个数值代表地址,只能用地址符&得到并赋给一个指针变量
  • & 取地址运算符
    * 指针运算符(间接访问运算符)
  • 不能通过执行调用函数来改变实参指针变量的值,但是可以改变实参指针变量所指变量的值

8.3 通过指针引用数组

  • 数组名代表数组中首元素地址,是一个指针型常量
    int a[3]={1,2,3};
    int *p;
    p=&a[0];
    p=a; //二者等价
    
  • [] 变址运算符
  • 引用数组元素可用下标法,也可用指针法(能使目标程序质量高-占内存少,运行速度快)
  • 指针已指向一个数组元素,可使用运算:+/+=、-/-=、++、–、指针相减(指向同一数组中元素,结果是地址之差除以数组元素的长度,即相对距离)
    p+1的1指的是一个数组元素所占用的字节数
    地址不能相加
  • a[i]:*(p+i)*(a+i)
    在编译时对数组元素a[i]就是按*(a+i)处理的
  • 用指针变量直接指向元素,不必每次重新计算地址;有规律地改变地址值(p++)能大大提高执行效率
    for(p=a;p<(a+3);p++)
      printf("%d",*p);
    
  • 指向数组元素的指针也可以带下标,如p[i],编译处理成*(p+i)
    不过必须弄清楚p当前值,容易出错
  • *p++:++ 和 *同优先级,自右向左结合,即 *(p++)
通过指针引用多维数组

int a[2][3]={{1,2,3},{4,5,6}};
a是二维数组名,每一个行元素又是一个一维数组

  • a[i] or *(a+1) 是一维数组名,值为 &a[i][0]
  • 第i行一维数组元素的地址 a[i]+j or *(a+i)+j,均为 &a[i][j]
    *(a[i]+j) or *(*(a+i)+j)a[i][j] 的值
  • 用指针变量来指向一维数组
    int (*pt)[3];
    pt的类型为 int(*)[3]型
  • a和a[0]的值虽然相同,但由于指针的基类型不同,a指向一维数组a[0],而a[0]指向列元素a[0][0]
    但并不存在a[i]这样一个实际的数据存储单元,只是一种地址的计算方式
  • 在指向行的指针前面加一个*,就转换成指向列的指针
    反之,在指向列的指针前面加&,就成为指向行的指针

8.4 通过指针引用字符串

  • 用字符指针变量指向一个字符串常量
    char *string="abc";
    基类型为字符型
    字符串常量中的内容是不可取代的(不能再赋值) 如:stirng[1]='r';
  • 为存放字符串常量开辟的字符数组是没有名字的,只能通过指针变量来引用
  • 可以对指针变量进行再赋值,但不能对数组名赋值
  • 输出字符串
    printf("%s\n",string);
    系统会输出string所指向的字符串第1个字符,然后自动使string加1,继续输出,知道遇到’\0’
  • 可变格式输出函数 (printf)
    char *format;
    format="a=%d,b=%f\n";
    printf(format,a,b);
    
    也可以用字符数组实现,但只能在定义数组时初始化或逐个对元素赋值的方法进行赋值

8.5 指向函数的指针

  • 函数名就是函数的指针,代表函数的起始地址
    类型名 ( * 指针变量名)(函数参数表列);
    int max(int,int);
    int (*p)(int,int);
    p=max; //只须给出函数名而不必给出参数
    c=(*p)(a,b); //调用
    
  • *p两侧的括号不可省略
  • 只能指向函数的入口处而不可能指向函数中间的某一条指令;不能进行算术运算
  • 只能指向在定义时指定了类型的函数
  • 通过指针变量调用函数比较灵活,可以根据不同情况先后调用不同的函数
  • 指向函数的指针可以作为函数参数,把函数的入口地址传递给形参,就可以在被调函数中使用实参函数;适用于调用函数不固定的情况
    作为实参的函数应在主调函数中用函数原型作函数声明

8.6 返回指针值的函数

  • 定义函数原型
    类型名 * 函数名(参数表列);

8.7 指针数组和多重指针

  • 定义一维指针数组
    类型名 * 数组名[数组长度];
    类型名中包括“*”
    char *name[]={"abc","def"};
  • 适合用来指向若干个字符串,便于处理字符串;用二维数组来存放需按最长字符串来定义列数,浪费内存空间
    如对字符串排序,不必改动字符串的位置,只须改动指针数组中各元素的指向
  • 指向指针数据的指针变量,即指向指针的指针
    定义: char **p;
    可以分为char * 和 (*p),后者表示p是指针变量,前者表示p指向的是char *型的数据
    如输出字符指针数组指向的字符串:
    p=name+1; printf("%d\n",*p);
  • 单级间址:在一个指针变量中存放一个目标变量的地址
    二级间址:指向指针数据的指针
    多重指针
指针数组作main函数的形参
  • int main(int argc,char *argv[])
    命令行参数:argc-参数个数,argv-*char指针数组(数组中每个元素指向命令行中的一个字符串的首字符)
  • main函数由操作系统调用,实参只能由操作系统给出(和执行文件的命令一起)
  • 命令行的一般形式:
    命令名 参数1 参数2 … 参数n
    命令名是可执行文件名(文件包含main函数);文件名也作为一个参数
  • echo命令-实现参数回送

8.8 动态内存分配与指向它的指针变量

内存动态分配区域-堆区,只能通过指针来引用
头文件 stdlib.h

  • malloc函数
    void *malloc(unsighed int size);
    返回值是所分配区域的第一个字节的地址
    指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址;若未能成功执行,返回NULL
  • calloc函数
    void *calloc(unsighed n,unsighed size);
    分配n个长度为size的连续空间,足以保存一个数组
    为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size,即动态数组
  • realloc函数-重新分配
    void *realloc(void *p,unsighed int size);
    将p所指向的动态空间的大小改变为size,p的值不变;重分配失败返回NULL
  • free函数-释放动态存储区
    void free(void *p);

void指针类型(空类型指针)

  • 指向空类型/不指向确定的类型
    将它的值赋给另一指针变量时由系统对它进行类型转换,使之适合于被赋值的变量的类型,如:
    int *pt; pt=(int *)malloc(100);
    可以简化为 pt=malloc(100); 编译时系统可以自动进行隐式转换
    在存放数据前就进行类型转换

九、自建数据类型

9.1 定义和使用结构体变量

  • 声明:struct 结构体名{成员表列};
    定义:类型名 成员名;
  • 先声明再定义
    声明类型的同时定义变量:
    struct 结构体名 {成员表列}变量名表列;
    不指定类型名而直接定义变量:
    struct {成员表列}变量名表列;
  • 定义时初始化
    对某一成员初始化:
    struct Student b={.name="ZF"};

    成员名前有成员运算符".",优先级最高
    其他未被指定初始化:数值型成员-0/字符型成员-‘\0’/指针型成员-NULL
  • 同类结构体变量可以相互赋值
  • 结构体变量的地址主要用作函数参数

9.2 使用结构体数组

struct Person{
  char name[20];
  int count;
}leader[2]={"Li",0,"Zhang",0};

9.3 结构体指针

struct Student stu;
struct Student *p;
p=&stu;
  • (* p)表示p指向的结构体变量,(*p).num是p所指向的结构体变量的成员
  • *p两侧的括号不可省
  • 可把(*p).nump->num代替
    "->"称为指向运算符

9.5 共用体类型

又称联合
用同一段内存单元存放不同类型的变量

  • 定义:
    union 共用体名{成员表列}变量表列;
  • 共用体变量所占的内存长度等于最长的成员的长度
  • 不能引用共用体变量,只能引用共用体变量中的成员
  • 共用体变量中只能存放一个值,初始化表中只能有一个常量;起作用的成员是最后一次被赋值的成员
  • 同类型的共用体变量可以互相赋值
  • 在数据处理中,用同一个栏目来表示不同内容的情况

9.6 使用枚举类型

一个变量只有几种可能的值

  • 声明枚举类型用enum开头,如:
    enum Weekday{sun,mon,tue,wed,thu,fri,sat};
    enum {sun,mon,tue,wed,thu,fri,sat} weekend; 直接定义
  • 定义变量称为枚举变量,花括号内的值称为枚举元素/枚举常量
  • 不能对枚举元素赋值
    每一个枚举元素都代表一个整数,编译按定义时的顺序默认它们的值为0,1,2…
    枚举常量可以引用和输出
    人为指定枚举元素的数值,在定义枚举类型时显式地指定,如:
    enum Weekday{sun=7,mon=1,tue,wed,thu,fri,sat}; 以后顺序+1

9.7 用typedef声明新类型名

  • 命名一个简单的类型名代替复杂的类型表示方法(即原有类型的typedef名称)
    typedef struct{
      int month;
      int day;
      int year;
    }Date;
    
  • 代表数组类型
    typedef int Num[100]; //声明Num为整型数组类型名
    Num a,b; //定义a,b为整型数组名
    
    利用数组类型可以定义多个数组变量
  • 代表指针类型
    typedef char *String;
    String p,s[10];
    
  • 代表指向函数的指针类型
    typedef int (*Pointer)();
    Pointer p1,p2;
    
  • 总结:按定义变量的方式,把变量名换上新类型名,并在最前面加typedef,就声明了新类型名代表原类型
  • 习惯新类型名首字母大写
  • #define区别
    #define是在预编译时处理的,只能作简单的字符串替换;typedef是在编译阶段处理的

十、文件的输入输出

10.1 C文件的有关基本知识

  • 程序文件:源程序文件(.c)、目标文件(.obj)、可执行文件(.exe)
    数据文件
  • 将输入输出称为流,即数据流
    C语言把文件看作一个字符(或字节)的序列,一个输入输出流就是一个字符流或字节(内容为二进制数据)流;不考虑行的界限,两行数据间不会自动加分隔符,对文件的存取是以字符(字节)为单位的
    输入输出数据流的始末仅受程序控制而不受物理符号(如回车换行符)控制
    这种文件称为流式文件
  • 文件标识(文件名):文件路径、文件名主干、文件后缀
  • 根据数据的组织形式,数据文件可分为:
    ASCII文件:在外存上以ASCII代码形式存储,需要在存储前进行转换,每一个字节存放一个字符的ASCII代码。又称文本文件
    二进制文件:数据在内存中以二进制形式存储的,如果不加转换地输出到外存,就是二进制文件,即存储在内存的数据的映像,称为映像文件
    用ASCII码形式一般占存储空间较多,且要花费转换时间;用二进制形式可以节省外存空间和转换时间
  • 缓冲文件系统:系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区,装满后才一起送到磁盘
    节省存取时间,提高效率
  • 文件类型指针(文件指针)
    每个被使用的文件都在内存中开辟一个相应的文件信息区,来存放文件的有关信息
    这些信息保存在一个结构体变量中,该结构体类型由系统声明,取名为FILE(包含在"stdio.h"中),在程序中可以直接使用FILE类型名定义变量
    这些信息在打开一个文件时由系统根据文件的情况自动放入,在读写文件时需要用到这些信息,并可能修改
    一般不定义FILE类型的变量命名,而设置一个指向FILE类型变量的指针变量,来引用这些FILE类型变量
    FILE *fp; 指向文件的指针变量

10.2 打开与关闭文件

打开文件时,一般都指定一个指针变量指向该文件;关闭是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件

  • fopen函数-打开数据文件
    fopen(文件名,使用文件方式);
    返回值是指向文件的指针(即文件信息区的起始位置),通常将其赋给一个指向文件的指针变量
    FILE *fp; fp=fopen("a1","r");
    如果不能打开,fopen函数返回NULL
  • 常用下面方法打开一个文件:
    if((fp=fopen("file1","r"))==NULL){
      printf("cannot open this file\n");
      exit(0); //关闭所有文件,终止程序(stdlib.h)
    }
    
    即先检查打开文件操作是否出错
  • 使用文件方式
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XAXuFpp5-1692346702726)(/otherpictures/QQ截图20220908104329.png)]
    • w方式写数据,如果原来已存在一个以该文件名命名的文件,则在打开文件前先将该文件删去,然后重新建立一个新文件
    • a方式打开文件时,文件读写位置标记移到文件末尾
    • b表示二进制方式;带b和不带b只有一个区别,即对换行的处理
      windows系统中为实现换行必须要用回车(‘\r’)和换行(‘\n’)两个字符;如用w方式打开,向文件输出时,遇到’\n’时系统把它转换为’\r’和’\n’两个字符;r方式打开,从文件读入时,遇到’\r’和’\n’两个连续的字符,就转回为’\n’一个字符
      加b表示使用的是二进制文件,系统就不进行转换
    • 如用wb方式,并不意味着在文件输出时把内存中按ASCII形式保存的数据自动转换成二进制形式存储
      输出的数据形式是由程序中采用什么读写语句决定的
  • 程序开始运行时系统自动打开这3个标准流文件:标准输入流(stdin)、标准输出流(stdout)、标准出错输出流(stderr)
    可通过这3个指针变量对流进行操作,它们都以终端作为输入输出对象
  • fclose函数-关闭数据文件
    fclose(文件指针);
    不关闭文件就结束程序运行将会丢失数据;用fclose函数关闭文件时,先把缓冲区中的数据输出到磁盘文件,然后才撤销文件信息区
    成功关闭,返回值为0,否则返回EOF(-1)

10.3 顺序读写数据文件

  • 读写一个字符的函数
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uSLnQm0s-1692346702727)(/otherpictures/QQ截图20220908112519.png)]
    在stdio.h中把fputc和fgetc函数定义为宏名putc和getc

    • 用scanf函数输入文件名时最后加了一个回车,保留在缓冲区中,应用getchar函数消化
    • 文件的所有有效字符后有一个文件尾标志,用标识符EOF表示(在stdio.h中被定义为-1)
    • feof函数可以检测文件尾标志是否已被读取过,若已被读出,则表示文件已结束,此时feof函数值为真(1)
  • 读写一个字符串的函数
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9fZCYWdp-1692346702728)(/otherpictures/QQ截图20220908115017.png)]
    char *fgets(char *str,int n,FILE *fp);

    • fgets在最后加一个’\0’
    • 在读完n-1个字符之前遇到’\n’或文件结束符EOF,读入即结束,但将’\n’也作为一个字符读入
    • fgets成功返回str数组首元素的地址;若一开始就遇到文件尾或读数据出错,则返回NULL
      int fputs(char *str,FILE *fp);
    • 向磁盘文件写数据时,只输出字符串中的有效字符,字符串末尾的’\0’不输出,会使前后两次输出的字符串之间无分隔
      为避免此情况,在输出一个字符串后,人为地输出一个’\n’,作为字符串之间的分隔 fputs("\n",fp);
    • 若输出成功,返回0;失败,返回EOF(-1)
  • 在字符串或字符中要表示’‘时,应在其之前再加一个’'(转义字符),如文件路径中

  • 格式化方式读写文本文件
    printf(文件指针,格式字符串,输出表列);
    fscanf(文件指针,格式字符串,输入表列)
    内存与磁盘频繁交换数据的情况下,最好不用这两个函数,而用下面两个函数进行二进制的读写
    fread(buffer,size,count,fp);
    fwrite(buffer,size,count,fp);
    buffer-存放从文件读入的数据的地址;size-读写字节数;count-读写多少个数据项(每个数据项长度为size);fp-FILE类型指针

10.4 随机读写数据文件

对任何位置上的数据进行访问

  • 对流式文件既可以进行顺序读写,也可以进行随机读写,关键在于控制文件的位置标记
  • 用rewind函数使文件位置标记指向文件开头
    此函数没有返回值
  • fseek函数-改变文件位置标记
    fseek(文件类型指针,位移量,起始点);
    位移量以起始点为基点,向前移动的字节数;位移量是long型数据(数字末尾+L)
    一般用于二进制文件
  • ftell函数-测定文件位置标记的当前位置
    用相对于文件开头的位移量来表示
    若出错,返回值为-1L

10.5 文件读写的出错检测

  • ferror函数
    ferror(fp);
    返回值为0(假),表示为出错;返回非零值,表示出错
    对同一个文件每次调用输入输出函数都会产生一个新的ferror函数值,所以应在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失
    执行fopen函数时,ferror初始值自动置位0
  • clearerr函数
    使文件出错标志和文件结束标志置位0
    调用输出输出函数出错后ferror为非零值,应立即调用clearerr(fp)
    只要出现文件读写出错标志,就一直保留,知道对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春风锤呀锤

碗在这 ,光光的T.T

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值