c博客06-2019-结构体&文件
1.本章学习总结
1.1 学习内容总结
整理结构体和文件这两章主要知识点,必须包含内容有:
1.结构体如何定义、成员如何赋值
- 结构体一般形式
struct 结构体名
{
数据类型 成员名1;
数据类型 成员名2;
:
数据类型 成员名n;
};
- 结构体的定义及赋值
- 先定义结构体类型再定义变量名,这是C语言中定义结构体类型变量最常见的方式。
struct 结构体名
{
成员列表;
};
struct 结构体名 变量名;
- 在定义类型的同时定义变量。这种形式的定义的一般形式为:
struct 结构体名
{
成员列表;
}变量名;
- 直接定义结构类型变量。其一般形式为:
struct //没有结构体名
{
成员列表;
}变量名;
2.结构体数组排序做法
- 结构体数组的排序方法与之前数组的排序方法差别并不大,这里列举一道学生信息按成绩排序的冒泡排序
/*
name:学生信息结构体 按总分排序 (降序)
*/
#include<stdio.h>
typedef struct {//结构体 定义时尽量放main()外面
int grade;
int age;
char name[10];
}Student;
int main()
{
int i, j;
Student temp;
Student student[3] = {/*分成多条初始化,更清晰*/
{600,18,"小明"},
{550,19,"小红"},
{700,20,"tom"},
};
/*用结构体指针---提高效率*/
/*冒泡排序*/
Student* p = student;
for (i = 1; i <= 2; i++)
{
for (j = 0; j <= 3 - i - 1; j++)//大-->小
{
if ((p + j)->grade < (p + j + 1)->grade)
{
temp = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = temp;//注意 temp 的类型为Student
}
}
}
/*以表的形式输出*/
printf("姓名 年龄 分数 \n\n");//表头
for (i = 0; i <= 2; i++)
{
printf("%-4s %4d %4d\n", (p + i)->name, (p + i)->age, (p + i)->grade);
}
getchar();
return 0;
}
3.结构体指针怎么用
在C语言使用中,随着知识的深入,我们结构体用到的机会很多。
- 首先让我们定义结构体:
struct stu
{
char name[20];
long number;
float score[4];
};
- 再定义指向结构体类型变量的指针变量:
struct stu *p1, *p2 ;
- 定义指针变量p 1、p 2,分别指向结构体类型变量。引用形式为:指针变量→成员;
#include<stdio.h>
#include <stdlib.h> //使用m a l l o c ( ) 需要
struct data //定义结构体
{
int day, month, year;
};
struct stu //定义结构体
{
char name[20];
long num;
struct data birthday; //嵌套的结构体类型成员
};
int main() //定义m a i n ( ) 函数
{
struct stu* student; //定义结构体类型指针
student = malloc(sizeof(struct stu)); //为指针变量分配安全的地址
printf("Input name,number,year,month,day:/n");
scanf("%s", student->name); //输入学生姓名、学号、出生年月日
scanf("%ld", &student->num);
scanf("%d%d%d", &student->birthday.year, &student->birthday.month,
&student->birthday.day);
printf("/nOutputname,number,year,month,day/n");
/*打印输出各成员项的值*/
printf("%20s%10ld%10d//%d//%d/n", student->name, student->num,
student->birthday.year, student->birthday.month,
student->birthday.day);
}
4.共用体、枚举类型做法
共用体
- 共用体是一种多变量共享存储空间的构造类型,它允许几种不同的变量共用同一存储空间。
- 共用体和结构体的区别:
- 1.结构体每一位成员都用来表示一种具体事务的属性,共用体成员可以表示多种属性(同一存储空间可以存储不同类型的数据)。
- 2.结构体总空间大小,等于各成员总长度,共用体空间等于最大成员占据的空间。
- 3.共用体不能赋初值。
枚举类型
- 枚举类型是指变量的值可以全部列出,定义一个枚举变量后,变量的值确定在定义之中。
- 它和结构体、共用体的区别在于,枚举元素是常量,只能在定义阶段赋值。
5.文件读写,文件中数据如何读进结构体数组
- 对文件的读和写是最常用的文件操作。在C语言中提供了多种文件读写的函数:
- 字符读写函数 :fgetc和fputc
- 字符串读写函数:fgets和fputs
- 数据块读写函数:freed和fwrite
- 格式化读写函数:fscanf和fprinf
- 打开文件函数为fopen(),具体形式为
fopen(“文件名”,“文件打开方式”);
- 文件打开方式,具体有
r 打开只读文件
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留
r+ 打开可读写的文件
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留
1.2 本章学习体会
学习感受
学到现在,新的东西越来越多,平时觉得缺漏并不多,但是一到大作业时才发现自己的不足之处还有很多。这个学期很快就要结束了,我们的C语言课程也要迎来结束,在这个时候,查缺补漏才更为重要。在这两周学习的文件与结构体中,一开始确实感觉很难,但是运用时候,发现函数真的变得简洁了很多,尤其是多文件编程,好几百行代码在一个文件中的情况载用不会出现了。但是在结构体的运用中,尤其结构体数组和指针,老师讲的时候感觉指向的地址都很明了,但是自己在写的时候经常出错,果然还是练习的不够多...到期末了,各种各样的考试、总结和复习接踵而至,这两周对代码量也有所疏忽,希望能尽快赶上!
2.综合作业--“我爱成语”(8分)
本次作业要编写一个成语游戏。用户登录后,系统随机出若干个成语,如果答对则得分。用户得分写入成绩文件。系统能对用户成绩排名
主要功能:
- 登陆:需要输入正确用户名和 密码,才能登陆系统。错误提示错误的信息。
- 成语游戏:从成语文件中随机抽一个成语,随机展示2个汉字,用户猜剩余汉字。如果答对,则得分。错误,给相应信息。
- 排名:系统跟用户得分排名。
- 退出。
要求:
- 本次作业必须建工程实现C的多文件。其中结构体定义、函数定义等都要封装在自定义头文件中。一个模块对应一个.h文件和.c文件。数据定义和数据实现分离。
- 用户登录、排名信息的数据请封装结构体实现。
- 涉及3个数据文件:user.txt、ranking.txt、idiom.txt
- 尽量减少全局变量使用,数据传递通过地址、参数等传递。学会利用函数形参、返回值实现独立功能。
- 菜单界面友好,任何一步都能随时推出,程序也 不会崩溃。程序界面要有清屏。
- 尽量写一个函数调试一个函数,保证函数能读写文件数据再做下面内容。
数据说明:
- user.txt :用户名、密码
- ranking.txt:用户名,答题总分数、最后答题时间。注意:答题分数是每次分数的积累,如答对一题5分,下一次同一个用户名再玩,这里数据能累加或更新。
- idiom.txt:成语、成语解释。注意:给大家数据还包含数字,建议对数据预处理下,把数据改造成规范格式。成语:成语解释。出现其他如【】、数字等都过滤掉。数据统一格式才好读写。
扩展功能
- 本题可以做一个成语字典,用户输入成语,可以查询相应成语解释。这个查询可以是模糊查询,就是用户只输入一个汉字也可以查询所有相关的成语内容。
- 游戏玩法可以多样,比如也可以做成语接龙,你写一个成语,根据最后一个汉字,系统能接龙下一个成语。如果是人机互玩,不是接龙规则,可提示。
2.1.文件介绍
头文件介绍
- 头文件1:idiom.h
功能介绍:定义了多个函数;IDIOM结构体:用于读取成语内容并储存;USER结构体:用于读取登录所需的账号与密码并储存。
2.函数实现文件介绍。
- 文件1:main.c。
功能:令程序正常进行
思路:在设计时,想主函数尽可能的简洁,因此大部分功能都使用了函数进行单独封装。
- 文件2:users.c
功能:读取并储存账号与密码,实现登录功能。
思路:与林丽老师上课讲过的一样,同时借鉴了课堂派上的内容,定义了USER结构体来实现登录系统。
- 文件3:Change.c
功能:作为连接系统,打开并读取成语文件,是竟然两个主要功能的连接口,同时也可以有切换登录用户的功能。
思路:因为老师说过一般打开文件都在主函数中打开,避免多次开关文件导致运行时间过长,因此一开始是把该函数的内容放在主函数中,但是后来为了实现切换登录用户的功能,写了这样一个函数。(个人能力有限)
- 文件4:GetIdiomsData.c
功能:最关键的将成语文件的成语内容与注释内容分开储存,并返回成语的个数number。
思路:定义了IDIOM结构体数组来进行分别储存成语名与注释。
- 文件5:Issue.c
功能:出题系统,进行成语随机挖去两个空出题。同时在函数末尾进行一个继续或返回的选择。
思路:在一开始进行写代码时,想通过遍历来随机出题,但是代码太长而且极易崩溃,让我挺头疼的。后来借鉴了同学的思路与代码,直接运用随机数来进行选择输出的成语和抹除的两个位置,同时运用复制函数,也避免了再重新定义一个数组。
- 文件6:Search.c
功能:查找系统,当用户输入文件中有的成语,输出该成语的意思。
思路:因为成语名和注释已经分别储存了,且对应的成语和注释下标一样,所以只要利用循环遍历进行寻找即可。
- 文件7:ListMenu.c
功能:菜单函数
思路:对用户进行提醒
- 文件8:Choice.c
功能:输入选择的功能并返回输入的选项choose
思路:通过输入来进入不同的功能。
- 文件9:End.c
功能:结束函数,代表程序的结束
思路:当用户选择退出时可以跳出,最后来到这个函数
2.2.运行结果
1.登录界面
2.游戏界面
3.查找界面
2.3大作业总结
1.碰到问题及解决办法
- 首先是在出题函数,这个函数几乎花掉了一整个晚上的时间来写,删了许多次。一开始是定义了一共新的指针,令它直接等于随机出来的成语名,结果发现抹去字符后新定义的指针也一起被改变了,后来改用了复制函数才解决。其次是我之后删去了新指针,而是用了idiom[num + 2].IdiomName来储存。这样做其实是有弊端的,因为我事先知道我定义的数组内容远大于成语个数num,所以idiom[num + 2].IdiomName中其实是没有内容的,当时为了节约时间,也觉得这样可以避免重新定义指针占用更多的空间,就这么定义了。
- 这次第一次使用清屏函数,刚刚开始老是用错地方,把有用的内容清掉。后来还是要一次一次调试慢慢改过来。
- 这次大量使用了while(1)循环,没有写好条件容易造成死循环,每次写完都要经过多次调试才能确保没有问题。
- 在读取文件内容时,因为不是操作不是很熟练,好多次没有加终止符号,再加上之前没有调好的while(1)循环,结果跳出来满屏的烫,吓了一跳,后来发现是在分离储存成语与注释的时候,在成语名赋值结束的末尾忘记加了。
- 在写了很多次排名函数后都出现了运行错误或者引发异常的情况,不知道怎么处理,所以后面就先删去了排名函数,先完成其他功能。但是到现在还行没写出来,后续应该会慢慢地完善功能。
2.小结
- 一开始布置大作业时真的是不知从何下手。一共两周的时间,前一周基本都在构思作业思路,陆陆续续写了一些代码,这一周在林丽老师又复习过一次文件,已经机房课的指导后逐渐有了一些想法。但这周又碰到英语的4级考试 ,所以之前一直都是只完成了框架,大部分的内容是在这两天写出来的。
- 个人感觉,相对于上一次,这一次写大作业的感觉要好得多,对于函数的运用也熟练了起来。但是对于新学习的知识,比如文件的运用,结构体的运用等还是不够熟练。(果然还是能力限制了我的发挥...)
- 个人在运用指针的时候还是很生疏,以后还是要多练习指针内容。
- 这一次对于一些新内容,比如清屏函数的运用多了起来。