1.本章学习总结(2分)

1.1 学习内容总结

结构体定义

  • 一般形式:
struct 结构名
{
  类型名 结构成员名1;
  类型名 结构成员名2;
  类型名 结构成员名3;
  ......
  类型名 结构成员名n;
};
  • 注意事项:
    • 结构体以“:”结束,在C语言中,结构体定义被视作一个语句
    • struct 和它后面的结构名一起组成了一个新的类型名
    • 结构体所占的空间大小是所有成员所占大小的总和

成员定义

  • 单独定义:即先定义一个结构类型,再定义这种结构类型的变量
    比如:
    • 先定义:
    struct student
    {
      char name;
      int score;
    };
    
    • 再定义:struct student s1;其中,s1存放的就是学生的名字和成绩。
    • 注意:关键词struct和结果名student必须联合使用,因为他们合起来才能表示一个数据类项名。
  • 混合定义:即在定义结构类型的同时定义结构变量
    • 一般形式:
    struct 结构名
    {
      类型名 结构成员名1;
      类型名 结构成员名2;
      类型名 结构成员名3;
      ......
      类型名 结构成员名n;
    }结构变量名表;
    
    • 比如:
    struct student
    {
      char name;
      int score;
    }s1,s2;
    
  • 无类项名定义:定义结构变量时省略结构名
    • 一般形式:
    struct
    {
      类型名 结构成员名1;
      类型名 结构成员名2;
      类型名 结构成员名3;
      ......
      类型名 结构成员名n;
    }结构变量名表;
    

成员赋值

比如,有下列结构体定义:

struct student
{
 int name;
 int score; 
}s1,s2;

  • 对成员的引用
s1.score=90;
strcpy(s1.name,"xi");
  • 如果两个结构变量类型相同,允许直接把一个变量赋给另一个。如s1=s2;

结构体数组排序做法

  • 选择排序
    设有n组数据
for 0 to n-1
  min=i;
  for i+1 to n
    if 比较数据
       更换位置
    end if
  end for
end for
  • 冒泡排序
for 1 to n//循环数为i
   for 0 to i-1
     if 比较数据大小 
         对换位置
     end if
   end for
end for

结构体指针怎么用

  • 比如,有如下定义:
      typedefstruct 
     {
        char name[10];
        int score;
     }STU;
     STU s1={"xi",99};
     STU *p;
     p=&s1;
    
    可以用它访问结构体成员:
    (*p).score=100;
    p->score=100;
    

共用体、枚举类型做法

  • 共用体:使几个不同的变量共占同一段内存的结构,称为“共用体”类型的结构。
    • 一般形式:
       union   共用体名
       { 
          成员表列;
       }变量表列;
      
    • 注意:与结构体不同的是,共用体类型变量所占内存长度等于最长的成员的长度。它常常用于节省内存。
  • 枚举:若一个变量只有几种可能的值,可以定义为枚举类型。
    • 定义方法:与结构体类似,把结构体中的struct换为enum即可。(同样可以用typedef定义)

文件读写,文件中数据如何读进结构体数组

  • 关于文件的读写(文件打开)
定义文件指针FILE *fp
打开文件:fp=fopen("文件名","r");
关闭文件:fcolse(fp);
- 打开方式:(文本文件)

       |   方式  |   含义   |
       |   "r"   |  打开只读文件     |
       |   "w"   |  建立只写新文件   |
       |   "a"   |  打开添加写文件   |
       |   "r+"  |  打开读/写文件     |
       |   "w+"  |  建立读/写新文件     |
       |   "a+"  |  打开读/写新文件     |
- 注:a和w的不同是,在已有文件存在的情况下,w会删去文件原有内容再写入,而a是直接接在文件内容后写入。
  • feof(文件指针):判断文件指针是否已经到了文件的末尾,是则返回1,否则返回0
  • 读写函数:
    • 字符读写函数: fgetc(文件指针)fputc(字符,文件指针)
    • 格式化读写函数:fscanf(文件指针,格式字符串,输入表)fprintf(文件指针,格式字符串,输出表)
    • 字符串读写函数:fputs(字符串,文件指针)fgets(字符数组名或字符指针,指定的读入个数,文件指针)
    • 二进制读写函数:fread (指针,数据块的字节数,要读写的数据块块数,文件指针)fwrite(指针,数据块的字节数,要读写的数据块块数,文件指针)
  • 其他函数
    • rewind(文件指针)定位文件指针
    • fseek(文件指针,移动偏移量,起始位置)移动指针
    • ftell(文件指针)获取当前指针相较于文件开头的位置(位移量)

1.2 本章学习体会

学习了结构体和文件之后,虽然说,感觉有点难,但是,学了这个之后,确实感觉,思路会比较清晰,而且主函数部分不会很繁杂。但是我在用到那些结构体指针的时候,很容易用错,虽然平常好像都明白哪个是指的哪里,但是在实际用到的时候,会有一点晕。还是那句话,熟能生巧。临近期末,事情比较多,因而代码量有所减少,orz。不仅仅是难度的问题,可能还有我因为难度而失去那种积极性的问题。但说实在的,解决掉所有的问题后,得到正确代码的时候,心情真的是无比愉悦的。
大作业:424(可能会有补充,就不再记录了)

2.综合作业--“我爱成语”(8分)

  • 本次作业要编写一个成语游戏。用户登录后,系统随机出若干个成语,如果答对则得分。用户得分写入成绩文件。系统能对用户成绩排名
  • 主要功能:
    • 登陆。需要输入正确用户名和 密码,才能登陆系统。错误提示错误的信息。
    • 成语游戏。从成语文件中随机抽一个成语,随机展示2个汉字,用户猜剩余汉字。如果答对,则得分。错误,给相应信息。
    • 排名。系统跟用户得分排名。
    • 退出
  • 要求:
    • 本次作业必须建工程实现C的多文件。其中结构体定义、函数定义等都要封装在自定义头文件中。一个模块对应一个.h文件和.c文件。数据定义和数据实现分离。
    • 用户登录、排名信息的数据请封装结构体实现。
    • 涉及3个数据文件:user.txt、ranking.txt、idiom.txt
    • 尽量减少全局变量使用,数据传递通过地址、参数等传递。学会利用函数形参、返回值实现独立功能。
    • 菜单界面友好,任何一步都能随时推出,程序也 不会崩溃。程序界面要有清屏。
    • 尽量写一个函数调试一个函数,保证函数能读写文件数据再做下面内容。
  • 数据说明:
    • user.txt :用户名、密码
    • ranking.txt:用户名,答题总分数、最后答题时间。注意:答题分数是每次分数的积累,如答对一题5分,下一次同一个用户名再玩,这里数据能累加或更新。
    • idiom.txt:成语、成语解释。注意:给大家数据还包含数字,建议对数据预处理下,把数据改造成规范格式。成语:成语解释。出现其他如【】、数字等都过滤掉。数据统一格式才好读写。
  • 扩展功能
    本题可以做一个成语字典,用户输入成语,可以查询相应成语解释。这个查询可以是模糊查询,就是用户只输入一个汉字也可以查询所有相关的成语内容。
    游戏玩法可以多样,比如也可以做成语接龙,你写一个成语,根据最后一个汉字,系统能接龙下一个成语。如果是人机互玩,不是接龙规则,可提示

2.1.文件介绍

头文件介绍

头文件main.h

  • 介绍:
包含内容 功能
结构体变量USER 结构体的成员有name和password,用于存放用户的用户名和密码
int users() 包含了注册及登录的函数,用户可根据自身情况选择注册或是登录
void Logon(USER* existingUser, FILE* fp1,int num) 这个函数用于注册,将用户名和密码存入文件users中
int LoginDetection(USER* existingUser,int num) 这个函数用于检验登录账户,判断用户名及密码是否匹配
  • 头文件代码截图

头文件function.h

  • 介绍:
包含内容 功能
结构体变量IDIOM 结构体的成员有idiom和meaning,用于存放成语及其意思
void Idiom() 总的函数,包含多种功能
void Logon(USER* existingUser, FILE* fp1,int num) 这个函数用于注册,将用户名和密码存入文件users中
int LoginDetection(USER* existingUser,int num) 这个函数用于检验登录账户,判断用户名及密码是否匹配
void MENU() 主菜单,创立友好界面
int FindIdioms(IDIOM* idioms, int number) 查找成语并输出它的意思
int OutputIdioms(IDIOM* idioms, int number) 输出所有的成语
void AddingIdioms(IDIOM* idioms, FILE* fp) 手动添加成语
int storageIdioms(IDIOM* idioms, FILE* fp) 将成语存入结构体中
void GetIdiom(IDIOM* idioms, int number) 出题
  • 头文件代码截图:

2.函数实现文件介绍。

文件1.成语游戏.cpp

  • 文件功能:主函数,调用其他函数
  • 设计思路:逐个调用
定义flag用于确定账户是否刚刚注册
while 1
if 账户是刚刚注册
   continue;
end if
调用函数Idiom()
if 不是刚刚注册的账号
   跳出循环
end if
  • 主要代码截图

文件2.Menu.cpp

  • 文件功能:使界面友好
  • 设计思路:逐个输出即可
  • 主要代码截图

文件3.AddIdiomAndFindIdioms.cpp

  • 文件功能:手动添加成语及其意思以及查找成语
  • 设计思路:
    void AddingIdioms(IDIOM* idioms, FILE* fp)/添加成语/
定义idiomStr存放成语;meaningStr存放意思
让文件指针移到文件尾
用户输入
写入文件中

int FindIdioms(IDIOM* idioms, int number)/查找成语/

定义findTarget[100]存需要寻找的成语;i用于循环;结构体指针ptr;
ptr = idioms;
用户输入寻找的成语
for  0 to number
    if 找到
       输出该成语及意思
       返回1
    end if
end for
没找到,返回0
  • 主要代码截图:

文件4:GetIdiom.cpp

  • 文件功能:出题
  • 设计思路:
定义response为用户输入的答案;answer为正确答案;choice用于决定是否继续答题;kind决定成语的题型;
position存放所取的成语的位置;p为结构体变量;i和j用于循环;flag用于判断答题正确与否;
while 1
  取随机数,赋给kind和position;
  for 0 to position
      p++;
  end for
  switch kind
      5种不同的挖空方式
  end switch
  用户输入作答
  判断答案是否正确
  输出相应的句子
  用户选择是否继续答题
  选否,跳出循环
  system("pause");
  system("cls");
end while

  • 主要代码截图:


文件5.idiom.cpp

  • 文件功能:包含各个功能函数
  • 设计思路:
定义choose存用户选择的功能;flag标记查找的结果;add为是否添加成语的选择;number记录成语个数;idioms为结构体数组;fp为文件指针;
打开文件
if 打开失败		
exit(0);
调用函数将成语存入结构体中
while (1)
   system("pause");
   system("cls");//清屏函数
   调用函数显示主菜单
   用户输入选择choose
   if 选择0
	break;
   end if
   if 选择1
        调用函数查找成语
        if 没找到
           输出相关语句
           用户选择是否手动添加成语
           if 选择添加	
               调用相关函数
           end if
        end if
    end if
    if 选择2
       调用出题函数出题
    end if
    if 选择3
       调用函数输出成语
    end if
    关闭文件
  • 主要代码截图

文件6.storage.cpp

  • 文件功能:把文件中的成语及其意思存到结构体中,输出文件内所有成语及意思
  • 设计思路:
    int storageIdioms(IDIOM* idioms, FILE* fp)/将成语存入结构体中/
定义idiomString存放每一个成语及其意思;locPtr为冒号的指针;loc为冒号距离成语首地址的下标差;
n存成语个数;i和j为循环数;p为结构体变量;
初始化各变量
while 文件未结束

i = 0;
将第一个成语及它的意思存入字符串idiomString中
找到:所在位置
if 冒号存在
loc = locPtr - idiomString;
end if
while 未到冒号
     将成语存入结构体的第一个成员并在末尾加上结束符
     将意思存入结构体的另一个成员并在末尾加上结束符
     p++;
     n++;
end while
返回成语总数n

int OutputIdioms(IDIOM* idioms, int number)/输出成语/

定义i为循环数;为结构体指针p;
p = idioms;
for 0 to number
    输出对应的成语及其意思
end for
return 0;
  • 主要代码截图

文件7.user.cpp

  • 文件功能:包括关于存放、注册和登录的函数,以及具体的注册、登录的函数
  • 设计思路:
    int users()/包含其他函数/
定义choice存用户选择;num为用户总个数;flag为登录结果;fp1为文件指针;existingUser为结构体数组;
打开文件
num = storageUsers(existingUser, fp1);
while 1
    用户输入选择
    while 选择不是1和2
	重复输入选择
    end while
    if 选择1
       调用登录函数
       if 用户名及密码正确
           跳出循环
       end if
       else
           输出提示
           system("pause");
           system("cls");
       end else
    if 选择2
       调用注册函数
       返回1
    end if
end while
不是注册,返回0
关闭文件

int storageUsers(USER* existingUser, FILE* fp1)/将用户存入结构体中/

思路与成语存进结构体的差不多,只是变量名有所更改,就不复述了

void Logon(USER existingUser, FILE fp1,int num)/注册/

定义userName存放用户所取的名字;userPassword为用户所设置的密码;i为循环变量;p为结构体指针;flag用于判断是否已有相同的用户名;
p = existingUser;
将指针移到文件末尾
while 1
   flag = 0;
   用户输入名字
   for 0 to num
       if 在文件中找到相同的名字
            flag = 1;
            跳出循环
       end if
   end for
   if 存在相同的用户名
      输出提示
      system("pause"); 
      system("cls");
   end if
   else
      跳出循环
   end else
end while
用户设置密码
写入文件中

int LoginDetection(USER* exitingUser,int num)/登录检验/

定义userName存用户输入的名字;userPassword为用户密码;i为循环变量;p为结构体指针;
p = exitingUser;
用户输入用户名、密码
for 0 to num
    if 在文件中找到用户名
         if 对应密码相等
	     输出提示词
              返回1
         end if
	 else
	      返回0
         end else
    end if
end for
遍历意味着没有找到,返回0
  • 主要代码截图





2.2.运行结果

1.登录界面


2.游戏界面



2.3大作业总结

1.碰到问题及解决办法

  • 我遇到了几次访问冲突,一个大大的❌,因为我之前用的文件比较小,后来换了数据大的文件,原先设置的结构体数组不够,就发生了异常。这种情况下,可以把数组改大,也可以,把文件换小。
  • 相同用户名的重复注册,导致登录检测错误:我给他加上了一个用户名的检测,如果注册时用户名重复,则提示用户更改用户名,并加了一个循环直至用户输入的注册名与文件中已有的用户名不再重叠。
  • 在登录页面,如果之前没有账号而误选择登录的,无法返回登录页面:我检验了登录,如果该用户不存在,则重新选择登录或是注册,用while循环来控制。
  • 在使用清屏函数时,原意是想要输出一个东西后,再清屏,但是加在输出的东西后面,它直接执行了清屏的命令,就导致输出的东西一闪而过,根本看不清输出的是个啥:然后我就get到了一个神奇的函数system("pause");,它的存在,使得用户可以在确认继续后清屏。不得不说,清屏函数真的是个很棒的东西,有了它,界面就不会显得很乱很繁杂。👍

2.小结

  • 这次大作业,其实还是比较有趣的。我尝试用了两个比较有趣的函数,一个是清屏函数,一个是更换颜色的函数。在写的过程,就感觉,原来我们平常的注册和登录是这么回事儿啊,有一种自己好像也变厉害的错觉。当然,我的这个页面还是很简陋,希望再过段时间,我能够学会类似更换背景为图片、设计漂亮的页面这种看上去感觉很厉害的东西。说白了,我就是想让我的界面变得漂亮点儿。还是继续加油吧!