C语言基础六
结构体
结构体:不同类型的数据组合成一个有机的整体,结构体是一种构造数据类型。
描述一组具有相同类型数据的有序集合,用于处理大量相同类型的数据运算–数组,数组也是构造函数。
结构体类型的概念:结构体是一种构造类型的数据结构,是一种或多种基本类型或构造类型的数据的集合。
结构体类型定义
先定义结构体类型,再去定义结构体变量
struct 结构体类型名{
成员列表
};
struct stu{
int num;
char name[20];
char sex;
};
//有了结构体类型后,就可以用类型定义变量了
struct stu caicai,shu//定义了两个struct stu 类型的变量每个结构体都有三个成员,分别是num name sex
在定义结构体类型的时候顺便定义结构体变量,以后还可以定义结构体变量
struct 结构体类型名{
成员列表;
}结构体变量1,变量2;
struct 结构体类型名变量3,变量4;
struct stu{
int num;
char name[20];
char sex;
}caicai,shu; //定义了caicai,shu
struct stu zhou,zhao;//又定义了zhou,zhao
在定义结构体类型的时候,没有结构体类型名,顺便定义结构体变量,因为没有类型名,所以以后不能再定义相关类型的数据了
struct {
成员列表;
}变量1,变量2;
struct stu{
int num;
char name[20];
char sex;
}caicai,shu;//以后没法再定义这个结构体类型的数据了,因为没有类型名,只有caicai,shu
最常用的方法
利用typedef将一个结构体类型重新起个类型名,用新的类型名替代原先的类型
struct stu{
int num;
char name[20];
char sex;
};
struct stu zhou,zhao;
//利用typedef重新起个名,
typedef struct stu{
int num;
char name[20];
char sex;
}STU;
STU zhou,zhao;
结构体变量的定义初始化及使用
结构体变量,是个变量,这个变量是若干个相同或不同数据构成的集合
在定义结构体变量之前首先得有结构体类型,然后再定义变量
在定义结构体变量的时候,可以顺便给结构体变量赋初值,被称为结构体的初始化
结构体变量初始化的时候,各个成员顺序初始化
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name[20];
char sex;
}STU;
int main()
{
STU caicai = {
102,
"caicai", //成员按顺序初始化,后面的成员可以不初始化
};
STU shu = {
100,
"shu",
'm'
};
return 0;
}
结构体变量成员的引用方法:结构体变量.成员名
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name[20];
char sex;
}STU;
int main()
{
STU shu;
shu.num = 100;
strcpy(shu.name, "shu");
//shu.name = "shu";shu.name是地址是个常量
shu.sex = 'm';
printf("name:%s",shu.name);
return 0;
}
结构体成员多级引用
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
struct date {
int year;
int month;
int day;
};
typedef struct stu {
int num;
char name[20];
char sex;
struct date bri;
}STU;
int main()
{
STU shu;
shu.num = 100;
strcpy(shu.name, "shu");
shu.sex = 'm';
shu.bri.year = 2000;
printf("name:%s\n",shu.name);
printf("name:%d\n", shu.bri.year);
return 0;
}
相同类型的结构体变量可以相互赋值
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
struct date {
int year;
int month;
int day;
};
typedef struct stu {
int num;
char name[20];
char sex;
struct date bri;
}STU;
int main()
{
STU shu;
shu.num = 100;
strcpy(shu.name, "shu");
shu.sex = 'm';
shu.bri.year = 2000;
printf("name:%s\n",shu.name);
printf("name:%d\n", shu.bri.year);
STU caicai;
caicai = shu;
printf("name:%s\n", caicai.name);
printf("name:%d\n", caicai.bri.year);
return 0;
}
结构体数组
typedef struct stu {
int num;
char name[20];
char sex;
struct date bri;
}STU;
STU erban[50];定义了一个STU(struct stu)类型的结构体数组,有50个元素
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
struct date {
int year;
int month;
int day;
};
typedef struct stu {
int num;
char name[20];
char sex;
float score;
struct date bri;
}STU;
int main()
{
STU erban[50];
strcpy(erban[0].name, "shu");
erban[0].num = 1;
erban[0].score = 100;
strcpy(erban[1].name, "caicai");
erban[1].num = 2;
erban[1].score = 99;
printf("第%d名的名字是%s分数是%.2f\n", erban[0].num, erban[0].name, erban[0].score);
printf("第%d名的名字是%s分数是%.2f\n", erban[1].num, erban[1].name, erban[1].score);
printf("平均成绩是%.2f\n", (erban[1].score+ erban[0].score)/2);
return 0;
}
结构体指针
结构体变量存放内存中,也有起始地址,定义一个变量来存放这个地址,那这个变量就是结构体指针变量。也是4个字节
结构体指针变量的定义方法:
struct 结构体类型名* 结构体指针变量名;
struct stu{
int num;
char name[20];
};
struct stu * p;//定义了一个struct stu *类型的指针变量
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name[20];
char sex;
float score;
}STU;
int main()
{
STU shu = {100,"caicai",'m',};
STU* caicai;//caicai与数必须同一个结构体类型
caicai = &shu; //caicai保存shu的地址,
printf("name:%s\n",shu.name);
printf("name:%s\n", (*caicai).name);//*caicai相当于shu
printf("name:%s\n", caicai->name);//caicai指向name,相当于shu.name
return 0;
}
给函数结构体变量的地址
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name[20];
char sex;
float score;
}STU;
void fun(STU *p) //定义变量类型STU*
{
p->num = 2; //赋值
strcpy(p->name, "lucy"); //赋值,p->name是一个地址常量不能被赋值
p->score = 99; //赋值
}
int main()
{
STU shu ; //定义结构体类型,变量
fun(&shu); //给fun函数传shu的地址
printf("name:%s\n",shu.name);
printf("num:%d\n", shu.num);
printf("score:%.2f\n", shu.score);
return 0;
}
传结构体数组的地址
结构体数组,是由若干个相同类型的结构体变量构成的集合。存放在内存里,也有起始地址,其实就是第0 个结构体变量的地址。
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name[20];
float score;
}STU;
void fun(STU *p,int j) //定义p接受传入的首地址类型为STU*
{
int i;
for ( i = 0; i < j; i++)
{
printf("num:%d name:%s score:%.2f\n", p[i].num, p[i].name, p[i].score);
}
}
int main()
{
STU erban[3] = {
{1,"shu",99},
{2,"caicai",98},
{3,"kunkun",90}
}; //定义一个结构体数组
fun(erban,3); //给fun函数传入结构体数组首地址
return 0;
}
结构体变量的地址编号和结构体第一个成员的地址编号相同,但指针的类型不同。
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name[20];
float score;
}STU;
int main()
{
STU shu;
printf("%p\n",&shu);//shu的类型为STU,指针类型为STU*
printf("%p\n", &shu.num);//shu.num类型为int,指针类型为int*
return 0;
}
结构体数组的地址就是结构体数组中第0 个元素的地址
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name[20];
float score;
}STU;
int main()
{
STU erban[3] = {
{1,"shu",99},
{2,"caicai",98},
{3,"kunkun",90}
}; //定义一个结构体数组
printf("%p\n",erban);//erban是一个数组的地址
printf("%p\n", &erban[0]);//数组第0个元素,erban[0]类型是STU,指针的类型是STU*
printf("%p\n", &erban[0].num);//数组第0个元素首个成员,erban[0].num类型是int,指针的类型是int*
return 0;
}
结构体的内存
结构体变量大小是,它所有成员的大小之和。
在实际给结构体变量分配内存的时候,是有规则的
规则1:以多少个字节为单位开辟内存
给结构体变量分配内存的时候,会去结构体变量中找基本类型的成员
哪个基本类型的成员占字节数多,就以它的大小为单位开辟内存,
说明:
成员中只有char 型数据,以1 字节为单位开辟内存。
成员中出现了short int 类型数据,没有更大字节数的基本类型数据。以2 字节为单位开辟内存
出现了int float 没有更大字节的基本类型数据的时候以4 字节为单位开辟内存。
出现了double 类型的数据,无论是那种环境,double 型变量,占8 字节。但是在vc6.0 和Visual Studio 中里,以8 字节为单位开辟内存。在Linux 环境gcc 里,以4 字节为单位开辟内存。
如果在结构体中出现了数组,数组可以看成多个变量的集合。
如果出现指针的话,没有占字节数更大的类型的,以4 字节为单位开辟内存。
在内存中存储结构体成员的时候,按定义的结构体成员的顺序存储。
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef struct stu {
int num;
char name;
float score;
}STU;
int main()
{
STU shu;
printf("%p\n",&shu.num);
printf("%p\n", &shu.name);
printf("%p\n", &shu.score);//char类型占一个字节但是name与score却相差4个字节
return 0;
}
规则2:字节对齐
char 1 字节对齐,即存放char 型的变量,内存单元的编号是1 的倍数即可。
short int 2 字节对齐,即存放short int 型的变量,起始内存单元的编号是2 的倍数即可。
int 4 字节对齐,即存放int 型的变量,起始内存单元的编号是4 的倍数即可
long int 在32 位平台下,4 字节对齐,即存放long int 型的变量,起始内存单元的编号是4
的倍数即可
float 4 字节对齐,即存放float 型的变量,起始内存单元的编号是4 的倍数即可
double
vc6.0 和Visual Studio 环境下8 字节对齐,即存放double 型变量的起始地址,必须是8 的倍数,double 变量占8 字节
gcc 环境下,4 字节对齐,即存放double 型变量的起始地址,必须是4 的倍数,double 变量占8 字节。
当结构体成员中出现数组的时候,可以看成多个变量。
开辟内存的时候,从上向下依次按成员在结构体中的位置顺序开辟空间
字节对齐的目的:用空间来换时间,提高cpu 读取数据的效率
使用#pragma pack改变默认对齐原则,#pragma pack (value)时的指定对齐值value。value只能是:1 2 4 8等,指定对齐值与数据类型对齐值相比取较小值
以多少个字节为单位开辟内存,结构体成员中,占字节数最大的类型长度和value比较,取较小值,为单位开辟内存,结构体成员中成员的对齐方法,各个默认的对齐字节数和value相比,取较小值
如:如果指定对齐值:
设为1:则short、int、float等均为1
设为2:则char 仍为1,short 为2,int 变为2
#pragma pack(2)
struct stu{
char a;
int b;
} ;//默认占8个字节,value设定为2,则占6个字节,设定为1,则占5个字节
位段
定义:在结构体中,以位为单位的成员,称之为位段(位域)。
struct stu{
unsigned int a:2; //定义a占2位
unsigned int b:6;//定义b占6位
unsigned int c:4;//定义c占4位
unsigned int d:4;//定义d占4位
unsigned int i;
} data; //总sizeof占8字节,2+6+4+4=16位,两字节+(两字节)+四字节=8字节
不能对位段成员取地址,位段成员可能不够1字节
对于位段成员的引用如下:
unsigned int a:2; //定义a占2位
data.a =2//a的取值范围是00–11,转十进制是0–3
赋值时,不要超出位段定义的范围;如段成员a定义为2位,最大值为3,即(11)2,所以data.a =5,就会取5(101)的低两位进行赋值01
位段成员的类型必须指定为整型或字符型
一个位段必须存放在一个存储单元中,不能跨两个单元,第一个单元空间不能容纳下一个位段,则该空间不用,而从下一个单元起存放该位段
char 型位段存储单元是1 个字节
short int 型的位段存储单元是2 个字节
int 的位段,存储单元是4 字节
long int 的位段,存储单元是4 字节
struct stu{
char a:7;//第一个字节占7位,剩余1位
char b:7;//不能跨储存单元即1个字节,所以从第二个字节开始储存,占7位,剩余1位
char c:2;//不能跨储存单元,所以从第三个字节开始储存,占2位,剩余6位
}temp;//占3 字节,
位段的长度不能大于存储单元的长度
char 型位段不能大于8 位,一个字节
short int 型位段不能大于16 位,两个字节
int 的位段,位段不能大于32 位,4个字节
long int 的位段,位段不能大于32 位,4个字节
char a:9;//编译出错,位段a 不能大于其存储单元的大小
unsigned char a:1;
unsigned char b:2;
unsigned char :0;//作用是使下一个位段从,下一个存储单元开始存放
unsigned char c:3;(另一个单元)
unsigned a: 1;
unsigned : 2;//可以定义无意义位段
unsigned b: 3;
共用体
共用体和结构体类似,也是一种构造类型的数据结构。先定义出类型,然后用类型定义变量
union 共用体类型名{
成员列表
};
几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构,共用体所有成员占有同一段地址空间,共用体的大小是其占内存长度最大的成员的大小
同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用
共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被
覆盖
共用体变量的地址和它的各成员的地址都是同一地址
初始化共用体只能为第一个成员赋值,不能给所有成员都赋初值
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
typedef union temp {
unsigned char a;
unsigned short int b;
unsigned int c;
}TEMP;
int main()
{
TEMP t;
printf("%p\n",&t.a);
printf("%p\n", &t.b);
printf("%p\n", &t.c); //地址一样
t.c = 0xffffffff; //相当于全部赋值
printf("%x\n", t.a);
printf("%x\n", t.b);
printf("%x\n", t.c);
t.a = 0x11; //所有成员都会改变
printf("%x\n", t.a);
printf("%x\n", t.b);
printf("%x\n", t.c);
return 0;
}
枚举
将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
枚举类型也是个构造类型的,类型定义类似结构体类型的定义。使用枚举的时候,得先定义枚举类型,再定义枚举变量
enum 枚举类型名{
枚举值列表(枚举元素);
};
枚举元素是常量,默认是从0 开始编号的。枚举变量仅能取枚举值所列元素
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
enum week
{
mon,tue,wed,thu,fri,sat,sun //0,1,2,3,4,5,6,7
};
int main()
{
enum week day;
day = wed;
printf("%d\n",day); //2
//mon=2 mon是常量不能被赋值
return 0;
}
枚举值是常量,不能在程序中用赋值语句再对它赋值
枚举元素本身由系统定义了一个表示序号的数值,默认是从0开始顺序定义为0,1,2…
可以改变枚举值的默认值
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
enum week
{
mon=2,tue,wed,thu,fri=1,sat,sun //2,3,4,5,1,2,3改变枚举值的默认值
};
int main()
{
enum week day;
day = wed;
printf("%d\n",day); //4
return 0;
}
文件指针
关于文件:文件在硬盘上存储的时候,物理上都是用二进制来存储的。标准io 库函数,对文件操作的时候,不管文件的编码格式(字符编码、或二进制),而是按字节对文件进行读写,所以文件又叫流式文件,即把文件看成一个字节流。
文件指针:在程序中用来标识(代表)一个文件的,在打开文件的时候得到文件指针,文件指针就用来代表打开的文件。它是个FILE 类型结构体指针,用文件指针来标识一个文件。
形式:
FILE * 指针变量标识符;
FILE 为大写,需要包含<stdio.h>
FILE 是系统使用typedef 定义出来的有关文件信息的一种结构体类型,结构中含有文件名、文件
状态和文件当前位置等信息
实际编程中使用库函数操作文件,无需关心FILE 结构体的细节,只需要将文件指针传给io 库函数,
库函数再通过FILE 结构体里的信息对文件进行操作
typedef struct FILE 在stdio.h 文件中的文件类型声明
{ short level; //缓冲区“满”或“空”的程度
unsigned flags; //文件状态标志
char fd; //文件描述符
unsigned charhold; //如无缓冲区不读取字符
short bsize; //缓冲区的大小
unsigned char *buffer; //数据缓冲区的位置
unsigned ar*curp; //指针,当前的指向
unsigned istemp; //临时文件,指示器
shorttoken; //用于有效性检查
}FILE;
对文件操作的步骤:
1、对文件进行读写等操作之前要打开文件得到文件指针
2、可以通过文件指针对文件进行读写等操作
3、读写等操作完毕后,要关闭文件,关闭文件后,就不能再通过此文件指针操作文件了
stdin: 标准输入默认为当前终端(键盘)
我们使用的scanf、getchar 函数默认从此终端获得数据
stdout:标准输出默认为当前终端(屏幕)
我们使用的printf、puts 函数默认输出信息到此终端
stderr:标准错误输出设备文件默认为当前终端(屏幕)
当我们程序出错使用:perror 函数时信息打印在此终端
打开文件fopen
FILE *fopen(const char *path, const char *mode);
fopen 函数的功能是打开一个已经存在的文件,并返回这个文件的文件指针(文件的标识)
或者创建一个文件,并打开此文件,然后返回文件的标识。
打开的文件的路径:绝对路径D:\\demo\\test\\aaa.txt
,从根目录开始的路径名称,相对路径.\\test\\aaa.txt
文件打开的方式,即以什么样的方式(只读、只写、可读可写等等)打开文件
r 或rb 以只读方式打开一个文本文件(不创建文件)
w 或wb 以写方式打开文件(使文件长度截断为0 字节,创建一个文件)
a 或ab 以追加方式打开文件,即在末尾添加内容,当文件不存在时,创建文件用于写
r+或rb+ 以可读、可写的方式打开文件(不创建新文件)
w+或wb+ 以可读、可写的方式打开文件(使文件长度为0 字节,创建一个文件)
a+或ab+ 以追加方式打开文件,打开文件并在末尾更改文件(如果文件不存在,则创建文件)
返回值:
成功:打开的文件对应的文件指针
失败:返回NULL
调用fopen 函数的时候,一定要判断一下,打开是否成功。
关闭文件fclose
int fclose(FILE *fp);
关闭fp 所代表的文件,一个文件只能关闭一次
返回值:成功返回0,失败返回非0,可以通过返回值,来判断关闭文件是否成功。
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
FILE* fp; //文件指针
int ret; //接受fclose的返回值
fp = fopen(".\\test.txt","r+"); //打开的文件路径.//与.c在同一路径,打开方式,
if (fp == NULL) // 判断文件是否打开
{
perror("fopen"); //perror可以打印出错信息, No such file or directory
return 0;
}
printf("打开文件成功\n");
ret = fclose(fp); //接受fclose的返回值,判断文件是否打开成功
if (ret == 0)
printf("关闭文件成功\n");
else
printf("关闭文件失败");
return 0;
}
读写字符
一次读写一个字符
int fgetc(FILE *stream);
fgetc 从stream 所标识的文件中读取一个字节,将字节值返回
以t (文本)的方式: 读到文件结尾返回EOF
以b (二进制)的方式:读到文件结尾,使用feof(文件指针)判断结尾
feof 是C 语言标准库函数,其原型在stdio.h 中,其功能是检测流上的文件结束符,如果文件结束,
则返回非0 值,否则返回0(即,文件结束:返回非0 值;文件未结束:返回0 值)。
int fputc(int c, FILE *stream)
fputc 将c 的值写到stream 所代表的文件中。
如果输出成功,则返回输出的字节值;
如果输出失败,则返回一个EOF。
EOF 是在stdio.h 文件中定义的符号常量,值为-1
打开文件的时候,默认读写位置在文件的开始,如果以a 的方式打开读写位置在文件的末尾
向文件中读取字节或写入字节的时候,读写位置会往文件的末尾方向偏移,读写多少个字节,读写位置就往文件的末尾方向偏移多少个字节
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
FILE* fp; //文件指针
FILE* fp2;
int ret; //接受fclose的返回值
char ch;
fp = fopen(".\\test.txt","r+"); //打开的文件路径,打开方式
if (fp == NULL) // 判断文件是否打开
{
perror("fopen"); //perror可以打印出错信息, No such file or directory
return 0;
}
printf("打开test文件成功\n");
fp2 = fopen(".\\test2.txt", "r+"); //打开的文件路径,打开方式
if (fp == NULL) // 判断文件是否打开
{
perror("fopen"); //perror可以打印出错信息, No such file or directory
return 0;
}
printf("打开test2文件成功\n");
while ((ch = fgetc(fp)) != EOF)
{
fputc(ch, stdout);//读取字符到屏幕上标准输出
fputc(ch, fp2);读取字符输出到fp2
}
printf("\n");
ret = fclose(fp); //接受fclose的返回值,判断文件是否打开成功
if (ret == 0)
printf("关闭文件成功\n");
else
printf("关闭文件失败");
fclose(fp2);
return 0;
}
一次读写一个字符串
char *fgets(char *s, int size, FILE *stream);
从stream 所代表的文件中读取字符,在读取的时候碰到换行符或者是碰到文件的末尾停止读取,或者是读取了size-1 个字节停止读取,在读取的内容后面会加一个\0,作为字符串的结尾
返回值:成功返回目的数组的首地址,即s失败返回NULL
int fputs(const char *s, FILE *stream);
将s 指向的字符串,写到stream 所代表的文件中
返回值:成功返回写入的字节数,失败返回-1
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
FILE* fp; //文件指针
FILE* fp2;
char ch[100];
fp = fopen(".\\test.txt","r+"); //打开的文件路径,打开方式
fp2 = fopen(".\\test2.txt", "r+"); //打开的文件路径,打开方式
fgets(ch,30,fp);//碰到换行符,文件结尾,size-1(30-1)就会停止读取
printf("*%s*\n", ch);
fputs(ch,stdout); //输出到屏幕
printf("\n");
fputs(ch, fp2);//输出到fp2文件
fclose(fp);
fclose(fp2);
return 0;
}
读写文件
读文件fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread 函数从stream 所标识的文件中读取数据,每块是size 个字节,共nmemb 块,存放到ptr 指向的
内存里
返回值:实际读到的块数。
unsigned int num;
num=fread(str,100,3,fp);
从fp 所代表的文件中读取内容存放到str 指向的内存中,读取的字节数为,每块100 个字节,3 块。
返回值num,
如果读到300 个字节返回值num 为3
如果读到了大于等于200 个字节小于300 个字节返回值为2
读到的字节数,大于等于100 个字节小于200 个字节返回1
不到100 个字节返回0
写文件fwrite
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
fwrite 函数将ptr 指向的内存里的数据,向stream 所标识的文件中写入数据,每块是size 个字节,共
nmemb 块。
返回值:实际写入的块数
fwrite 函数是将内存中的数据原样输出到文件中。
fread 函数是将文件中的数据原样读取到内存里。
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
struct stu
{
char name[10];
int num;
int age;
}boya[2], boyb[2]; //定义两个结构体数组boya,boyb,分别有两个元素
int main()
{
FILE* fp; //文件指针
int i;
if ((fp = fopen("test.txt", "wb+")) == NULL)
{
printf("Cannot open file!");
return 0;
}//判断文件是否打开
printf("input data\n");
printf("name、num、age\n");
for (i = 0; i < 2; i++)
scanf("%s %d %d", boya[i].name, &boya[i].num, &boya[i].age);
//输入,赋值
fwrite(boya, sizeof(struct stu), 2, fp); //
rewind(fp); //文件指针经过写操作已经到了最后,需要复位
fread(boyb, sizeof(struct stu), 2, fp); //将文件中的数据读入到内存中,大小是结构体的大小
for (i = 0; i < 2; i++)
printf("%s %d %d\n", boyb[i].name, boyb[i].num, boyb[i].age);
fclose(fp);
return 0;
}
文件的随机读写
移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写
实现随机读写的关键是要按要求移动位置指针,这称为文件的定位.
完成文件定位的函数有:rewind、fseek 函数
rewind 复位读写位置
rewind(文件指针);
把文件内部的位置指针移到文件首
fwrite(pa,sizeof(struct stu),2,fp );//读的时候位置指针会往后便宜
rewind(fp);//复位读写位置
fread( pb,sizeof(struct stu),2,fp);//又可以从头读了
ftell 测文件读写位置距文件开始有多少个字节
long ftell(文件指针);
取得文件流目前的读写位置.
返回当前读写位置(距离文件起始的字节数),出错时返回-1.
long int length;
length = ftell(fp);//取得文件流目前的读写位置
fseek 定位位置指针(读写位置)
fseek 函数(一般用于二进制文件即打开文件的方式需要带b)
int fseek(FILE *stream, long offset, int whence);
//int fseek(文件类型指针,位移量,起始点);
移动文件流的读写位置.
whence 起始位置
文件开头SEEK_SET 0
文件当前位置SEEK_CUR 1
文件末尾SEEK_END 2
以起始点为基点,向前、后移动的字节数,正数往文件末尾方向偏移,负数往文件开头方向
偏移。
fseek(fp,50,SEEK_SET);//从头向尾偏移50个字节
fseek(fp,-50,SEEK_END);//从尾向头偏移50个字节
fseek(fp,0,SEEK_END);//在尾部不偏,定位到尾
fseek(fp,20,SEEK_CUR);//从当前位置向尾偏移20
打开一个文件到屏幕上
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
FILE *fp;
long int len;
char* str;
fp = fopen(".\\test.txt","r+"); //打开文件,txt文件内容一行,编码为ANSI
if (fp == NULL) //判断文件是否打开
{
perror("fopen");
return 0;
}
fseek(fp, 0, SEEK_END); //定位位置指针到文件尾
len = ftell(fp); //取得文件字节数
rewind(fp); //复位位置指针到文件头
printf("%d\n", len);
str = malloc(len+1); //申请动态内存加1
if (str == NULL) //判断动态内存是否申请成功
{
perror("malloc");
return 0;
}
fread(str, len, 1, fp); //读取文件到str
str[len] = '\0'; //将最后一个内存赋字符串结束符
printf("%s\n",str);
fclose(fp); //关闭文件
free(str); //释放申请的内存
return 0;
}