《C程序设计》课程设计指导书0104.docx
《《C程序设计》课程设计指导书0104.docx》由会员分享,可在线阅读,更多相关《《C程序设计》课程设计指导书0104.docx(18页珍藏版)》请在冰点文库上搜索。
《C程序设计》课程设计指导书0104
长春理工大学光电信息学院
《C程序设计》
课程设计指导书
信息工程分院计算机实验室
一、课程设计的目的
采用结构化程序设计方法,综合运用C语言的基本知识,尤其是数组、函数、指针及流程控制等,并补充课程中虽未涉及但实用中必要的其他内容,实现一个功能较为齐全的程序实例。
二、设计题目
学生成绩管理系统。
程序的主要功能如下:
1、按学号记录一个班M名学生N门课程的期末考试成绩;
2、逐一显示M个学生的有关数据;
3、实现查找、删除;
4、能统计出补考学生及其相应科目。
三、设计的方法步骤
1、自愿结合,每2~3名同学为一组,选组长一名。
2、由组长主持,全组一起消化理解整个程序的基本功能。
在此基础上,明确每一名同学所承担的具体模块(函数)。
3、尽可能独立地实现系统的功能(组内同学可一起讨论),确有困难,可参照本指导书中所附的示范案例。
4、应认真研读本指导书中示范案例中的思考题,为答辩做准备。
四、课程设计报告的内容
1、课程设计目的;
2、课程设计题目及主要功能;
3、程序中用到的主要数据结构及程序的总体功能框图;
4、所实现的模块(函数)功能及源程序;
5、所实现的模块(函数)中最能代表你设计水平的②算法框图;(可选)
6、程设计的心得体会。
(可选)
五、答辩要求
1、以组为单位答辩,答辩时应提供能运行的完整程序及课程设计报告(每人1份)。
2、组长概述程序的总体功能及总的设计思路后,逐个同学上机演示你本人承担的模块功能并回答老师的提问。
3、提问问题中除指导书上列出的思考题,还包括老师随时针对你的源代码、框图等以及设计中涉及到的基本知识所提出的问题。
附1:
设计参考案例
a)设计的基本思路
我们采用C语言,VC6.0集成开发环境,字符用户界面,结构化程序设计方法。
依据N.Wirth的著名公式:
程序=数据结构+算法
其中数据结构要解决两个问题:
表示一个学生的属性及M个学生的集合;算法则应实现程序的功能。
二、数据结构
用一个结构体类型表示一个学生的属性:
typedefstructstudent{
longnum;/*学号*/
charname[20];/*姓名*/
intscote[N];/*N门功课考试成绩*/
structstudent*next;/*为构成链表而设*/
}Student;
我们采用单链表表示M个学生的集合,这主要是为了熟悉链表的操作。
所以在Student类型中事先已设置了next域。
。
我们也可以采用结构体数组表示M个学生的集合(当然应去掉Student中的next域)。
三、系统的功能框图
四、系统各功能模块(函数)的实现
1、主模块(main()函数)再任意选择两个模块书写
主模块的主要功能是反复显示菜单,根据用户的选项,调用相应的功能模块,直到用户选择退出。
参考程序如下:
#include"my.h"
charcourse[N][20];
voidmain()
{
Student*head;
Student*p;
charch;
longnum;/*numberofstudents*/
inti;
clrscr();
printf("\nEnter%dcoursenames:
\n",N);
for(i=0;i{
fflush(stdin);
gets(course[i]);
}
p=(Student*)malloc(sizeof(Student));
if(p==NULL)
{
printf("\nMemoryallocationerror.");
exit
(1);
}
else
{
p->number=0;
p->next=NULL;
strcpy(p->name,"headnode");
for(i=0;ip->score[i]=0;
head=p;
}
do{
ch=menu();
switch(ch)
{
case'1':
input(head);
break;
case'2':
display(head);
printf("\nEnteranykeytocontinue,please:
");
getche();
break;
case'3':
delete(head);
printf("\nEnteranykeytocontinue,please:
");
getche();
break;
case'4':
printf("\nEnterthenumberofstudent:
");
scanf("%ld",&num);
p=search(head,num);
if(p==NULL)
printf("\n%ldnumberstudentisnotfound.",num);
else
dispnode(p->next);
printf("\nEnteranykeytocontinue,please:
");
getche();
break;
case'5':
reexamine(head);
printf("\nEnteranykeytocontinue,please:
");
getche();
break;
case'6':
printf("\nExittheprogramnow,bye_bye!
");
exit(0);
break;
default:
printf("\nYoushouldpress<1>---<6>");
if(ch=='\n'||ch=='\t'||ch=='')
printf("\nchiswhitecharacter.\n");
else
printf("\nch=%c\n",&ch);
break;
}
}while
(1);
}
_
思考题:
(1)main()函数中的循环怎样才能结束?
(2)除了用do-while,还可用什么循环语句?
(3)head所指的空节点的num域可用于表示什么信息?
2、显示菜单模块
显示一个字符界面的菜单,返回代表用户选项的一个数字字符。
参考程序如下:
#include"my.h"
charmenu()
{
charch;
inti;
clrscr();
printf("\n\n\t1---Input\n");
printf("\n\t2---Display\n");
printf("\n\t3---delete\n");
printf("\n\t4---Search\n");
printf("\n\t5---Reexamine\n");
printf("\n\n\t6---Exit\n");
printf("\n\n\t\tEnteryourchoice:
");
fflush(stdin);
ch=getche();
getche();/*waitfor*/
returnch;
}
思考题:
(1)你能自己设计一个更美观好用的字符用户界面吗?
(2)用单个字符代表用户的选项,如,I代表input,,则菜单程序应如何修改?
(3)函数中的fflush()getche()起什么作用?
3、录入模块
录入学生的数据,以学号从小到大的顺序插入到链表中。
如果链表中已存在该学生,则给出提示信息,不重复录入。
一次可录入0…n名学生数据,当输入学号为负时,结束录入。
参考程序如下:
#include"my.h"
externcharcourse[N][20];
voidinput(Student*h)
{
longnum;
Student*p,*q,*r;
inti,n=0;/*nisnumberofinsertedinonecalling*/
charch;
clrscr();
printf("\nEnterdataforastudent:
\n");
printf("\nNumber:
");
scanf("%ld",&num);
while(num>0)
{
p=search(h,num);
if(p!
=NULL)
{
printf("\n%ldnumberstudenthasbeenexisted.",num);
printf("\nReenternumberforastudent,please:
");
fflush(stdin);
scanf("%ld",&num);
}
else
{
p=(Student*)malloc(sizeof(Student));
if(p==NULL)
{
printf("\nMemoryallocationerror.");
exit
(1);
}
else
{
n++;/*incrementsnumberofstudents*/
p->number=num;
printf("\nName:
");
fflush(stdin);
gets(p->name);
printf("\nEnter%dexaminescores:
",N);
for(i=0;i{
printf("\n%s:
\t",course[i]);
scanf("%d",&p->score[i]);
}
}
r=h;
q=r->next;
while(q!
=NULL&&num>q->number)
{
r=q;
q=q->next;
}
r->next=p;
p->next=q;
clrscr();
printf("\nEnterdataforastudent:
\n");
printf("\nNumber:
");
scanf("%ld",&num);
}
}
h->number+=n;
printf("\nThereare(is)%dstudent(s)were(was)inserted.",n);
printf("\nThereare(is)total%ldstudent(s).",h->number);
}
思考题:
(1)在录入模块中,为什么要调用查找模块?
(2)为什么在接收输入的学号时,另设一个变量num,而不直接使用p→num?
(定p指向新近成功申请的节点)
(3)插入新节点时,如何在原附加在表头,而与其它情况同样处理可以吗?
(4)在此模块内,表头指针所指的空的头节点起到了什么作用?
(5)在接收学生姓名时,用了库函数gets(name);比运用scanf("%s”,name)好处在哪儿?
(6)不调用search(),代码应如何修改?
(7)果用程序保证每门课的成绩在[0,100]范围内,怎样实现?
4、查找模块
在给定的链表中查找给定学号的学生。
找到则返回给定节点前驱节点的指针,找不到返回null.
这里所以要返回前驱节点的指针,而不是待查节点的指针,原因在于删除模块想调用查找模块,插入节点时要用到待删节点的前驱节点的指针。
这是本查找模块与一般查找不同的一个创意。
参考程序如下:
#include"my.h"
Student*search(Student*h,longnum)
{Student*p,*q;
q=h;
p=h->next;
if(p==NULL)
returnNULL;
else
{
while(p!
=NULL&&p->number!
=num)
{
q=p;
p=p->next;
}
if(p!
=NULL&&p->number==num)
returnq;
else
returnNULL;
}
}
_
思考题:
(1)此search()模块为何返回所查节点前驱的指针?
(2)如果不考虑插入的需要,本模块应如何修改?
5输出补考名单
遍历整个链表,输出需补考的学生名单及补考课目。
这一功能很有实际意义。
它是在遍历链表的基础上进行筛选。
附带求出全班的不及格率。
参考程序如下:
#include"my.h"
voidreexamine(Student*h)
{Student*p;
intpass;/*logicvar.*/
inti;
intnopass=0;/*numberofno-passstudent*/
clrscr();
if(h->number==0)
printf("\nThereisnostudentinthelist.");
else
{
p=h->next;
while(p!
=NULL)
{
pass=1;
for(i=0;i{
if(p->score[i]<60)
{
pass=0;
nopass++;
break;
}
}
if(pass==0)
{
dispnode(p);
printf("\nEnteranykeytocontinue,please:
");
getche();
}
p=p->next;
}
printf("\n\nThere%stotal%ldstudent(s)",h->number>1?
"are":
"is",h->number);
printf("\nincluded%dstudent%cno-pass.",nopass,nopass>1?
's':
'');
printf("\nNo-passpercentageis%.2f%%.\n",(float)(nopass)/h->number*100);
}
}
思考题:
(1)能设计一个更好的输出格式;
(2)int型变量pass的作用是什么?
while循环中为什么有pass=1;这条语句
7、显示模块遍历链表,逐个显示学生的数据。
此模块功能与输出补考名单类似,只是不必筛选,因此更简单一些。
参考程序如下:
#include"my.h"
voiddisplay(Student*h)
{
Student*p;
clrscr();
if(h->number==0)
printf("\nThereisnostudentinthelist.\n");
else
{p=h->next;
while(p!
=NULL)
{
dispnode(p);
printf("\nEnteranytocontinue,please:
");
getche();
p=p->next;
};
}
{
intmany;/*indicatenumberofstudent>1?
*/
many=h->number>1;
printf("There%s%ldstudent(s)",many?
"are":
"is",h->number);
}
}
思考题:
(1)显示与输出补考名单模块有何相似之处?
(2)自己设计一个更好的输出格式。
6、删除模块:
由用户指定待删除学生的学号,调用search()模块,若找到则删除,否则返回,一次只删除一个学生。
参考程序如下:
#include"my.h"
voiddelete(Student*h)
{Student*p,*q;
intnum;
charanswer;
clrscr();
printf("\nEnternumberofthestudentbeingdeleted:
");
scanf("%ld",&num);
q=search(h,num);
if(q!
=NULL)
{
p=q->next;
dispnode(p);
printf("\nDeletethestudent,areyousure?
(Y/N):
");
fflush(stdin);
answer=getche();
answer=tolower(answer);
if(answer=='y')
{
q->next=p->next;
free(p);
h->number--;
}
else
{
printf("\nYouchangeyouridea,bye_bye.");
}
}
else
{
printf("\nThestudenttobedeletedisnotfound.");
}
}
思考题:
(1)如果想一次可删除多个学生,此模块的程序代码应怎样修改?
(2)free(p);起什么作用?
(3)实际删除节点前,显示一下待删节点包含的信息,然后再次要求用户确认要删除此节点,你认为是否有必要?
程序如何实现的?
附2:
程序的调试:
一位著名的编程大师曾说过:
“程序就是10%的灵感和90%的调试。
所有的编程高手都是调试的能手。
调试可分单模块调试和多模块联调。
关于调试的基本原则和方法以后在《软件工程》等相关课程中会深入探讨,我们在在此仅就以上案例说说具体作法。
为了调试每个模块,首先要对该模块单独编译,以便让编译器为我们尽可能地发现语法方面的错误,然后加以改正,直至编译成功。
然后,我们需要编写一个main()函数,调用该模块,以发现逻辑上的错误,并进一步分析对代码进行优化。
当可以正常执行后再仔细划分程序所能处理数据的各种情况,特别是边界上的数据,能否被正确处理;异常或错误的数据能否被程序所发现并剔除,例如,对menu()函数,就须反复调用,以验证在多次调用时是否每次都能按预期的方式工作。
案例中的fflush()就是在调试中发现问题后,通过查找资料而填加上的。
另外,各模块虽然相互独立,但逻辑上的有一定的先后次序。
建议调试次序先调menu(),search(),input(),然后调其它模块。
联调要建立项目文件(或称工程文件)。
具体操作方法是:
在TurboC2.0的集成开发环境下,用编辑器建一个一个项目文件,(.prj是必须的,文件名可选)内容如下:
main.c
menu.c
input.c
display.c
dispnode.c
search.c
delete.c
reexam.c
命名为student.prj然后在project下拉菜单,的PrjectName项下,注册上述项目文件名。
再接ctrl-F9菜单就自动对项目文件中所列的C源文件进行编译连接并加以执行了。
当然,在各模块正确通过编译后,联调还会产生一些错误,有些错误还比较隐蔽,需要耐心,细致地一一查找改正,这也是锻炼我们实际工作能力的好机会。
调试中要充分利用TurboC2.0集成开发环境提供的各种调试手段,如单步运行、设置断点、添加观察项等。
具体操作请参照教科书上的有关章节。
相信同学们经过艰苦努力,一定会有事前难以预想的收获。