分类

教育学习

c数据结构课程设计案例

c数据结构课程设计案例

大小:266 KB

语言:简体中文系统:WinXP, Win7, Win8, Win10, WinAll

类别:教育学习时间:2016-06-14

  我们设计的程序有三个,分别是:航空订票系统、24点游戏、旅游交通查询系统,为了用户的方便和更能体现C语言的模块化理念,我们把三个程序放到一个系统中去实现了。

c数据结构课程设计案例

设计内容

  1、需求分析:

  在完成课程设计的过程中,我们组合作为主,欧阳锦林主要负责程序设计与调试,王峰和段静缘主要负责资料收集与文档输入。设计完成后交流了各人收获与体会。

  (1)、航空订票系统:

  通过此系统可以实现如下功能:

  1) 录入航线信息

  每条航线信息包括航班号、飞机号、目的地、订票数、余票数共5项。假设现在有3条航线, 目的地分别是北京, 上海, 广州, 飞机上可乘坐100人( 即初始订票数为0, 余票数为100) , 将这3条航线信息存入文件“airline.dat” 中。

  2) 订票业务

  客户信息包括姓名, 航班号, 座位号(初始为0), 假设已有3个客户信息存入文件“customer.dat”中。

  有新客户订票时, 先输入客户的姓名和他提出的航班号, 查询该航线的订票情况, 若有余票, 则为客户办理订票手续, 分配给客户一个座位号, 然后将新客户的信息添加到文件“customer.dat”中, 并修改文件“airline.dat”中该航线的订票数和余票数。若无余票, 则输出客满信息。进一步可实现如果该航班已经无票,可以提供相关可选择航班信息。

  3) 退票业务

  根据客户提出的航班号, 办理退票, 从文件“customer.dat”中删除该客户的信息, 并修改文件“airline.dat”中相应航线的订票数和余票数。

  4) 修改航班信息:当航班信息改变可以修改航班数据文件。

  5) 输出全部航线信息和全部客户信息。

  6) 退出系统。

  (2)、24点游戏:

  基本要求及步骤:

  1) 随机产生四个1-13的数,分别代表13张牌。

  2) 提示玩家输入算式。

  3) 判断玩家输入的表达式是否合法,其中算式中的四个数字只能是程序所给的四个数字,非法则回到1)。

  4) 如果玩家认为这四张牌算不出24点(如:1,1,1,1),可只输入?,程序将判断这四张牌是否能得出24点,如果能,则程序将给出算式,如果不能,说明不能,并回到1)。

  5) 当用户正确输入算式后,用“堆栈来求表达式的值”的原理 求出结果并判断是否为24,得出用户是输是赢的结果。

  6) 询问用户是否继续,是则回到1),否则结束程序。

  (3)、旅游交通查询系统:

  实现功能:火车信息查询、最短路径查询、火车信息编辑、读入修改信息、查看火车信息、查看城市信息。每个功能中又有一些小功能,如火车信息查询中有:按车次查询、按出发地与目的地查询(其中又有最快、最省钱、全部选择)中转站查询、查看火车信息,火车信息编辑又包括:添加火车信息、删除火车信息、查看火车信息、保存火车信息功能。

  2、概要设计:

  (1)、航空订票系统:

  1)、抽象数据类型定义如下(C语言下的):

  typedef struct airline{

  char line_num[8];//航班号

  char plane_num[8];//飞机号

  char end_place[20];//目的的

  int total;//座位总数

  int left;//剩余座位

  struct airline *next;//下一个结点

  }airline;

  typedef struct customer{

  char name[9];//顾客名

  char line_num[8];//航班号

  int seat_num;//座位号

  struct customer *next;//下一个结点

  }customer;

  /******************链表操作模块***********/

  airline *init_airline();

  //初始化链表

  customer * init_customer();

  //初始化链表

  status insert_airline(airline **p,char *line_num,char *plane_num,char *end_place,int total,int left);

  //airline链表插入操作

  //插入航班

  status insert_customer(customer **p,char *name,char *line_num,int seat);

  //customer链表插入操作

  status creat_airline(airline **l);

  //创建airline单链表

  status creat_customer(customer **l);

  //创建customer单链表

  /******************链表操作模块********************/

  2)、其它模块的实现函数声明如下:

  //**********************信息修改****************/

  airline *modefy_airline(airline *l,char *line_num);

  //修改airline链表中的数据

  status delete_airline(airline *h,char *line_num);

  //删除航班

  status delete_customer(customer *h,char *line_num);

  //删除顾客

  status delete_cus(customer *h,airline *l,char *name);

  //顾客退票

  status increase_air(airline *l,char *line_num,char *plane_num,char *end_place,int total);

  //增加航线

  //**********************信息修改*************/

  //*********************文件操作模块**************/

  status save_airline(airline const *l);

  //保存airline.dat

  status save_customer(customer const *l);

  //保存顾客信息 customer.dat

  //*********************文件操作模块**************/

  int changStrInt(char *ch);

  //把字符串转化为整型

  status book(airline const *l,char *line_num,customer *c,char *name);

  //订票

  status print_airline(airline const *l);

  //打印航线信息

  status print_customer(customer const *l);

  //打印顾客信息

  status air_main();

  //执行函数

  (2)、24点游戏:

  1)抽象数据类型定义如下(C语言下的):

  int number[2][4];

  enum

  {

  eNumber = 0, //操作数

  eOperator = 1 //算子

  };

  typedef struct sqlist{

  int bol;//bol is 0 when num_ch is a number;bol is 1 when the num_ch is a oprater

  int num_ch;

  struct sqlist *next;

  }sqlist;

  typedef struct sqstack {

  int *base;

  int *top;

  int stacksize;

  }sqstack;

  /***************链表操作模块****************/

  status init_sq(sqlist *l);//初始化链表

  status insert_sq(sqlist **p,int e,int bl);

  //链表插入操作

  //由于这里要求修改外部指针,因此要用指向指针的指针

  //将插入到链表的末尾

  /***************链表操作模块**************/

  2)、其它模块的实现函数声明如下:

  /*******用栈进行表达式计算模块***********/

  int check(sqlist l);

  //保证输入的数字是给出的四个数字

  int chang(char *s,sqlist *l);

  //将用户的输入转化为单链表

  int Operate(int a,int theta, int b);

  //计算a theta b

  int ReturnOpOrd(char op,char* TestOp);

  //被char precede(char Aop, char Bop)所调用来求优先级

  char precede(char Aop, char Bop);

  //返回优先级

  status initstack(sqstack *s);

  //栈初始化

  int gettop(sqstack *s);

  //取栈顶元素

  status push(sqstack *s,int e);

  //把 e 压栈

  status pop(sqstack *s,int *e);

  //出栈,用e 保存

  int EvaluateExpression(char* MyExpression);

  /***************链表操作模块******************/

  int randomm();//产生四个随机数

  /****************计算机计算模块****************/

  int CalcOneExpress(int expression[][2]);

  int Equal24(int n);

  int CalcArray1(int iNumInput[2][4]);

  // a * b * c * d //7 number

  int CalcArray2(int iNumInput[2][4]);

  // (a * b) * c * d //9 number

  int CalcArray3(int iNumInput[2][4]);

  // (a * b * c) * d //9 number

  int CalcArray4(int iNumInput[2][4]);

  // (a * b * c) * d //9 number

  int CalcArray5(int iNumInput[2][4]);

  // (a * b) * (c * d) //11 number

  int CalcArray6(int iNumInput[2][4]);

  // ((a * b) * c) * d //11 number

  int CalcArray7(int iNumInput[2][4]);

  // (a * b * c) * d //9 number

  int Calc24(int number[2][4]);

  //括号的几种情况

  //1 无括号

  //2 (a b) c d 同a b (c d), 下省略

  //3 (a b c) d

  //4 a (b c) d

  //5 (a b) (c d)

  //6 ((a b) c) d

  //7 (a (b c)) d

  /****************计算机计算模块*****************/

  void game_24_main();

  //24点游戏执行函数

  (3)、旅游交通查询系统:

  1)所需抽象数据类型定义如下(C语言下的):

  typedef struct {

  int day;//天数

  int hour;//小时数

  int minute;//分钟数

  }time_train;

  typedef struct town{

  char name[9];//城镇名称

  time_train arrive;//火车到达时间

  time_train leave;//火车离站时间

  struct town *next_town;//下一个城镇

  }town;

  typedef struct train{

  char train_num[8];//火车序列号

  char start_place[9];//始发地

  char end_place[9];//终点站

  int fare;//费用

  int hour;//时间(用小时计算)

  struct train *next_train;//指向下一辆列车

  struct town *next_town;//指向下一个城镇

  }train;

  typedef struct{

  int length;//长度

  // int hour;//时间

  // int fare;//费用

  int ivex,jvex;//边的两端顶点号

  }path,*path_p;//路径类型

  typedef struct path_node{//边结点

  path pa;//路径类型

  struct path_node *i_link,*j_link;

  //i_link,j_link分别指向边结点的两顶点的地址

  }path_node,*path_node_p;

  typedef struct {

  char city_name[9];//城市名

  path_node_p firsh_path;//指向第一条依附该顶点的边

  }city_node,*city_p;//图结点

  typedef struct {

  city_node adj_list[MAXSIZE];//邻接多重

  int city_num,path_num;//图的顶点数和边数

  }graph_country;//图储存结构

  //邻接多重表储存图

  //路径类型

  typedef struct {//

  int vx,vy;

  }Edge;

  typedef struct {

  Edge path[100];//路径中边的序列

  int len;//路径中边的数目

  }path_city;

  typedef struct {

  char citys[MAXSIZE][9];//路径中城市的序列

  int num;//城市数目

  }p_city;//

  /**************火车的链表操作模块**************/

  train *init_train();

  //初始化链表

  town *init_town();

  //初始化链表

  status insert_train(train *l, char *train_num,char *start_place,char *end_place,int fare,int hour);

  //火车链表的插入操作

  status insert_town(train *l,char *name,time_train arrive,time_train leave);

  //城市的插入操作

  status find_town(train *l,char *town);

  ////查找中转站

  status find_train_num(train *l,char *train_num);

  //用火车序列号来查询

  status find_place(train *l,char *start_place,char *end_place,char choice);

  //用始发始发站和终点站来查询

  //status creat_train(train *l);

  //内置数据来创建火车链表

  status creat_train_f(train *l);

  //用文件来创建火车链表

  status creat_train_save(train *l);

  //用修改后的储存文件train_save.dat来创建火车链表

  status print_train(train const *l);

  //打印火车信息,包括中转站

  status save_train(train l);

  //储存修改后的到train_save.dat文件中

  status delete_train(train *l,char *train_num);

  //删除列车

  /******************火车的链表存储操作模块*******/

  /*******火车的图(多重邻接表)操作模块***********/

  status insert_path(graph_country *l,path pa);

  //在图l中插入边pa

  status insert_city(graph_country *l,char *city_name,int i);

  //在图l的i位置插入一城市city_name

  status init_graph(graph_country *l);

  //初始化图l

  status creat_graph(graph_country *l);

  //创建图l,从文件中读取信息

  void get_city(graph_country l,int i, char *city_name);

  //以city_name返回邻接多重表中序号为i顶点的城市名

  path_node *first_path(graph_country l,int vi);

  //返回图中依附于顶点的第一条这的指针:l.adj_list [vi].firsh_path

  path_node * next_path(graph_country g,int vi,path_node p,int *vj,path_node *q);

  //以vj返回图g中依附于顶点vi的一条边(由指针p所指)的另一端点;

  //以q返回图中依附于顶点vi且相对于指针p所指边的下一条边

  status print_graph(graph_country g);

  //打印城市图的信息

  /********火车的图(多重邻接表)操作模块*********/

  2)、其它模块的实现函数声明如下:

  /**********迪杰斯特拉算法实现模块**********/

  void init_p(path_city *pa);

  //初始化为一条空路径

  void init_set(p_city *p);

  //初始化生成最短路径结点的集合

  void copy_path(path_city *pa1,path_city *pa2);

  //复制路径

  void insert_p(path_city *pa,int v,int w);

  //在pa中插入一条边(v,w)

  int path_length(path_city *pa);

  //返回路径长度

  void out_path(graph_country l,path_city pa,p_city *citys,int nd);

  //将路径转换为城市名称的序列

  void putin_set(char *city_name,p_city *p,int st);

  //把city_name(序号为st的结点)放入

  void shortest_path(graph_country g,int st,int nd,int *pathlength,p_city *path_info);

  //利用迪杰斯特拉算法的基本思想求图g中从顶点st到顶点nd的一条最短路径

  //最短路径path_info及其路径长度path_lenth

  int minnal(int *dist,p_city ss);

  //求dist[]中的最小边

  /********迪杰斯特拉算法实现模块************/

  void train_main();

  //全国旅游交通查询的执行函数

  int city_name_int(graph_country l,char *name);

  //在城市与计算机储存序号之间建立一一映射

  3、详细设计:

  所需抽象数据类型定义和一些函数的声明见设计概要。

  这里只解释一下执行函数和一些比较复杂一点的算法。

  (1)、航空订票系统:

  1)、执行函数:

  见void air_main();

  2)、定票函数:

  status book(airline const *l,char *line_num,customer *c,char *name)//订票

  {

  //订票

  airline *p=l;

  customer *q=c->next ;

  p=l->next ;

  for(;q->next !=NULL;q=q->next){}

  // PR("%sn",q->name );

  for(;p!=NULL;p=p->next )

  {

  if(strcmp(line_num,p->line_num )==0)

  {

  if(p->left >0)

  {

  PR("恭喜您!订票成功!n");

  PR("你的座位号是: %dn",(p->total -p->left+1));

  insert_customer(&q,name,line_num,p->total-p->left +1);

  p->left --;

  return OK;

  }

  else PR("对不起,座位已满!n");

  return 0;

  }

  }

  PR("对不起,没有这个航班号!n");

  return ERROR;

  }

  定票的同时也同时了修改航线信息,并在修改后把修改后的信息储存到文件中去。

  3)、退票函数:

  status delete_cus(customer *h,airline *l,char *name)

  {

  //顾客退票

  customer *p,*pr;

  char line_num[8];

  // qr=h;

  pr=h;

  p=pr->next ;

  // PR("开始删除n");

  while(p!=NULL)

  {

  if(strcmp(name,p->name )==0)

  {

  strcpy(line_num,p->line_num );

  l=modefy_airline(l,line_num);

  pr->next =p->next ;

  free(p);

  PR("顾客 %s 退票成功!n",p->name );

  return OK;

  }

  pr=pr->next ;

  p=pr->next ;

  }

  PR("无此顾客,无法退票!n");

  return ERROR;

  }

  在此函数执行后执行顾客信息修改和航线信息修改。从而实现了退票功能。

  (2)、24点游戏:

  这个系统中主要有两个功能:人算24点和计算机算24点,前者是在单链表和用栈来表达式求值的模块中实现的,后者是在机算模块(也有用栈求值)中实现的。

  int CalcOneExpress(int expression[][2]);是实现计算机计算功能。

  int Equal24(int n);是判断是否为24。

  这里我主要讲一下计算机计算24点的算法:

  要计算机判断是否能计算24,就必须考虑所有的计算情况才能得出结论,如何把所有的情况都考虑进去呢?我想了一个星期,如果把四个计算数的数的地位看成一样的,把+-*/四种运算符也看成同等地位(这可用两个四重for循环来实现),如此一来繁乱复杂的很多种情况都归于七种情况,分另用一个函数去计算这种“特殊”的情况,那就是:(*号表示操作符,abcd分别表示四个数字)

  int CalcArray1(int iNumInput[2][4]);

  // a * b * c * d //7 number

  int CalcArray2(int iNumInput[2][4]);

  // (a * b) * c * d //9 number

  int CalcArray3(int iNumInput[2][4]);

  // (a * b * c) * d //9 number

  int CalcArray4(int iNumInput[2][4]);

  // (a * b * c) * d //9 number

  int CalcArray5(int iNumInput[2][4]);

  // (a * b) * (c * d) //11 number

  int CalcArray6(int iNumInput[2][4]);

  // ((a * b) * c) * d //11 number

  int CalcArray7(int iNumInput[2][4]);

  // (a * b * c) * d //9 number

  (3)、旅游交通查询系统:

  这个系统中主要是有三个模块来完成所有功能,其中有关火车信息查询与编辑功能都在一个火车模块中实现的。而最小路径的算法实现都在图模块和最小路径模块实现的。

  这里分析一下status creat_train_f(train *l);函数,我这里用从文件中读入信息的方式创建火车信息链表。为了能编辑它,我提供了两种方式:一是直接修改文件,但这不太安全,因为你必须以定义的文件格式编辑文件,否则错误不可预料。二是用我这里提供的编辑方式,再把修改信息保存下来(有这个选项)。

  编辑火车信息的一些关键语句如下:

  PR("---课程设计选择菜单------*n");

  PR("*---旅游交通查询系统选择菜单------------*n");

  PR("*----火车信息编辑菜单-------------------*n");

  PR("* 添加火车信息------------0 *n");

  PR("* 删除火车信息------------1 *n");

  PR("* 查看火车信息------------2 *n");

  PR("* 保存火车信息------------3 *n");

  PR("* 退出火车信息编辑--------4 *n");

  PR("*---------------------------------------*n");

  PR("请选择: ");

  ch3 = getch();

  PR("%cn",ch3);

  if(ch3=='0')

  {

  PR("请输入你要添加的火车的列车号 :");

  scanf("%s",train_num);

  PR("请输入始发站: ");

  scanf("%s",start_place);

  PR("请输入终点站: ");

  scanf("%s",end_place);

  PR("请输入所需费用: ");

  scanf("%d",&fare);

  PR("请输入所需时间: ");

  scanf("%d",&hour);

  insert_train(tr,train_num,start_place,end_place,fare,hour);

  PR("成功添加%s号火车n",train_num);

  while(t4==1)

  {

  PR("*-----------------------------*n");

  PR("---课程设计选择菜单---------*n");

  PR("*---旅游交通查询系统选择菜单----*n");

  PR("*----火车信息编辑菜单---------*n");

  PR("*-----火车中转站编辑菜单-----*n");

  PR("* 添加中转站----0 *n");

  // PR("* 删除中转站-------1 *n");

  PR("* 查看中转站信息------1 *n");

  PR("* 中转站编辑完毕----2 *n");

  PR("*--------------------------*n");

  PR("请选择: ");

  ch4= getch();

  PR("%cn",ch4);

  if(ch4=='0')

  {

  PR("!!中转站也包括始发站和终点站n!!");

  PR("!!这儿途经各站的顺序与实际刚好相反!!n");

  PR("请输入你要添加的中转站名 :");

  scanf("%s",town_name);

  PR("请输入第几天到达: ");

  scanf("%d",&arrive.day );

  PR("请输入到达的时数: ");

  scanf("%d",&arrive.hour );

  PR("请输入到达的分钟数: ");

  scanf("%d",&arrive.minute);

  PR("请输入第几天出发: ");

  scanf("%d",&leave.day );

  PR("请输入的时数: ");

  scanf("%d",&leave.hour );

  PR("请输入出发的分钟数: ");

  scanf("%d",&leave.minute);

  if(tr->next_train ==NULL)

  {

  insert_town(tr,town_name,arrive,leave);

  }

  else insert_town(tr->next_train,town_name,arrive,leave);

  }

  最后分析一下利用迪杰斯特拉算法来求最短路径的基本思想与过程,集合ss始终包含已求得的最短路径的结点,再算其它结点与已求集合的最短距离,再把最短距离的结点加入到ss 集合,重复上棕过程。C程序如下:

  void shortest_path(graph_country g,int st,int nd,int *pathlength,p_city *path_info)

  {

  //利用迪杰斯特拉算法的基本思想求图g中从顶点st到顶点nd的一条最短路径

  //最短路径path_info及其路径长度path_lenth

  int dist[MAXSIZE];

  int i=0,v,w;

  path_node q;

  int found,min;

  p_city ss;//ss为已求得最短路径的顶点的集合

  int adjvex;

  path_node *p,*qq;

  path_city path[MAXSIZE];

  for (i=0;i

  {

  }

  p=first_path(g,st);

  while(p!=NULL)//初始化dist数组,检测依附于起始边的每一条边

  {

  qq=next_path(g,st,*p,&adjvex,&q);

  dist[adjvex]=p->pa.length ;

  insert_p(&path[adjvex],st,adjvex);

  p=qq;

  }

  found =FALSE;

  init_set(&ss);

  putin_set(g.adj_list[st].city_name ,&ss,st);

  while(!found)

  {

  min=minnal(dist,ss);

  //在所有尚未求得最短路径的顶点中求使dist取小值的i值

  if(min==nd) found= TRUE;

  else

  {

  v=min;

  putin_set(g.adj_list[v].city_name,&ss,v);

  p=first_path(g,v);

  while(p!=NULL)//检测依附于v的每一条尚未访问的边

  {

  qq=next_path(g,v,*p,&w,qq);

  if((ss.citys [w][0]==''/*w不在ss中*/)&&((dist[v]+p->pa .length )

  {

  dist[w]=dist[v]+p->pa.length ;

  copy_path(&path[w],&path[v]);

  insert_p(&path[w],v,w);

  }

  p=qq;

  }//while(p!=NULL)

  }//else

  }//while(!found)

  pathlength=&dist[nd];

  PR("从 %s 到 %s 的最短路径是:n",g.adj_list [st].city_name ,g.adj_list [nd].city_name );

  out_path(g,path[nd],&ss,nd);

  PR("总路径长度 :%d 公里n",dist[nd]);

  }

  4、调试分析:

  调试是程序设计中最重要的一环,他几乎决定了程序优劣。现将我调试时遇到的一些问题及其解决的方法的记录陈列如下以供学习与交流:

  我将调试时遇到的一些问题分为两大类:

  (1)、语法错误:

  语法错误相对来说要好调试一些的,但有两点需要特别指出:一是应该用规范化的格式输入源程序,我推荐的格式是:函数体内、循环体内等都应该缩进一个TAB位,相应的块语句的两个大括号都应保持在同一列上,函数体之间、模块之间都应用空行隔开,这就解决了各种匹配的问题,更重要的是它极大的增强的程序的可读性。二是应该注意函数的实参与形参的传递问题,要尽量保持两者类型的匹配,(当不匹配又可通过编译时会发生数据类型的隐式转换,这样会产生很多不安全且又很难找到的错误)当不需要改变形参时,只需传入变量,如果你想在函数体内改变函数的外部变量,则传入指针。

  我的调试一个打开文件进行写入操作时遇到这样一个问题,char filename[]=”C:airline.dat”,fp=fopen “filename”,”wb”);调试了我一个半小时,还是没有找到问题的所在,最后发现是在filename上多了一对引号!!!

  (2)、逻辑错误:

  在编译错误为0的情况下,不要高兴的太早,这只是你调试程序的第一步,此时也要关注一下警告warning,每一个warning都有他一定的道理。当你修改的只剩下一些无关紧要的时,你才可以连接运行你的程序了。这其中出现的一些逻辑错误才是调试的难点所在。在连接程序时可能出现的问题可能是,库连接不上、标志符有问题(如函数名不应该以数字开头命名,你定义的标志符与编译器内部或库内部定义的标识符相冲突。如我在定一个火车的时间的结构体时用到了time作为它的名称,与time.h内部定义的变量名相冲突了)此外还应该注意几点:

  1)、溢出的问题,其中包括数组的下标上溢或下溢,for语句的控制语句超出数据范围(如在访问单链表时应该是:for p=l;p!=NULL;p=p->next))。

  2)、指针问题,记住,声明一个指针只是在内存中开辟了一个这种数据类型的空间,并让这个指针指向它,由于它还没有指向任何变量,因此不能引用其指向的任何成员(编译器会警告你说你的指针没有初始化却用了它,如我在初始化单链表时这样写的:init_sq sqlist *l){l->base= *elemtype)malloc sizeof elemtype));}而在调用时用语句:sqlist *l;init_sq );这是不允许的,因为它仅仅是一个地址,而不是一个变量,这个程序有几种改法:)(又如:char *name;再将name做为一个指针传到函数中,你的本意可能是想通过函数改变你的字符串,但这里你忽略了一个问题,你没有初始化你的指针却用了它,这样很不安全,虽然你有时可以运行,却有了不安定的因素。你可以这样定义:char name[20];这里的name是一个常量地址,也是一个数组名,因此不用担心它没有被初始化。字符数组与字符串的区别是前者不用在最末位加一个‘‘,但你如果把它当做字符串用时系统会自动给你加上的,因此在定义字符数组时尽量多定义一位)

  3)、何时用指向指针的指针(二级指针)?

  我的经验是:当你在函数体内改变了一个形参的地址且你又想同时影响函数体外部的指针,则需要一个二级指针。如我在24点游戏中的status insert_sq sqlist **p,int e,int bl)函数中,我为了每次都在链表末尾加一个结点,我就使用了二级指针p,使其始终指向最后一人结点。当然这也可以用返回指针的方式完成同样的功能。但当你必须返回一个别的变量时这个方法就不行了。

  4)、养成打开了文件(fopen)就要在某个时候关闭(fclose)的习惯,否则会有安全隐患。

  5)、C语言本身的一些安全问题:

  a)、字符串函数的不安全性:

  如scanf();由于它是基于获取单词而不是获取字符串,

  它遇到空格或换行符就结束(谁先满足执行谁),当提前遇到空格或输入的字符个数大于你所容纳的个数时,系统会把其它的字符溢出到相邻的内存中去,产生一些不安全的因素。Gets(); strcpy();等字符串函数都是这样。因此建议你严格控制用户的输入或用另外的一些相应的函数来代替,如用:strncpy();代替strcpy();

  b)、常量定义时的一些不安全因素:

  用#define 定义常量编译器不会去检查其数据类型,引发安全隐患。解决的方法是用关键字:const去定义常量,这样可以要编译器给你进行类型匹配检查。

  c)、名字冲突的不安全性:

  名字,特别是函数名、数据类型名经常与自己定义的名字或系统的,包括库里面的名字相同,解决的方法是尽量用能表达意思且系统不太可能出现的名字命名。(仅在当两个相同的标识符在同一个作用域时才会出现此问题。

  d)、内存管理的不安全性:

  内存泄漏,野指针等内存问题,应该好好的考虑一下,我的经验是每删除一人用malloc()申请的结点时,都应该用free();语句释放它;并且在这个作用域内不再使用此指针去指向其它的地址。

  e)、移植的安全性问题

  虽然C语言具有很强的可移植性,但在不同机器上运行(如32位机),不同的编译器上(如tc 上整型是2个字节,而在vc++6.0中却是4位。)解决的方法是在申请空间等涉及到数据类型长度时都采有sizeof(elemtpye);的形式,又如我在定义maxint时用了:#define maxint (sizeof(int)>2?MAXINT_2:MAXINT_1)这样考虑不用管 int型是四位的,还是二位的。

  5、遗留问题分析:

  (1)、在航空订票系统中的密码输入时,由于我用了for 语句和getch );putchar ‘*‘);来实现的,而getch );不分区另ENTER和BACKSPACE等特殊键,不好控制它的结束。因此我只有避过问题强行规定密码必须是8位的,但在输入密码时仍然不允许用户输入ENTER和BACKSPACE等特殊键。

  (2)、24点中的问题:

  相对于一个完整的二十四点游戏本程序尚存在两个缺陷:

  I.时间限制:由于本程序不是重在娱乐,而重在学习与交流,再加上我也没找到C语言中的适当的时间函数,(要是用Visnal C++6.0的可视化编程加一个时钟计十分方便,我也正在把此游戏改为c++语言,主要是想熟悉一下)故而来把此功能加入进去,如果玩家有心,可以自行加入。

  II.由于我在算a/b时,a,b和其结果却设为整型,导致计算机可能得出2/5=0的结果,因此我又改了一下,在计算a/b之前先判断,if((a%b)!=0),我返回了一个-2000,目的是阻止这种运算,现实中是却存在一些数字非得这样计算才能得出结果(如:5,5,5,1,只能由((5-1/5)*5=24来算)。导致一些问题的产生。要解这些问题有两个方法:其一是修改四个随机数的产生函数:randomn(),控制诸如此等的随机数,当然这虽然不先为一个办法但只是避开问题本身;其二是把一些相关的函数和数据类型,如:单链表的int num_ch改为float num_ch;把入栈、出栈等等一些函数的以前接的int型的改为接的和处理float型,再把int equal_24(int num)改为int equal_24(float num)用if(fabs(num_24-24)<0.000001)来判断,这样虽然改动极大,但此法可完全解决此问题,可以一试。(其实这个问题在其他的数据处理算法中也存在)

  (3)、交通旅游系统中:

  1)、中转站的反输入问题:

  由于我在插入操作时为了节约时间,都是从前部插入,造成了实际站点顺序与程序中储存的顺序刚好相反,本来可以通过加一个语名for(q=l;q->next!=NULL;q=q->next)来解决,但我觉得这里没有这个必要,反正我在编辑文件时反过来放了。

  2)、在求最小路径时,没有同时实现最省钱,最省时的最佳路径。

  但我已把解决他们的接口都留下了。便于以后把此功能加上。

  3)、终点站本无出站时间的,但我们为了公平处理所有问题,我都把其设置为000,这个问题可通过增加控制输出语句来解决,但我觉得没有必要,因为每个人都知道哪里是终点站吧。(我们在readme.txt中也有提示)。

  4)、数据文件,密码的安全性问题。

  由于我把用户修改后的密码储存在pass.dat里,却又不能把它设置为只读,用户可以随便改变,也许会导致异常的产生。数据文件也存在这个问题。

  6、使用说明及测试结果:

  (1)航空订票系统:

  航空订票系统的界面:

  *---------------------------------------*

  *---课程设计选择菜单--------------------*

  *-----航空订票系统选择菜单--------------*

  * 订票------------------0 *

  * 退票------------------1 *

  * 查看所有信息----------2 *

  * 修改航线(需密码)------3 *

  * 退出航空订票系统------4 *

  *---------------------------------------*

  进入修改航线(需密码)------3时的原始密码是:weiji024

  经测试,此中第个功能都实现了。

  (2)、24点游戏:

  24点游戏的界面:你想采用什么模式?n");

  ************************************************

  *----课程设计选择菜单--------------————----*

  *-------24点游戏菜单--------------------------*

  *计算机给出四个数字,你来算直到你想退出: 0 *

  *自己给出四个数字,要计算机来算,直到想退出: 1 *

  * 退出24点游戏: 2 *

  ************************************************

  经测试,此中每个功能都实现了。

  (3)、旅游交通查询系统:

  旅游交通查询系统界面:

  *---------------------------------------*

  *---课程设计选择菜单--------------------*

  *-----旅游交通查询系统选择菜单----------*

  * 火车信息查询---------0 *

  * 最短路径查询---------1 *

  * 火车信息编辑---------2 *

  * 读入修改信息---------3 *

  * 查看火车信息---------4 *

  * 查看城市信息---------5 *

  * 退出-----------------6 *

  *---------------------------------------*

  经测试,此中每个功能都实现了。其中最短路径查询的原始数据为《数据结构》第187面的图7.33, 经测试,最小路径全部符合实际。

  7、源程序:

  我用了9个文件:分别是:all.h train.c

  24.h rainGraph.c

  train.h 24game.c

  airplane.h main.c t

  航空订票系统.c

  四个需要读入的数据文件:pass.dat ,train.dat ,trainGraph.dat

  train_save.dat

  两个生成文件:airline.dat ,customer.dat

  所有源程序都在VC6.0中调试通过。

  三、设计收获及心得体会:

  这几天编程发现了几个有趣的结论:

  1、编程的真正吸引力可与玩游戏相媲美

  考完英语之后的两天,除了吃饭外没有一刻不是在电脑前的,晚上睡觉想着它,早上6点起床摆弄它。尤其是在调试时找一个错误用了几个小时仍未找到却忽然醒悟或找的那一刻的感觉实在太好了。这种执著与感觉我只有在玩《天龙八部》、《新仙剑一》、《新仙剑二》触发了找了好久的剧情或走出了困了好久的迷宫的感觉。天地为之开阔……

  2、真正的程序不只是“运行”了!

  真正可称得上“好程序”是要满足一大堆的条件的。可读性、健壮性、可维护性、高效性等等等等条件。其实大部分功能我早就已经实现了,(只用了两天),但其后的测试、修改、完善、注释、润色和现在的编写系统文档也用了不少的时间。

  3、成功的感觉真好!

  当你看着自己把功能一个个实现,把错误一个调试出来,那种感觉给了自己某种安慰,还有自信!!

  4、几句废话:要提高自己的编程能力,你必须亲自去体验、去设计、编辑、编译、调试、运行。在此之前,我也以为自己对C语言已经比较懂了,可还是遇到了一系列问题,也学到很多东西。每一个程序员都是在失败、尝试、失败、尝试与收获中成长起来的。作为一个团团的计算机学院,大部分学生对计算机竟不大感兴趣,导致学院人才凋零。我本学识尚浅,无权谈论这些,只是希望能对大家有所警醒,编程之道漫漫无边,吾将上下而求索.最后以林锐先生的话来作为自己的追求目标和最后的结束语:以振兴民族软件产业为己任,作真实、正值、优秀的科技人员!

  四、自己的特色和申请的成绩

  1、可读性较强:

  我们的程序可读性比较好,主要体现在以下几个方面:

  (1)、结构严谨,都采用模块化设计,我们采用了多文件结构,不同的文件实现了不同的功能,并将每个模块的函数都在相应的头文件中声明并带有功能注解。

  (2)、较详细的注释 使得程序更容易阅读。我们在每个函数、每种数据类型的定义、每条关键语句都不得有相应的注释。

  (3)、书写规范:在输入编辑源程序时我们力争使程序语句更规范(用上面提到的格式规范)。

  (4)标识符(如函数名、变量名、数据类型名)命名有讲究:我们大都采用了操作_对象的命名规则,如creat_train );表示创建火车链表之意,尽量使标识符“词能达意”,增强可读性。

  2、容易维护:

  我在编程时有个习惯,就是先把执行函数写好,如main )函数,同时设置留下每个函数的接口,再编写相应的函数来实现它。这样做的好处是让我样很清楚自己需要什么样的函数,有什么样的接口,当然就更容易维护了。

  3、很强的健壮性:

  我在编写程序时尽量多的考虑了各种情况的发生,如用户的各种非法输入,数据文件的的丢失或者错误的更改,并做出了相应处理,使得程序仍可运行。由于C语言的异常机制本身就不是很强,我也只能做到这一步了。

  4、支持数据更新:

  我在交通旅游系统中,数据都是从文件中读取的,只要你按照readme.txt文件中所定义的文件格式编辑文件,即可得到新的.exe文件。

  5、功能比较完善:

  完善的功能主要体现在交通旅游系统中,我们基本实现了火车查询的所有功能,如:始发地与目的地的查询、车次的查询、最快、最省钱查询、中转站查询等等,与火车查询的同类软件相比还似乎更胜一筹,因为我们还提供的“最短路径”的查询。

  6、比较友好的界面:

  由于各种原因,如时间,语言等原因,我们没有做形如windows那么友好的界面,但我也在界面上下了一些功夫:我采用了逐级菜单,提示用户的输入与注意事项,并把三个系统融入到一个系统中去,结构谨然,界面也比较友好。

  7、涉及的数据结构知识广泛:

  课和设计本来就是为了提高自己的编程能力,我们为了尽可能的多的让自己学得更多,在系统中我们定义了14个数据类型,70多个函数,操作涉及到单链表、多重链表、栈、图、队列、多重邻接表等等数据结构的多种操作。

  8、24点中的几个特色:

  1)在接受到用户输入的字符串后,程序判断其为数字还是字符,并将其信息包括是数字还是字符的标识(用int BOL,0为数字,1为字符)储存到单链表中。从而使得其区别和求值非常简单,大大简化了程序的复杂度。

  2)当玩家认为程序随机产生的四个数字不能算出24时要求程序给出答案,这其实是一个复杂的问题,因为数字是不定的,而算式出是不定的,要判断所有的组合才能下结论。但经我研究发现如果把四个数字和加减乘除不加以区别,加上括号一共只有7种组合,见程序的源程序中的

  int CalcArray1(int iNumInput[2][4])//处理第一种情况,即无括号的一种,这样把本是很复杂的问题大大简化了。

  9、算法时间空间复杂度分析:

  (1)、在参数传递时,我大多数都采用了地址的传入,因为这样可以提高程序工作效率。同时考虑到安全的因素,为了防止传入的指针意外的改变函数体外面信息,在不需要改变此地址时,我们都有了const 修饰符。做到了效率与安全兼得。

  (2)、当需要删除某个链结点时,我使用了一种比较高较的一种算法:即用指针来实现,避免两次重复遍历。其代码请看源程序中的delete_函数。

  (3)、24点机算算法分析:

  由于我采用和如详细所述的算法进行计算,最坏的情况要执行:4*4*4*3*2*1*7= 10752次,最好时执行1次,平均次数是5376次。(对于现在的计算机来说即使是最坏的情况也不过是几毫秒的事)这里程序比较简洁易懂,效率也比较高。

∨ 展开

网友评论

我要跟帖
取消
所有评论 共 0