1.本章学习总结
1.1 学习内容总结
1.结构体如何定义、成员如何赋值
1.结构体的定义
- 一般形式:
struct 结构名{
类型名 结构名成员1;
类型名 结构名成员2'
···
类型名 结构名成员n;
};
- 结构的定义以分号结束,因为C语言中把结构定义看做一条语句
- 关键字struct和结构名必须联合使用,因为它们合起来表示一个数据类型名。
- 结构的嵌套定义:
/*设置结构体保存学生的学号,姓名,通行地址,以及计算机,英语,数学和平均成绩,其中通行地址包括,居住的城市,街道,门牌号,邮编*/
struct address {
char city[10];
char street[20];
int code;
int zip;
};
struct nest_student{
int num;
char name[10];
struct address addr;
int computer,english,math;
double auerage;
};
注意:在注意嵌套的结构类型时,必须先定义成员的结构类型,在定义主结构类型。
- 运用typedef语句
2.结构体成员的赋值
- 在c语言中,使用结构成员操作符"."来引用结构成员,格式为
结构变量名.结构成员名
。
/*对学生的信息进行赋值*/
struct student{
int num;
char name[10];
int computer,english,math;
double average;
};
struct student s1,s2;
/*分别对s1的每个结构体成员赋值;*/
scanf("%d %s %d %d %d",&s1.num,s1.name,&s1.computer,&s1.english,&s1.math);
s1.average=(s1.computer+s1.math+s1.english)/3.0;
/*将s1中各个结构体成员的值赋给s2*/
s2.num=s1.num;
strcpy(s2.name,s1.name); //这里注意,对字符串的赋值,不可以直接写“s2.name=s1.name”,这样的写法是错误的,要用库函数strcpy()!
s2.math=s1.math;
s2.computer=s1.computer;
s2.english=s1.english;
s2.average=s1.average;
/*对于将两个相同类型的结构变量,把一个结构变量赋给另外一个结构变量,可以直接整体赋值,这样和分别对结构体成员的赋值的操作等效*/
s2=s1;
- 定义结构指针变量,使用指向运算符->访问指针指向的结构成员,以此来给每个结构成员赋值;
struct student *p;
/*使结构指针变量p指向s1,从键盘上读取数据赋给p指向的s1*/
p=&s1;
scanf("%d %s %d %d %d",&p->num,p->name,&p->computer,&p->english,&p->math);
p->average=(p->computer+p->english+p->math)/3.0;
/*使结构指针变量p指向s2,再把s1中各个结构体成员的值赋给s2*/
p=&s2;
p->num=s1.num;
strcpy(p->name,s1.name);
p->math=s1.math;
p->computer=s1.computer;
p->english=s1.english;
p->average=s1.average;
2.结构体数组排序做法
1.选择排序
/*从大到小排序*/
struct student students[n];
struct student temp;
for i=0 to n-1//外循环
for j=0 to n//内循环
if(students[j]>student[i])//如果后面的数据比前面大,交换数据;
temp=students[i];
student[i]=student[j];
student[j]=temp;
end if
end for
end for
2.冒泡排序法
struct student students[n];
struct student temp;
for i=1 to n
for j=0 to n-i
if(students[j+1]>student[j])
temp=students[j];
student[j]=student[j+1];
student[j+1]=temp;
end if
end for
end for
运行结果:
3.结构体指针
1.概念
- 就是指向结构类型变量的指针。
- 一般形式
struct 结构名 * 指针变量名
2.用结构指针访问结构成员
struct student *p;
p=&s1;
3.结构指针作为函数参数
将学生的信息按平均分排名
虽然结构变量也可以做为函数参数,但是当结构成员数据很多时,在参数传递时过程就需要消耗很多空间,而使用结构指针作为函数参数只要传递一个地址值,可以提高传参效率。
4.共用体、枚举类型做法
1.共用体
- 一般声明形式:
union 联合名
{
成员声明
成员声明
···
}变量列表
2.枚举
- 一般声明形式:
enum 枚举名 {枚举值1,枚举值2,···}变量列表;
- 每个枚举值应该是一个合法的标识符,或者是一个标识符后面跟上一个等号,再加上一个常量表达式。
- 编译程序将从0开始逐个给枚举值赋值,如果某个枚举值标识符后面跟有等号和常量表达式,那么编译程序就将该常量表达式的值作为该枚举值的值,该枚举值后面的枚举值从这个枚举值开始逐个加1,重新编号。
5.文件和缓冲系统
- 1.文本文件和二进制文件
- 数据文件可分为文本文件和二进制文件。
- 文本文件是以字符ASCII码值进行存储与编码的文件,文件内容就是字符,可通过“记事本”等编辑工具来对文件内容进行查看,修改等。
- 二进制文件就是存储二进制数据的文件,它包含的是计算机才能识别的机器代码,如果用编辑器工具打开,就会看到一堆乱码,因为无法用编译器工具打开,所以二进制文件比文本文件更安全。
- 2.缓冲文件系统
- 程序与文件的数据交换药通过内存缓冲区来进行,根据这种文件缓冲的特性,把文件系统分为缓冲文件系统与非缓冲文件系统。
- 对缓冲文件系统,在进行文件操作时,系统自动为每一个文件分配一块文件内存缓冲区(内存单元),C程序对文件的所有操作就通过对文件缓冲区的操作来完成。缓冲区文件系统的大小是由具体的C语言版本决定。
- 对非缓冲文件系统,文件缓冲区不是由系统自动分配的,而需要程序员在程序中用c语言实现分配。
6.文件结构与文件类型指针
- 1.文件结构
- FILE是一个结构类型,用typedef语句来进行命名的,typedef语句在头文件stdio.h中定义,因此使用文件的程序都要
#include
- FILE是一个结构类型,用typedef语句来进行命名的,typedef语句在头文件stdio.h中定义,因此使用文件的程序都要
- 2.文件类型指针
- 一般形式:
FILE * fp
- FILE是文件类型定义符,fp是文件类型的指针变量。
- 这里的文件指针fp做自增的话,比如++fp,表示的是fp指向下一个FILE结构(如果存在),而不是指向文件中的下一个数据
- 一般形式:
7.打开文件和关闭文件
1.打开文件
- 一般调用形式为
fopen("文件名","文件打开方式");
,这里的文件名要指出对哪个具体文件进行操作,一般要指定文件的路径,如果不写出路径在,则默认与应用程序的当前路径相同。文件路径若包含绝对完整路径,则定位于目录用的斜杆“”需要用“\”,因为''在c语言中表示转义符,"\"表示实际的""。 - fopen函数有返回值。如果执行成功,函数将返回包含文件缓冲区等信息的FILE结构地址,赋给文件指针fp,否则返回一个NULL(空值)的FILE指针。
- 在调用fopen函数时最好做一个判断,以确保文件正常打开后再进行读写。
if ((fp=fopen("abc.txt","r"))==NULL)
{
printf("File open error!");
exit(0);//exit(0)是系统标准函数,作用是关闭所有打开的文件,并终止程序的执行。
}
- 文件打开方式:
- 文本文件(ASCII)
打开方式 | 含义 |
---|---|
"r" | 打开文本文件进行只读,该文件必须存在 |
"w" | 建立新文本文件进行只写 |
"a" | 打开文本文件进行追加,如果该文件不存在就新建一个文件 |
"r+" | 打开文件进行读/写,对文件进行写时会把文件中的内容覆盖,该文件必须存在。 |
"w+" | 建立新文件进行读/写,如果该文件存在就会把原来的文件删除,再建一个新的文件 |
"a+" | 打开文本文件进行读/写/追加,如果该文件不存在,就新建一个文件 |
- 二进制文件打开的操作和文本文件一样,只不过打开方式多了一个字符“b”。
2.关闭文件
- 一般形式:
fclose(文件指针);
- 若该数为0表示正常关闭文件,否则表示无法正常关闭文件,所以关闭文件也应该使用条件判断:
if(fclose(fp))
{
printf("Can not close the file!\n");
exit(0);
}
8.文件读写,文件中数据如何读进结构体数组
- C语言标库stdio.h中提供了一系列文件的读写操作函数:
函数 | 调用格式 | 功能 |
---|---|---|
fgetc() | fgetc(FILE*fp) | 从fp指向的文件中得到一个字符 |
fputc() | fputc(char ch ,FILE*fp) | 把字符ch输入到fp指向的文件中去,成功的话返回ch,失败则返回EOF(在头文件stdio.h中有说明其值为-1) |
fgets() | fgets(charp,n,FILEfp) | 从fp指向的文件中获取n-1个字符赋给指针p所指向的字符串,昂函数读取的字符达到指定的个数,或者接收到换行符,或接收到文件结束标志EOF时,在读取的字符后面自动添加一个'\0'字符,如果读取成功则返回字符串,否则返回空指针,此时字符串的内容不确定 |
fputs() | fputs(charp,FILEfp) | 把p指向的字符串输到fp指向的文件中去 |
fscanf() | fscanf(文件指针,格式字符串,输入表) | 从文件中按照给定的控制格式读取数据 |
fprintf() | fprintf(文件指针,格式字符串,输出表) | 从文件中按照给定的控制格式读取数据保存到变量 |
fread() | fread(buffer,size,count,fp) | 从二进制文件中连续读入count个数据块(size个字节)到buffer所指向的变量中 |
fwrite() | fwrite(buffer,size,count,fp) | 向二进制文件中连续输出入count次buffer所指向的变量中数据块(size个字节) |
- 调用函数feof()可以检测文件指针fp所指文件的位置是否到了文件末尾。该函数在打开文件是文件指针是指向文件首部的,每次调用一个读取文件中的数据的读取函数成功后,文件指针fp会自动向后移动到还未被读取的数据去,如果文件指针指向文件末尾时,则会返回一个无效的字符EOF,因为EOF不是常规的ASCII码,而是一个值为-1的常量,这样可以区分于文件中的字符内容。
9.其他相关函数
和文件有关的其他函数 | 调用格式 | 功能 |
---|---|---|
rewind() | rewind(FILE*fp) | 使文件指针fp指向文件的首地址 |
fseek() | fseek(FILE*fp,long offset,from) | offset表示从当前位置开始向前或者向后偏移的长度,from表示从哪个位置开始计算偏移量,from可取三个值“SEEK_SET,SEEK_CUR,SEEK_END”,分别表示文件首部,当前位置和文件尾部,实际表示的值为0,1,2. |
ftell() | ftell(FILE*fp) | 获取当前位置距文件开头的位移量(字节数),成功则返回位移量,失败时返回-1L |
clearer() | clearer(FILE*fp) | 用来清除出错标志和文件结束标志,使它们为0值 |
1.2本章学习总结
学习体会:c语言学到最后,学习的东西越来越多,虽然这学期我们就要结束c语言的课程,可一写题目才发现自己还有许多东西都还没有学习,很多东西也还一知半解,到了后面综合性越来越强,如果前面有落下的知识应该要及时去补上,否则这部分的内容理解起来就会有些困难。(没错就是我555)
代码量:1890
2.综合作业--“我爱成语”
2.1.文件介绍
头文件介绍
头文件1.idiom.h
- 结构体及功能
结构体 | 功能 |
---|---|
IDIOM | 从idiom.h文件中读取成语及其意思 |
RANK | 从ranking.h文件中读取排名成绩和时间 |
- 函数声明
函数 | 功能 |
---|---|
void Theme() | 主题 |
void Login(char*user) | 登入界面 |
int IsUser(char* name_str, char* password_str) | 判断是否为用户 |
void GetChoice(FILE* fp1, FILE* fp2, char* user) | 得到用户的选择并进入该功能 |
int Game(FILE* fp1, FILE fp2 ,char user) | 成语游戏 |
int GetIdiom(FILE* fp, IDIOM* idioms) | 获得文件idiom.txt中的所有成语 |
void RightSentence() | 夸奖的句子 |
void WorrySentence() | 提示回答错误的句子 |
int Problems(int num,IDIOM *idioms) | 随机得到一个成语,并判断是否正确 |
void PrintProblem(char* idiom) | 随机挖空,输出题目 |
void ArrageRank(FILE* fp, char* user, int right) | 在文件ranking.txt中更新排名 |
void GetTime(RANK*rank_ptr) | 获取当前时间和日期的函数 |
void GetScore(int right, int count) | 计算正确率并告知答题结束 |
int NowTime() | 记录当前时间 |
void GetRank(FILE*fp) | 获取排名 |
int FindIdiom(FILE*fp) | 查找成语 |
int WriteNewIdiom(FILE*fp) | 添加新成语 |
int CheckIdiom(FILE* fp, char* idiom) | 查看文件中是否有相同的成语 |
int IsAgain() | 判断是否要继续的函数 |
2.函数实现文件介绍。
1.idiomMain.cpp
2.List.cpp
- void Theme()
- void PrintLine()
- void Login(char* user)
- int IsUser(char* name, char* password)
- void ChoiceMenu()
- void GetChoice(FILE * fp1,FILE * fp2,char * user)
3.idiom.cpp
- int IsAgain()
- int Game(FILE * fp1,FILE * fp2, char* user)
- int FindIdiom(FILE * fp)
- int CheckIdiom(FILE * fp,char* idiom)
- void GetRank(FILE * fp)
- int WriteNewIdiom(FILE* fp)
3.Game.cpp
- int GetIdiom(FILE* fp, IDIOM* idioms)
- void RightSentence()
- void WorrySentence()
- int Problems(int num,IDIOM * idioms)
- void PrintProblem(char* problem)
- int NowTime()
- void ArrageRank(FILE* fp, char* user, int right)
- void GetTime(RANK* rank_ptr)
- void GetScore(int right, int count)
2.2.运行结果
2.3大作业总结
Q1:首先是多个函数都使用了文件指针,如果某个函数的文件指针已经指到文件尾部了,下一个函数调用的时候就会出问题,
A1:所以在所有调用文件指针时,都使用fseek(fp,0,0)使得指针指向文件头/尾。
Q2:刚开始对题目的输出处理,总是无法输出正常的汉字。
A2:单个汉字占两个字节,如果想要输出一个汉字只使用一个%c或者两个%c是不可以实现的,应该要把单个汉字作为字符串处理,于是我利用了printf("%.*s",problem+2);来控制输出的汉字个数;
Q3:对文件中排名的处理,不知道如何获取当前的日期和时间,本以为c语言的内容已经学习了很多了,没想到除了上课学习的还有许多需要我们去拓展的。~~革命还尚未成功啊啊~~
A3:我上网去查找了,知道了函数localtime()将从time()函数中获得的从1970年1月1日到现在返回的秒数转换为当前的年,月,份,时,分,秒形式;但是实际年份还要加上1900,月份要加上1;
Q4:多个文件函数之间的传参比单个文件函数的传参的难度要大太多了,写到后面发现需要前面的参数,然后去修修改改真的真的好难TAT
A4:要写一个框架,标出每个功能需要的参数,整理好了再敲代码!!