c语言程序设计——数组、指针、预处理、文件——软协第三周

指针、数组、指针与数组、字符数组、预处理之条件编译和文件包含、位运算、文件

一、指针

指针是c语言广泛使用的一种数据类型,它极大地丰富了c语言的功能,运用指针编程是c语言最重要的风格之一,利用指针变量可以表示各种数据结构,可以更加方便的使用数组和字符串,并能像汇编语言一样直接处理内存地址,从而编写出精炼而高效的程序。

①地址与指针:

计算中所有的数据都是存放在存储器中的,我们对计算机的操作,实际就是不断地对存储器中存放的数据进行读取或写入的操作,那么读取或写入的前提就是首先可以找到这些数据,如何才能准确找到存放的数据呢,我们可以使用变量名直接访问这些数据,那么我们还可以为数据编上一个号码,即进行编址,地址前的0x是16进制数据的前缀,即这个编码是用16进制的数据表示的,(是因为现在的数据内存都比较大,作为编号的数字也就非常大),有了这个编号后,我们就可以根据这个内存单元的编号准确找到该内在单元里的数据,这个内存单元编号就被称为地址,这个地址我们就把他叫做指针

②间接访问

我们定义一个变量并且赋值的过程就是在变量名对应的存储单元放入该变量一个对应类型的值。

在c语言中,变量的地址是由编译系统分配的,用户不知道变量的具体地址,所以提供地址运算符&来表示变量的地址,一般形式为:&a(前提是a必须有定义说明(可不必有赋值))

需要说明的是,由于int当在某些系统占两个字节时,系统分配了两个字节单元,取的地址仅仅是两个字节单元的首地址值,那我们该怎么使用这个地址值间接的访问数据呢?

首先我们也找一个变量来存储这个地址值,如变量p来存储,即p=&x;既然用到了这个变量,就应该定义这个变量,变量名是p,这个变量用来存储一个地址,地址显然和我们之前讲的数据类型不同,为了区分,在变量名前面加了一个标识符*,另外还需要类型说明符,即这样的变量应开辟多大的空间呢,仔细思考后我们知道,p的数据长度实际是固定不变的,即四个字节,那是不是既不需要类型说明符了?由于这个变量p存储的是x的地址(首地址),但我们需要通过p去间接的找到完整的x的值,就必须说明从起始地址向下要找几个字节,即变量x的类型,它决定了向下的偏移量,因此我们间接要访问的数据类型作为指针p的基类型,它的作用就是为了告诉机器从起始地址应该向下找多少个字节的数据

那么c语言怎么通过p去访问存储值呢?我么提供了一个指向运算符*,即*p

③指针变量的相关运算

1、指针变量加减一个整数n,将指针指向的当前位置向前或向后移动n个偏移量(n*sizeof(基类型))

2、两个指针变量相减所得之差,是两个指针所指变量之间相差的地址单元,实际上是两个地址值相减之差再除以该基本类型的长度(字节数)在这里插入图片描述
表示两个指针之间相差了多少个相应数据类型的单元

两个指针变量你能不能进行相加运算呢?不能,因为没有实际意义。

3、两个指针变量的关系运算
如:p1==p2表示二者指向同一个变量
p1>p2表示p1处于高地址位置,p1<p2,表示p1处于低地址位置。

4、指针可以作为函数的参数来达到引用传递的效果

传递方式解释:

值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。

引用传递:(形式参数类型是引用数据类型参数):也称为传地址。方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。

二、数组:

取值运算,线性方程组求解,海量数据的查找和排序,图形图像的计算机表示和各种处理 压缩 增强 和复原等为了处理方便,c语言提供了一种构造类型的数据:数组,它可以把相同类型的多个数据组成一组,然后在下循环中依次处理,用数组存储数据,用循环对下标变量进行相同操作的程序处理,使程序变得简洁清晰。

数组是一种构造类型数据,是按照有规律排列的同类型数据元素的集合。
在这里插入图片描述
1、定义时用花括号进行初始化
2、数组长度必须是整型常量
在这里插入图片描述

①一维数组的应用

数组元素在内存中按一定顺序连续排列存放的,和简单变量一样,数组也必须先定义后使用。
数组的逆置
1、数组的长度必须设置足够大
2、当元素个数为偶数,交换一半,若为奇数,交换除中间元素的一半,因此需要遍历数组一半的元素
3、需要定义临时变量
在这里插入图片描述
例题2、在这里插入图片描述

在这里插入图片描述

②二维数组的概念

实际环境中有很多对象是二维的或者多维的,如数学中的矩阵元素是二维分布的,3D图像中的数据是三维的,因此c语言允许构造多维数组。

数组的维数是指数组名后面下标的个数

在这里插入图片描述

实际的硬件存储器是连续是连续编制的,存储器单元是按一维线性排列的,那么对于二维甚至多维数组
在这里插入图片描述
在这里插入图片描述
可以看出c[0],其实第一行一维数组的数组名,我们知道一维数组名是一维数组第一个元素的地址,则c[0]其实就c[0][0}的地址,因此二维数组可以看做是一种特殊的一维数组。

1、二维数组的初始化:(可以省略行数,不能省略列数)

2、二维数组的应用:

除了函数调用外,不能单独对数组名引用,像整体数组一样,不能给数组整体赋值,引用二维数组元素,也相当于引用同类型的简单变量。

3、例题1:
求两个矩阵和
在这里插入图片描述

例题2:求两个矩阵积
在这里插入图片描述
优化
在这里插入图片描述
例题3
从键盘输入8个学生三门课程的成绩,求每个学生各门的平均成绩,并按照平均分从高到低的顺序输出每个学生各门课程的成绩和平均成绩

分析:
三门成绩采取整型方式存储,平均成绩采取浮点型方式存储,由于不同数据类型不能存在同一个数组,只能采用两个数组,且两个数组的对应下标必须一致。

1、从键盘输入每个学生各门成绩并存入score[][3};
2、计算出平均成绩并存入数组aver[]
3、按平均成绩对各门课程成绩进行排序
4、输出排序后的score,aver数组

三、指针和数组

数组名是指向数组首元素的指针类型的符号常量,而指针变量可以存放地址数据,所以可以让指针变量来指向数组元素,数组元素可通过数组名和下标进行访问。

数组和数组元素的引用也可以通过指针变量的形式进行操作。

定义指向数组元素的指针变量和定义一般的指针变量相似,指示要求在定义的过程中基类型和数组元素类型相同,定义同时也可以进行赋初值
如:p=&array[0]或p=array
注意:不可以写成array++,但可以写成p++
不能把一个整形的数赋值给p,也不能把p赋值给整形变量在这里插入图片描述
当指针变量指向一个地址时,指针变量可以加一个整型m或者减一个整型m,此时指针向前后者后移动m个元素,注意是元素的个数,而不是m个字节。指针变量每增加1,地址的字节数就会增加相应类型的字节数。两个同类型的指针相减表示两者之间的元素个数,即两者地址的字节数之差除以数据类型,两个之指针之间不能进行加法、乘法、除法、等运算
p[-],相当于访问p前面那个元素

在这里插入图片描述
但若两个指着变量不指向同一数组,比较无意义

NULL空指针,不指向任何存储空间,可以赋值给任何指针类型的变量,可以和任何指针进行相等和不相等的比较
用指针访问数组
在这里插入图片描述
在这里插入图片描述

四、字符数组

用来存放字符数据的数组是字符数组。字符数组中一个元素存放一个字符。
定义格式:
一维数组:char 数组名[常量表达式];

如:char name[20];

一维字符数组常用于处理一个字符串。

二维数组:char[常量表达式1][常量表达式2};
如:

char fruits[40][20}

二维字符数组常用于处理多个字符串。

还有多维字符数组
在c语言中,没有字符串类型,因此采用字符数组来表示字符串

字符串常量:双引号括起来的字符序列。
字符串的存储:使用字符数组来存储字符串,将其中的字符按顺序存放在数组中,并在数组最后自动加一个字符串结束符‘\0’

所以存储长度=字符序列长度+1
在这里插入图片描述

①字符数组的初始化

(可以使独立的字符,还可以是字符串常量)
1、独立的字符
在这里插入图片描述
2、字符串常量
在这里插入图片描述

②字符数组及字符串的输入与输出

在这里插入图片描述
第一种单个字符输入时,当输入的字符是换行时,退出输入操作,用这种方法进行输入时,系统不会自动添加字符串结束符‘\0’,输出时也不会 进行自动检测‘\0’,必须认为加上‘\0’(较繁琐)

第二种方式,输入多个字符串时,用空格隔开

例题:
在这里插入图片描述

③二维字符数组的初始化:

二维字符数组常用于处理多个字符串。同一位数组一样有两种方式。
1、按独立的字符
在这里插入图片描述

2、按字符串常量
在这里插入图片描述

④字符串处理函数

计算机里的数据分为数值数据和非数值数据,字符串是最基本的非数值数据,字符串处理在语言编译,信息检索文字编辑等问题中有着广泛应用,需要对字符串做整体的处理,字符串不能被整体赋值,也不能整体比较,字符串处理函数应运而生。它们被包含在<string.h>中
在这里插入图片描述
1、gets(字符数组)从键盘输入一以回车结束一行/整行字符到字符数组中,并自动加‘\0’,因此输入串长度应小于字符数组长度注意,可以不带空格也可带空格,返回值为字符数组的首地址。注意gets和scanf不同,gets可以输入带空格的字符串,但scanf格式说明符不可以。

2、puts(字符串)将指定的字符串或者在字符数组中以‘\0’结束而存储的序列输出到终端屏幕上,输出完自动换行

3、strcat(字符数组1,字符串2)将字符串2连接在字符数组1后面,字符串2可以是字符串常量或者字符数组名,字符数组1只能是数组名返回字符数组的首地址。函数的返回值是一个指针,指向生成新字符串的首地址.(注意:字符串1、2必须以‘\0’结束,字符串1必须有足够空间)

4、strcpy(字符数组1,字符串2);赋值字符串2到字符数组1中,返回字符数组1的首地址(注意:字符串2必须以‘\0’结束,字符串1必须有足够的空间)

5、strcmp(字符串1,字符串2);两个字符串比较,返回比较的结果。对字符串1、2从左到右逐个比较其ASCII值,直到字符值不相等或遇到结束标志符结束,若相等返回值为整数0,不相等前者字符大返回正整数,后者大返回负整数

6、strlen(字符串)计算字符串有效长度,不包括‘\0’,但是字符串必须以‘\0’结束。
在这里插入图片描述
7、注意事项
在这里插入图片描述
8、字符串数组的应用
在这里插入图片描述
在这里插入图片描述
9、注意
在这里插入图片描述

五、位运算:

c语言最初是用来编写系统软件的程序设计语言,而系统软件中,经常要以位为单位进行数据处理,所以c语言提供了丰富的位运算,位运算是对二进制位进行运算,通常位运算的对象是整形数据,也可以是字符型数据,在操作时这些数据是以补码的形式进行运算,其结果仍然是整数,在运算时要注意位运算是对整个整数进行的运算,因此不允许只操作这些数据中的某一位,位运算符众多,这些操作符的优先级也有所不同,取反最高,随后是与操作,异或操作和或操作,最低的是左移和右移操作

除了按位取反为单目运算符,其余均为双目运算符
1、按位与:先将数化为二进制补码 (假设一个整数由两个字节存储时共有16位,前八位为符号位),按位与通常用来将某些位清0或保留某些位的值,例如:
在这里插入图片描述

2、按位或:仍是先将数转化成二进制补码的形式再进行按位或运算,按位或通常用来将某些位设置为1,例如:
在这里插入图片描述
注意,当我们说某个数的第几位时,是从最右侧的第0位开始数的。

3、异或运算:当一个值为1,另一个结果为0,则结果为1,若两数相同,则为0,仍然是现将数转化成二进制补码进行运算,通常用来将某些位的值反转或保留原值,例如:
在这里插入图片描述
若保留怎么办?
在这里插入图片描述
练习:
1、判断整数的奇偶性
分析:偶数的补码最后一位是0,奇数为1,因此可以通过位运算把整数的最低一位取出来与1比较判断是奇数还是偶数。注意,按位与运算优先级低于关系运算符等于,因此需要加上括号。
在这里插入图片描述
2、要求交换两个变量的值(不适用临时变量)
分析:需要保留原来的值
什么运算可以保留原来的值呢?
按位与或者按位异或,若是按位与,则需要和补码全为1的数操作,而异或操作可以使用任意一个数,连续异或两次从而保留原数据的值。
如:a=011,b=101;
a=a^b=011 ^101=110
b=a^b=110 ^011=101
a=a^b=110 ^101=011
4、按位取反:先计算出数值的补码,然后进行取反操作,这里的取反是符号位也取反,取反运算可以用来间接构造一个数,例如,构造一个所有位都为1的整数,由于不同运算中存储整数的字节数是不一样的,直接写a=0x1111对四个字节长度的整数是不合适的,此时就可以利用取反操作,将取反0赋值给a,此时a所有位都为1,。
5、左移:<<(双目)x<<y:表示x所有位向左移动y位,移出的高位丢弃,空出的低位补0.
在这里插入图片描述
若丢弃的高位不包含1,每左移一位相当于给该数乘以2,所以可以把左移运算当做乘2运算,并且效率很高,比乘法运算快得多。

6、右移操作:x>>y:x所有位向右移动y位,移出的低位丢弃,空出的高位补0,有符号数高位补的数字与符号位值相同。

每右移一位相当于给该数除以2.
7、位运算符与赋值运算符结合组成新的赋值运算符
在这里插入图片描述
例题1、求整数的绝对值
在这里插入图片描述
在这里插入图片描述
例题2、将整数x循环右移bit位
在这里插入图片描述
代码实现:

#include<stdio.h>
#include<math.h>
void main()
{
   int x,bit;
   int temp=0;
   scanf("%d",&x);
   scanf("%d",&bit);
   temp=x<<(8*sizeof(int)-bit);
   x>>=bit;
   x|=temp;
   printf("%d",x);
   
}

总结:位运算直接针对数据进行操作运算速度快 节约内存 在系统编程中占有重要地位,例如对表示状态位时就可以使用位运算来操作,另外,位运算有6个基本运算符,其余均为混合运算符,注意与其他运算符使用时的优先级。
在这里插入图片描述

六、编译预处理:

①编译预处理之条件编译

之间遇到过求平方根操作,需要使用math.h头文件,打开此文件会发现里面有条件编译命令,为什么使用条件编译命令呢?出于对程序代码优化或可移植性考虑,希望只对代码的一部分内容进行编译,此时就需要在程序中加上条件,让编译器只对满足条件的代码进行编译,将不满足条件的代码舍弃。

例如:程序的运行平台具有多样性,在windows平台下编写的程序可能使用到某一个库或者与硬件相关的属性方法,现在要将这个程序移植到linux系统中,那么程序中可依赖的库,或者和硬件相关的属性和方法就不得不更改了,此时程序设计人员可以使用条件编译来根据不同环境的标志符选择不同的库或方法,这样就提高了程序的可移植性。

条件编译的使用方法:
第一种格式:

#ifdef  标识符
     程序段 1
#else
     程序段2
#endif//若标识符已被#define定义,则编译程序段1,否则编译程序段2

第二种格式:

#ifndef  标识符
      程序段1
#else
      程序段2
#endif//表示若标识符未被#define定义,则编译程序段1,否则编译程序段2

以上两种格式都可以选择不适用#else和程序段2

#include<stdio.h>
#define MA 1
main()
{
   int a=10;
   #ifdef MA
     a=a-10;
     printf("%d\n",a);
   #else
     a=a+10;
     printf("%d\n",a);
   #endif
   
}

第三种格式:

#if 常量表达式
    程序段 1
#else
    程序段 2
#endif

若常量表达式非0,则编译程序段1,否则编译程序段2

第三种格式举例:输入一任意字符串,将其输出为全部是大写字母,或者全部是小写字母


void main()
{
   char str[20];
   int i=0;
   scanf("%s",str);
   while((c=str[i])!='\0')
   {
      i++;
      转换大小写字母
      printf("%c",c);
   }
}

由于我们不知道到底是转变成大写还是小写,因此就可以进行条件预处理:
在这里插入图片描述
与条件语句不同的是,条件语句会对整个源程序进行编译,生成的目标代码程序较长,而采用条件编译时,根据条件,只编译程序段1或程序段2,生成的目标程序较短,如果条件选择的程序段很长,采用条件编译是十分必要的。

②编译预处理命令之文件包含

一个c语言程序文件要转换成可执行文件,首先要经过编译,将其编译为目标文件之后,目标文件经过连接才可以成为可执行文件,编译过程可以被分为两个阶段。首先是编译预处理(在编译前根据预处理命令对源文件做出相应的处理,如常见的文件包含,宏定义,条件编译),随后进行编译。

文件包含:
文件包含是将指定的某个源文件的内容全部包含到当前文件中,用#include命令来实现在这里插入图片描述
为什么用文件包含?
现在的程序规模通常很大,开发时是通过不同的功能来将整个程序划分为若干个小的模块文件,这样方便多个人同时编程实现,当一个程序由多个文件组成时,就可以使用文件包含将它们整合成一个文件进行编译,且可将多个功能函数集合在一个头文件中,当其他文件要是用这些功能时,就可以将这个头文件包含进来,这样更高效,确保了函数使用的一致性。
格式1:#include<文件名>
预处理程序仅在\include目录下查找指定文件。

格式2:#include"文件名"
预处理程序首先在当前目录中查找指定文件,若找不到再到\include目录中查找

说明:在这里插入图片描述
在这里插入图片描述

七、文件:

①文件概述:

文件式存储在外部存储介质上的数据集合。使用文件名作为区分标志,例如扩展名为.c的文件保存程序的源代码,扩展名为.obj的文件,保存着程序的目标代码。

对文件的访问也是通过文件名进行的,访问文件时,程序会根据文件所在的盘符路径找到文件所在的位置,随后按照主文件名和文件扩展名才能确定要访问的文件。

②文件的存储特性:
文件是一个有序的数据序列。c语言把文件作为一个字符(字节)序列——**数据流**来处理,对文件的存取是以字符(字节)为单位进行的。
③分类:

根据文件的依附的介质
可以分为普通文件设备文件

普通文件:驻留在外存介质上的文件,如文本文件,可执行文件。
设备文件:每台与主机相连的输入输出设备都被看做是一个文件,如显示器,打印机,也就是把实际的物理设备抽象成了逻辑文件。

根据文件的组织方式
可以分为顺序读写文件随机读写文件
顺序读写文件:
按照文件所存储的数据的顺序从头到尾进行访问,如Linux操作系统中的FIFO文件,这种文件每次读写的数据长度一般是不相等的。
随机读写文件:
存储的数据通常是有结构的,每条数据记录长度相等,因此可以通过计算直接访问文件中的特定记录。(跳跃式访问文件)
根据文件的存储形式:
分为ASCII码文件(文本文件,它在存储时,每个字符对应一个字节,用来存储字符的ASCII码值)和二进制文件(除ASCII码文件之外的文件都是二进制文件)

如将整数32767分别存储在这两种数据文件中占用5个字节。字符3的ASCII码值为00110011,字符2的ASCII码值为00110010,字符7的ASCII码值为00110111,字符6的ASCII码值00110110,字符7的ASCII码值00110111。当我们在DOXS系统或者Windows系统打开该文件时,内容是可见的,显示为32767

如果使用二进制文件(以二进制**补码形式存储)保存只需要占用两个字节,该文件的内容无法通过直接打开文件来识别,打开该文件将会在屏幕上显示ASCII值为127和255的两个字符。在c语言中,默认情况下键盘为标准输入设备,显示器为标准输出设备,这两个文件都被当做ASCII码文件进行处理。

访问文件时,程序可以使用操作系统提供的一系列接口函数或库函数。按照操作系统对磁盘文件的读写方式,文件系统可分为:缓冲文件系统,非缓冲文件系统。

这里的和缓冲区是为了提高程序访问文件的速度而开辟的一块用于读写操作内存,大小通常为512字节。程序在和文件交互的过程中,可以将一批数据暂时存放在缓冲区,而不必直接读写磁盘上的文件

根据是否使用缓冲区,c语言的文件处理函数被分为带缓冲区的标准I/O函数和不带缓冲的系统I/O函数
为了保证程序的可移植性,编程时通常使用标准的I/O函数,这组函数的处理过程为:

程序读文件时,将会读出磁盘文件中的一块,装入文件缓冲区中,然后从缓冲区中取出所需的数据,送入数据区中指定变量对应的内存单元中,程序写文件时,首先将数据写入缓冲区中,当缓冲区写满或调用了缓冲区刷新函数时,才会将缓冲区的数据写入磁盘文件(外存)

④如何在c程序中使用文件

c语言程序操作文件的步骤一般是;
1、建立/打开文件
2、根据需要对文件进行读/写
3、关闭文件

1、打开文件时,会建立用户程序与文件的联系,若使用标准I/O函数打开文件,系统将会为文件分配一个文件信息区(缓冲区的位置;文件的下一个位置),同时返回一个FILE类型的指针,记录该信息区的地址,程序中后续代码将会使用这个指针对文件进行读写操作,读写文件是指根据实际问题的需要,访问文件中的数据或修改文件中的数据,还包括在文件末尾追加信息和定位文件读写指针的操作。读写文件时,会从外存中取出一块数据临时存入文件信息区的缓冲区中,随后程序根据文件读写指针的位置读出或写入指定大小的数据,读写完成后,文件读写指针会自动向后移动直到遇到文件结束符号EOF,关闭文件时,系统将切断文件与用户程序的联系同时释放文件的信息区。由此,在访问文件时,系统会在内存中为每个打开的文件开辟一个文件信息区,用来存放文件的相关信息,例如,文件名,文件状态,文件当前读写位置,缓冲区状态,这些信息保存在一个结构体变量中,通过这个变量才能读写文件编写程序。

c程序使用文件时,通过一个指针变量指向对应的文件信息区,这个指针变量称为文件指针。(文件指针和文件读写指针是两个不同的概念)

定义文件指针的一般形式:FILE *指针变量标识符
如: FILE *fp;
FILE类型定义位于stdio.h头文件中,使用FILE类型时,要将stdio.h头文件用include命令包含在程序中
在这里插入图片描述
c程序中,文件的打开关闭读写等操作通常是使用库函数来实现的,stdio.h文件提供了大量的标准I/O函数,调用这些函数时,程序都需要包含stdio.h

1、文件打开函数:fopen(文件名,使用文件方式);
函数功能:按指定的文件使用方式打开指定文件。若文件打开成功,为该文件分配一个文件缓冲区和一个FILEL类型变量,返回一个FILE类型指针(第一个参数是要打开的文件名,可以是字符串变量或者双引号括起来的字符串常量,表示要打开文件的相对路径名或绝对路径名);若文件打开失败,返回NULL。

如:如果当前程序所在目录为e:\program,目录中有文件input.txt

FILE *fp;
fp=fopen("e:\\program\\input.txt","r");//盘符或目录名之后要用两个斜线来分割,除此之之外,也可以用一个反斜线来替换两个斜线
或
fp=fopen("input.txt","r");char *str="input.txt";
fp=fopen(str,"r");
不管使用何种方式,fopen执行后都会返回input.txt文件相关联的文件指针,将这个指针赋给指针变量fp,后续程序局可以使用fp来读写文件了
⑤文件使用方式对访问文件的影响

打开文件时可以选择
1、r方式(只读方式):为读文件打开文件。若文件不存在,返回NULL。
2、w方式(只写方式):为写文件打开文件。若文件不存在,则建立一个新文件;若文件已存在,则清空文件。
3、a方式(追加方式):为写文件打开文件。若文件已存在,则保持原来文件的内容,将新的数据增加到原来数据的后面;若文件不存在,则建立一个新文件。
以上三个方式互斥,默认以文本文件的形式对文件进行访问。

添加其他选项
b选项表示按照二进制文件方式进行访问,+方式表示打开的文件,既允许读出也允许写入数据,但r+方式必须保证读写文件存在,否则fopen函数返回NULL值
在这里插入图片描述
文件关闭函数
fclose(文件指针fp);
其中fp:已经打开的文件指针。
函数功能:关闭fp指定的文件,释放该文件的缓冲区、FILE类型变量及文件指针。
若文件关闭成功,则返回0;否则返回非0值

fp=fopen("input.txt","r");
fclose(fp);

打开并关闭文件的程序完整代码:

#include<stdio.h>
void main()
{
    FILE *fp;
    fp=fopen("input.txt","r");
    if(fp==NULL)
    {
      printf("The file does not exist!\n");
      return;
    }
    printf("The file was opened!\n");
    printf("fclose return value %d.\n",fclose(fp))
}

文件顺序读写
单字符读写函数:(fputc和fgetc)
字符串读写函数:(fputs和fgets)
格式化读写函数:(fprintf和fscanf)

单字符读写函数:

写函数函数原型:int fputc(char ch,FILE *fp);
功能:将字符ch写入到文件指针fp所指向的文件的当前写指针位置。成功时返回字符本身,否则返回EOF.
读函数函数原型:int fgetc(FILE *fp);
功能:从文件指针fp所指向的文件的当前读指针位置读取一个字符,读取完成后指针自动后移指向下一个字符。成功时返回该字符,否则返回EOF.

字符串读写函数:

写函数函数原型:int fputs(char *str,FILE *fp);
功能:将字符串str写入到文件指针fp所指向的文件的当前写指针位置。成功时返回非0值,否则返回EOF.

读函数函数原型:char *fgets(char *str,int n,FILE *fp);
功能:从fp所指向的文件的当前读指针位置读出n-1个字符放入字符串str中。成功时返回该字符串地址,否则返回NULL.

例题:

#include<stdio.h>
#include<stdlib.h>
void main()
{
    int i;
    char str[30];
    FILE *fp;
    fp=fopen("alph.txt","w");
    if(fp==NULL)
    {
        printf("can't open the file !\n");
        exit(0);
    }
    for(i=0;i<26;i++)
      fputc('A'+i,fp);
     fclose(fp);
     fp=fopen("alph.txt","r");
     if(fp==NULL)
     {
        printf("can't open the file!\n");
        exit(0)
     }
     fgets(str,27,fp);
     printf("The content is:\n%s\n",str);
     fclose(fp);
}

scanf和printf的作用对象是标准I/O设备(键盘和显示器)而fscanf和fprintf的读写对象是磁盘文件。

写函数函数原型: int fprintf(FILE *fp,格式控制串,输出项参数表)//这里的格式控制串与printf函数相同

函数功能:将输出项按照指定的格式写入fp所指向的文件中。若输出操作成功,返回实际写入的字节数。若输出操作失败,则返回EOF.

读函数函数原型:int fscanf(FILE *fp,格式控制串,变量地址列表);
//格式控制串和变量地址表的规定和使用方法与scanf函数相同。
功能:按格式控制串所描述的格式,从fp所指向的文件中读取数据,送到指定的变量。
若输入操作成功,返回实际读出的数据项个数,不包括数据分隔符。
若没有读到数据项,则返回0,若文件结束或调用失败,则返回EOF.
#include<stdio.h>
#define N 3
struct stu_info
{
    char name[10];
    int score;
};
void main()
{
   struct stu_info stu[N],output[N];
   int i;
   FILE *fp;
   for(i=0;i<3;i++)
   {
     printf("please input No.%d student's name:",i+1);
     fflush(stdin);
     gets(stu[i].name);
     printf("please input No.%d student's score:",i+1);
     scanf("%d",&stu[i].score);
   }
   printf("The information was inputted!\n");
   fp=fopen("info.txt","w");
   if(fp==NULL)
   {
      printf("create file failed!\n");
      return;
   }
   for(i=0;i<3;i++)
   {
       fprintf(fp,"%s %d",stu[i].name,stu[i].score);
       fclose(fp);
    fp=fopen("info.txt","r");
    if(fp==NULL)
    {
       printf("can't open the file!\n");
       return;
    }
    printf("name\t\score\n");
    printf("-------\n");
    for(i=0;i<3;i++)
    {
       fscanf(fp,"%s %d",&output[i].name,&output[i].score);
       printf("%s\t\t%d\n",output[i].name,output[i].score);
    }fclose(fp);
   }
}

字符型数据采用单字符或字符串读写函数,其他类型数据可以使用格式化读写函数

数据块读写及随机读写

写函数:int fwrite(void *p,int size,int n,FILE *fp);
p:某类型指针
size:数据块的大小(字节数);
n:此次写入文件的数据块数;
fp:文件指针变量
//函数功能为,将p指向的存储区中的n个大小为size的数据块写入fp所指向的文件。若输出操作成功,返回写入的数据块数;操作失败返回0

读函数:int fread(void *p,int size,int n,FILE *P);
P:某类型指针
size:数据块的大小(字节数)
n:此次写入文件的数据块数
fp:文件指针变量
//功能:从fp所指向的文件中,读取n个大小为size的数据块,存入指针p所指向的存储区域。若输入操作成功,返回实际读出的数据块个数;若文件结束或调用失败,则返回0.

使用fwrite和fread函数时通常还会配以feof函数来协助,对文本文件进行读操作时,文件读写函数可以通过是否遇到了结束符EOF来判定文件是否结束,结束符EOF的值为-1,但在二进制文件中,-1是合法的数据,所以系统提供了一个适用对象更普遍的文件结束测试函数feof

函数原型:int feof(FILE *fp);
功能:测试fp所指向的文件是否已读到文件尾部。若该文件没有结束,返回0;否则,返回非0的值。

块读写函数是以二进制文件的方式来写入和读出数据的,无法从Windows系统直接读取出来。

随机读写文件
通常实现文件的随机读写是通过调整文件读写指针的位置来实现的,程序中首先通过函数调整文件的读/写指针到需要读数据或写入数据的位置,随后再使用之前学习过的文件读写函数来读取或写入数据。
注意:文件指针是指向文件信息区的指针变量,文件的读写指针用来记录当前读写操作进行到了文件中的什么位置。

第一个调整文件读写指针位置的函数时rewind:

rewind函数原型:void rewind(FILE *fp);
功能:将fp所指文件的位置指针重新返回到文件的开头,调用后不返回任何值。


ftell函数原型:long ftell(FILE *fp);
功能:返回fp所指文件当前的读写指针位置,如果函数执行成功,返回读写指针相对于文件开头的偏移量(如结果为1024,说明相对开头的偏移量1024个字节);如果函数执行失败,返回-1;

fseek函数原型:int fseek(FILE *fp,long offset,int where);//offset表示文件偏移量,int型参数where表示起始位置。
功能:按照(起始点where+位移量offset(用字节数表示))设置文件位置指针的当前位置。如果执行成功返回0,否则返回非0

其中在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值