c语言程序设计补充题(通用8篇)
一、课程设计的目的及意义
设计并实现该课程设计的目的主要在于:
1)对一定规模的综合软件编程有一定的经历与认识。在做的过程中,你会发现,提前的规划即分析与设计重要过编程过程,否则
会走很多弯。
2)综合并结合现实应用使用C语言的知识。这个设计会用到C语言这门课的全部知识,其中以文件、数组与链表为主,书中提
到的主要及重点算法都会使用到。
3)不仅涉及编程,还涉及到功能分析、模块规划等方面的知识,这些知识在后续课程学习时,会经常使用。这些知识在学《软件
工程》这门课时,会系统讲解。希望有了这次的经历,你能更好的体会《软件工程》这门课的意义,并能学好和用好其中的知识。
4)每项功能的实现,一般有多种方法,这里都强调使用时空效率最高的方法,此次实现只是让你有一个初步的认识,在下学期学习了《数据结构》后,你就知道为什么让你这样做,你也对《数据结构》这门课的作用及意义有所了解。
二、课程设计的实现思路
任何一个管理系统的基本操作主要如下:
1)能录入并能向文件里保存数据
2)能计算并修改文件里的数据
3)能向文件里追加数据
4)能查找文件里是否有某项数据
5)根据要求显示文件里的某些数据或全部数据
6)能在文件里插入或删除某项数据
7)按某个数据项进行排序生成排序文件
可以看出,所有操作的围绕的中心是文件。而文件本身提供的功能相当有限,主要是打开与关闭文件、读与写文件、在文件末尾追加数据、修改文件里的某些数据(这个前提是需要用fseek找到修改的位置再往里写数据),这四项功能。其它的功能文件不能直接完成,需要借由内存将相应的操作完成后,再写入文件。比如:
1)录入并向文件里保存数据的实现思路:C语言并没有提供由键盘输入数据直接录入文件的功能,只有内存变量向文件写入数据的功能,而由键盘输入数据可以到内存变量,因此实现此部分功能时,应当由键盘将数据放入变量,再由变量写入文件。这里录入要求不采用书上例子,它是用结构体数组,我们要求只用一个结构体实现。先将一个人的信息放入结构体,将这个结构体数据写入文件后,再将下一个人的信息继续放在这个结构体中,再将这个结构体写入文件。这样节省内存空间。
2)计算并修改文件里数据的实现思路:这部分的功能的实现应当先将文件的数据读到变量当中,在变量当中完成计算,再将数据
写入文件。如果只是修改文件的数据,并不计算,则可以直接定位到文件中相应的位置,写入数据,则把原来的数据覆盖以完成修改。
3)向文件里追加数据的实现思路:文件本身提供了这项功能,只要以 “a”的方式打开就行。然后向文件写入的数据,直接放在文
件末尾。
4)查找文件里是否有某项数据的实现思路:C语言并没有提供判断文件内容的功能,必须将文件内容读到变量里再进行判断。实
际的查找可能是在大量的数据里的查找,高效的查找是折半查找(下学期的数据结构专门讲这一内容),折半的前提是排序,因此需先对排序后的文件读出,以折半方式查找。(这要要求,是希望巩固折半查找与排序两个重要算法,至于它的时空效率是否高,可以学完《数据结构》知识后自己再判断)。
5)根据要求显示文件里的某些数据或全部数据的实现思路:C语言没有提供将文件内容显示的功能,所以需要将文件内容读到变
量里,再显示变量。
6)在文件里插入或删除某项数据的实现思路:C语言同样没有直接提供该项功能,因此必须借由内存变量完成。由以前的知识知
道,在大量的数据里删除一个数,用数组表示不合适,因为涉及到大量的数据的移动,用链表是合适的,效率高(关于这一点,在《数据结构》这门课有详细的讲解)。因此完成这部分操作要求用链表实现,先将文件里的数据读出组织成链表,在链表上完成插入与删除后,再将链表中的数据写入文件。
7)按某个数据项进行排序生成排序文件的实现思路:排序是在数组里实现。因此先要将文件里的数据读到数组里,将数组排完序
后,再将数据写入文件(一般写入一个新文件)。
因此对每一项功能,关于内存变量的使用有三种方案:一个结构体、结构体数组与结构体链表,实现每个功能应当只会使用其中的一种结构,使用哪种结构取决于你的做的事和使用的算法,比如排序必须用数组,插入与删除必须用链表,其它的根据要求可能会有多种选择,但只有一种时空效率最高,可以从这个角度选择一种结构。
最终实现的软件,只要提供这上面提到的所有功能就算满足要求,至于其它功能,能结合实际要求考虑周全,更好。
三、课程设计的方法
此次课程设计的功能较多,相对于以往所做程序规模较大,因此在编写与调试程序时,特别注意调试程序的方法,以提高效率。具体方法如下:
1)将功能分解,细分至每个函数要完成的功能后,编写一个函数测试一个函数,测试完全通过后,再编写测试下一个函数,然后
再将这些函数组合后完成相应功能。将全部功能完成后,再组合成软件,最后再加入菜单控制。从《软件工程》的角度看,这是从底至上的开发过程,这样便于测试与实现,出错也便于定位错误位置。不要一开始的精力放在菜单控制上,也不要一开始就将所有的函数都编写、组装并一起调试,这样出错位置较难定位,调试效率低。
2)在编程过程中,不要使用全局量。它不仅长时间大量占用内存,而且完全违背实际的软件开发要求。需要使用的数据,应当通
过参数传递。就这个软件而言,数据主要是从文件读写,所以连数据传递都不太多。
3)在列出的这些功能里,有些操作是很多功能都使用的,比如从某个文件读或写数据,这些可以编写成一个函数(当然里面可能再
调用其它函数)。这些一次编写严格测试通过后,可以被其它函数调用,避免代码重复,并提高效率。
4)因为软件有一定的规模,所以必须拆成多个文件,这样附合实际项目开发需求,同时便于编写与调试。在做这个软件过程中,必须学会这种方法。
5)有问题要及时交流,尽早动手编写代码,在编写过程中掌握知识,同时发现问题并进行调整,在这个过程中一步步积累经验。
随着人工智能技术的不断发展, 基于人工智能的解决方案被应用到各个领域中。其中教学领域也在引入该技术不断改进教学手段, 如计算机辅助教学等。为了提高考试工作效率节约开支和避免教师为学生划定考试范围来应付考试, 利用基于题库的考试软件进行考试成为首选。考试系统可以很好地完成选择判断等客观题的考试评卷工作, 但在评阅主观试题时效率就显得比较低下。一是主观题往往答案不唯一, 利用简单字符匹配很难公平给出成绩。二是当有多个答案时难以逐一列举, 同样造成评判不公。
二、C程序设计操作题考试系统总体设计
该系统用户分为两类, 分别是学生用户和教师用户。教师用户具有出题评分等权限, 学生用户抽题和答题权限。本系统分为出题模块、评分模块、成绩处理和答题模块。当用户登录时可以利用数据库用户表的权限字段加以区分用户角色, 然后分配给不同权限。用户如果是教师则在主界面显示出题评分等操作, 如果是学生用户则显示试题抽取操作, 学生抽取试题后进入答题界面。学生答题结束则提交操作后的C源文件。具体层次图如图1所示。
三、阅卷算法的实现
在该系统中除阅卷模块外, 其他模块的功能在实现方法上有比较成形的技术都能够比较容易达到设计目标。但对于评卷模块来说, 它要完成的是主观操作题的评阅因此实现起来有一定的难度。经过多次的实验和资料查阅, 最后选用了人工智能技术来实现。
(一) 基本设计思想。
本系统中主要包含三类操作题分别是程序改错, 程序填空和函数编写。每类题的答题点都有可能出现多种答案。可以将每个答题点的答案存放到答案表中, 答案表也可以被称为阅卷知识库。
对于改错和程序填空题在知识库中每个答题点都可以有多个可选答案相对应, 并且不同的答案具有不同的得分权重, 由此每个答题点Point有多个带有不同权重w的答案Ans构成:
每个答题点的成绩最终为某个Ans的权重w成绩该答题点的分值得出, 因此权重w取值为[0, 1]。
对于函数编写题则在答案知识库中与改错和程序填空有一定差别, 在该知识库中将答案视为一个文本T, T应该由多个关键词Key构成。每个关键词Key组合成T, 每个关键词Key在答案文本中有一定的得分比例Percent。由此每个题的答案可以为:
该式中得分比例累积为1, 该题得分为最终得分比累加和乘以该题分值。
(二) 基本数据表设计。
对应上述设计思想, 设计如下两个表作为阅卷知识库。
对于改错程序和程序填空题阅卷知识库表结构设计如表1:
对于函数编写题阅卷知识库表结构如表2:
(三) 算法实现。
当学生完成答题后, 将答题结果提交到学生答题答案表。表中包含学生学号、姓名、试题套号、程序改错答题结果、填空题答题结果和函数编写答题结果。算法按照改错题评阅、填空题评阅和函数编写题评阅顺序进行。
1.改错题与填空题评阅算法。首先提取答案表中的一条记录, 然后提取该记录中的试题各个答题点结果, 提取后放入相应变量内。提取该试题套号, 以试题套号、类别号和具体答题点号为条件在评阅知识库表Testans中查找相关记录, 将提取的结果集中记录的具体答题点的具体答案与学生答题点的答案进行字符串匹配测试。当某个评阅知识库表中的某个具体答案与学生答题结果中的答案匹配成功时则提取该记录的权重值然后乘以该答题点分值。依照上述方法重复进行直至所有答案表中的相关答案全部进行了匹配运算的出具体分值为止, 将所有分值累加, 结果为该考生改错与填空题最终成绩。
2.函数编写题评阅算法。在考生答案记录提取后, 将其中的函数编写答案字段提取, 此字段中存储考生函数编写题的程序文本。同时以试题套号为依据在函数编写题评阅知识库表progans中搜索该套试题答案关键词记录。得到结果集为该题程序中应该出现的关键词结果集, 以每个关键词为依据在考生函数编写题答案文本中搜索该关键词, 一旦匹配成功则累加该关键词对应的权重。结果集每个关键词都进行以上算法后累加的权重值乘以该题的分值即为考生得分。
(四) 知识库知识累积实现。
上述算法基本解决了当主观题答案出现多种变化时不能公平给分的矛盾。但是由于出题者对知识库构建时难免发生遗漏, 因此评阅试题知识库最初的质量并不一定很高。为了能使知识库中知识更加丰富本算法中还增加了知识库知识累积处理。主要是通过人工干预评阅试卷, 当教师对考生答题点逐个人工查看时, 如果知识库中没有出现相应答案但又是正确答案时评阅教师可以将该答案加入知识库。添加到知识库后的答案同时给予一个适当的权重, 如果是函数编写题在加入新的评阅关键词后所有的关键词权重要重新分配使得权重累加结果为1。
这样经过几轮人工干预评阅试卷知识库中的内容将更加丰富, 对以后的评阅更加公平起到促进作用。
四、结语
本文主要阐述了基于人工智能原理的C语言操作考试题的评阅算法。算法中主要利用了知识库功能和简单字符匹配算法。为了解决知识库内容起初不够丰富的问题, 设计了知识库知识累积算法。经过测试表明在经过若干轮人工干预后基本实现了公平评阅试卷。
参考文献
[1].蒋秀莲.基于人工智能技术的智能教学系统研究与设计[J].Microcomputer Applications, 2009
[2].李闯, 常锐.基于人工智能原理的考试系统[J].长春工业大学学报 (自然科学版) , 2009
关键词:C语言 解题技巧 变量 一般方法
大多数学生都有学好C语言的愿望,但由于学习方法不对,最终事与愿违。有没有一种方法能够将学生这种好学的愿望转变成学习动力,真正将知识转变成学生行万里路的智慧呢?
笔者从自己的教学实践出发,以一道C语言填空题的解题方法为例,与广大同行共同进行探讨。
例:10个小孩围成一圈来分糖果,教师分发给每个小孩的糖果数依次为12、2、8、22、16、4、10、6、14、20,然后所有的小孩同时把自己的糖果分一半给右边的小孩,糖果数变为奇数的小孩向老师补要一块,问经过多少次调整后每个小孩的糖果数都一样,此时的糖果数为多少?请填空。
#include
void main()
{int i,k,f,y,b[10],a[10]=
{12,2,8,22,16,4,10,6,14,20};
k=1;f=0;
while(f!=1)
{ ①;
for(i=0;i<=9;i++)
{ ②;
b[i]=a[i];}
for(i=0;i<=9;i++)
{y= ③;
a[y]=a[y]+b[i];
if(a[y]%2!=0) ④; }
for(i=1;i<=9;i++)
if(a[0]!=a[i]){f=0;break;}
k++;}
printf(“k=%d,a[0]=%d”,k,a[0]);}
C语言的填空题首先要从变量入手,弄懂变量的作用,然后通读整个程序,读懂程序的算法,接下来再结合一些技巧和经验来解答,问题就能迎刃而解了。
要读懂变量的作用,应该从变量的初值入手。对于变量的初值,笔者做了一些总结,如某一变量的初值为0(假如s=0),那么变量s一般情况下可以用来求和、进行统计、状态变量、数组下标等。如果出题者严格遵循变量名的命名规则,那么学生应该可以很快读懂变量的作用。有时初值为1的变量也可以用来进行统计,关键要善于归纳、总结。特别要提醒的是,如果初值作为状态变量的话,那么该变量的数值一定是在0和1之间变化的。
另外,变量k是用来统计分糖果的次数,变量f是一个状态变量。既然这样,状态肯定在1和0之间变化。而整个程序只有一处f=0,那么肯定有一空是变量f=1,不然这道题就成了一个死循环。结合程序,第①空应该填写f=1。
a数组第一次用来存放最初的糖果数,循环之后就变成用来存放分糖果后自己以及左面小孩给的总糖果数。统一起来a数组就是用来存放每次分之前各个小孩的糖果数。因为题目中要求同时分糖果,程序b数组是用作一个中间数组,用来存放每个小孩要分给右面小孩的糖果数,即每个小孩手中糖果数的一半,故b[i]=a[i]。所以,第②空可填成“a[i]=a[i]/2”。
经过仔细审题,再结合程序,第④空的答案也就显而易见了。故此空可填“a[y]++”。另外,学生在解题时,要注意体会题目中的“同时”一词。
经过前面的几个步骤之后,学生就会发现第二个for语句是真正意义上的分糖果的过程。学生很快得出答案“y=i+1”。但是,这个答案是错误的。把两个变量的变化关系写出来,为:
y=1时 i=0
y=2时 i=1......
y=9时 i=8
y=0时 i=9
前面9处两个变量间的关系均可以写成“y=i+1”。但最后一处如果也用这样的关系,那么得出“y=9+1=10”。显然此处的变量y不等于10。这时就要采用取余方法,就得出答案为“y=(i+1)%10”。
参考文献:
[1]谭浩强.C程序设计语言[M].
北京:清华大学出版社,2005.
[2]方明.陶行知教育名篇[M].北京:教育科学出版社,2006.
[3]李崇泰.C语言案例教程[M].
北京:电子工业出版社,2005.
题目01:在一个已知的字符串中查找最长单词,假定字符串中只含字母和空格,空格用来分隔不同的单词。
[cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
// 用数组定义一个字符串
char array[50] = “zha junju zhamengjun z mengjun”;
char *str = array;// 定义指针变量str,指向数组array
int len = 0;// 定义变量len,用于计数
int max = 0;// 定义变量max,存放最长单词的长度
char *p = 0;// 定义指针变量p,指向最长单词的首字符
10.11.// 判断指针当前指向的字符是不是'
12.while(*str!= ')13.{
14.if(*str!= )// 判断字符是不是空格 15.{
16.len++;// 计数加1 17.18.// 判断最大长度跟len长度的大小
19.if(max < len){
20.max = len;// 如果max小于len,将len赋值给max
21.p = str1];
46.} 47.else
48.{
49.sumDaysOfMonth += pingYear[monthDay;// 定义整型变量days,存储一个月内相隔的天数
54.int sumDays = 0;// 定义整型变量sumDays,存储两个时间点相隔的总天数
55.56.// 得到两个时间点相隔的总天数
57.sumDays = sumDaysOfYear + sumDaysOfMonth + days;58.59.printf(“两个时间点相隔%d天n”, sumDays);60.61.// 根据相隔的天数,判断小明遇到的人
62.if((sumDays % 2 == 0)&&(sumDays % 3 == 0))63.{
64.printf(“小明既结识了帅哥又结识了美女!n”);65.}
66.else if(sumDays % 2 == 0)67.{
68.printf(“小明结识了帅哥!n”);69.}
70.else if(sumDays % 3 == 0)71.{
72.printf(“小明结识了美女!n”);73.} 74.else
75.{
76.printf(“小明没有结识帅哥和美女n”);77.} 78.}
心得体会:(1)利用for循环遍历,if条件来判断是平年还是闰年,求出相隔年数的累加的天数(2)同理,利用第一步的方法,求出相隔月数的累加的天数,只是要注意每月的天数,根据平年和闰年的不同分别保存在两个不同的数组中,以便利于累加
(3)将输入的日减1,计算出当月相隔的天数,最后求出两个日期相隔的总天数。(4)根据总天数取余2和3,判断出小明当天结识的是美女还是帅哥。
题目05:提示用户输入一个正整数n,利用while循环计算并输出:1-2+3-4+5-6+7…+n的和。
[cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
// 1.定义变量存储用户输入的整数
int n = 0;
// 2.判断n是否为正整数
while(n <= 0){
// 2.1 提示输入
printf(“输入一个正整数:n”);
10.11.// 2.2 让用户输入
12.scanf(“%d”, &n);13.} 14.15.// 3.计算阶乘
16.int sum = 0;// 存储计算结果
17.int current = 0;// 当前要累加的数值
18.while(current < n){ 19.current++;20.21.// 如果是偶数,就减
22.if(current % 2 == 0){ 23.sum-= current;24.} else { // 如果是奇数,就加
25.sum += current;26.} 27.} 28.29.// 4.输出结果 30.printf(“%dn”, sum);31.32.return 0;33.}
心得体会:
(1)确保从键盘上输入的是一个整数(用while来判断);(2)用while循环来遍历从1到n的值;
(3)通过奇偶性判断所要累加数值的正负性,奇数就累加,偶数就累减。
题目06:提示用户输入一个正整数n,计算并输出n的阶乘结果:1*2*3*…*n。
[cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
// 1.定义变量存储用户输入的整数
int n = 0;
// 2.判断n是否为正整数
while(n <= 0){
// 2.1 提示输入
printf(“输入一个正整数:n”);
10.11.// 2.2 让用户输入
12.scanf(“%d”, &n);13.} 14.15.// 3.计算阶乘
16.int result = 1;// 存储计算结果
17.int current = 1;// 当前的乘数
18.while(current <= n){
19.result *= current;// 累乘每次的乘数
20.current++;// 乘完一次就++
21.} 22.23.// 4.输出阶乘结果
24.printf(“%d!= %dn”, n, result);25.26.return 0;27.}
心得体会:
(1)可以利用for循环或者while循环进行遍历,利用累乘即可求出值。(2)还可以利用递归来做,更简单。
题目07:编写一个函数,判断某个字符串是否为回文。回文就是从左边开始读 和 从右边开始读 都是一样的,比如“abcba” [cpp] view plain copy print?
1.2.3.4.5.6.7.8.9.int main(){
printf(“%dn”, isHuiwen(“a”));
return 0;}
/*
返回1代表是回文
返回0代表不是回文
10.*/ 11.int isHuiwen(char *str)
12.{
13.// 1.定义一个指向变量left指向字符串的首字符
14.char *left = str;
15.// 2.定义一个指向变量right指向字符串的末字符
16.char *right = str + strlen(str)1)+ count(n);// 求出每一个阶乘的累加和
58.} 59.60.// 定义和求出一个累加和
61.int count(int n)
62.{
63.if(n == 1)64.return 1;
65.return count(n1;22.23.// 如果左边元素的下标 < 右边元素的下标
24.while(left < right)25.{
26.// 利用中间变量交换两个元素的值
27.int temp = array[left];28.array[left] = array[right];29.array[right] = temp;30.31.// 交换一次后,左边元素下标增加,右边元素下标减小
32.33.left++;34.right--;35.} 36.}
心得体会:
(1)首先要明白一点,为什么不能通过sizeof(array)/ sizeof(int)来求出数组元素的个数?因为当数组作为参数传递的时候,函数的参数array实际上当做变量来存储传来的数组首元素的地址。而每一个指针变量占用8个字节。
(2)分别拿出数组首元素和数组尾元素,然后利用中间变量交换两个元素的值。(3)利用while循环,遍历数组元素,并使left< right保证循环到中间即可,否则每个元素又进行一次交换,结果值没有改变。
改错题总的出错分为两大类:语法出错和逻辑出错,此类题目是比较简单的题型,因此也是最容易把握的知识点。
注意:错误都是出现在/**********found**********/下面的这一行。只是下面的这一行,其他行都没有错误。不能修改其他行。做题方法:
1.首先拿到题目,先将题目详细看看(包括题干),看清题目对做题时很有帮助,特别是一些逻辑性较强的题目很有用。有的同学可能会觉得题目太难了不想去读,其实在做改错题目时即便你不会也能把题目做对。
2.按照以下的步骤解决题目中的错误的内容,对应着修改。修改完之后保存即可。一 语法错误
1.关键字出错:在二级C语言考试中仅仅考查学生对基本的关键字掌握情况,比如:整型就是int 不是Int,知识点简单。(1)6题第二个空:If应该改为if;(2)1 2题第二个空:If应该改为if;(3)2 3题第一空:dounle 应该改为double;(4)3 5题第二空:I f应该改为if;(5)3 6题第二空:Double 应该改为double。(6)2题第二空:wihle 应该改为while。
2.格式出错:在考试中主要考查学生细心的程度,最喜欢考简单的知识点。
(1)少分号:2题第一空、28 题第一空、29 题第一空、34 题第二空、37 题第 二空、48 题第二空。考查的知识点:分号是语句结束的标志。
(2)for 语句的格式:for(;;)中间用分号隔开不是用逗号隔开。3题第二空、6 题第一空、14 题第二空都是出现了for 语句格式出错的问题。
(3)if 语句的格式:if(表达式),if 后面必须加上小括号。3 7题第一空if 后面必须加上小括号。
(4)命令行格式出错:17 题第一空中include 命令少了#,正确的形式应该是#include “std io.h”;
(5)常用函数格式:
A 22 题第一空中n=strlen[aa]应该改为n=strlen(aa),记住函数名后面一定是花括号,不能是方括号,方括号只有在数组下标中用到。
B 4 9题第一空int j,c, float xa =0.0;应该改为int j,c;float xa =0.0;记住:要同时定义两种以上不同的数据类型的时候必须在不同类型的数据中间加上分号。
C 4 1题第二空scanf(“%d”,a[i][j]);应该改为scan f(“%d”,&a[i][j])。考查scanf 输入函数的格式。
(6)数组:所有老师讲到数组时候必须统一将此知识点给学生回顾一遍!A数组下标引用时候只能用反括号。2 8题第二空t(k)=b(j)应该改为t[k]=b[j]。
B二维数组中的列下标不能省略。4 6题第一空必须将数组列下标补充完整。A[][3](7)常用符号出错:
A C语言中大于等于符号是“>=”,但是在考试的时候可能出现将它写为“=>”,49 题第二空就是这样的错误。
B当题目中需要比较某个字符是否是大写字母或是小写字母、数字时,必须将“与”、“或”、“非”记住。判断某个字符是否是大写字母:char ch;if(ch>=’A’&&ch<=’Z’)printf(“%c”,c h);判断某个字符是否是小写字母:char ch;if(ch>=’a’&&ch<=’z’)printf(“%c”,c h);判断某个字符是否是数字:c har ch;if(c h>=’0’&&c h<=’9’)printf(“%c”,c h);判断某个数字是否能同时被5和7整除:
int i=35;if(i%5==0&&i%7==0)printf(“%d”,i);判断某个数字是否能被5或7整除:
int i=35;if(i%5==0||i%7==0)printf(“%d”,i);判断某个数字是否是偶数或奇数:
int i=35;if(i%2==0)printf(“%d是一个偶数”,i);else printf(“%d是一个奇数”,i);
二 逻辑错误
1.使用了没有定义过的变量:这种题目可以通过编译查找出出错的地方并改之。(1)0(零)和o(字母欧)的区别:19 题第一空、2 3题第二空、43 题第二空。(2)P(大)和p(小)的区别:3 3题第一空。(3)普通变量大小写的区别:int a=10;A A有的变量在定义的时候是小写但是在使用的时候是大写,或是在定义的时候是大写在使用的时候却是小写。B大小写没有出错,程序中直接使用了没有定义过的变量,要求学生更加细心的读题目。4 5题第二空使用到了i这个变量,但是我们在定义行中没有找到i的定义,因此在做题时候要上下结合来分析题目。22 题第二空c=aa [j]中的c没我们一直用心做教育!有定义过,参看定义的语句不能发现应该是c h=aa [j]。3 8题第一空要求将后面用到的变量s1 定义完整。2.运算符号出错:
(1)C语言中除号是“/”不是“”
(2)赋值号(=)和等号(==)混淆,记住在if 语句中出现赋值号(=)一般是讲它改为等号(==)。14 题第一空和29 题第二空都是这样的错误。3.表达式的取值范围出错:
当遇到计算表达式的累加或是累减时,一定用到循环来完成相应的操作。例如:改错题第七题中要求计算t=1-1/2*2-1/3*3-„.-1/m*m这个表达式,在这个题目中第一空有这样的f or 语句:for(i=2;i 这个考点同样考查表达式的相关知识。例如:改错题第七题中要求计算t=1-1/2*2-1/3*3-„.-1/m*m这个表达式,在这个题目中第一空有这样的for 语句:for(i=2;i 遇到这种题目在改错题中最容易考查的是大于(>)、小于(<)等符号的变化,当你查找出错的地方没有语法错误,也没有使用了未定义的变量时,记住这个口诀:将大于改为小于,将小于改为大于!6.函数的返回值及其参数:重点 int fun(int x, int y) main(){ { if(x>y)return x; int a,b,c;else return y; scanf(“%d%d”,&a,&b);} c=fun(a,b); printf(“最大值为:%d”,c);}(1)函数的返回类型:在上面的例子中fun 函数的返回值是int,如果将int 改为doubl e,很明显是错误的。从两个地方来看,首先return x;中x的类型必须和fun 函数中的返回值相同,x是整型。其次c=fun(a,b);中fun 函数将一个返我们一直用心做! 回值赋值给了变量C,说明函数返回的值一定是整型。记住:函数调用、函数的返回值和return中数据类型必须一致! (2)return后面的表达式及其类型:1 9.2、3 0.2、39.2(3)函数传递的参数: int fun(int x, int y) main(){ { if(*x>*y)return*x; int a,b,c;else return*y; scanf(“%d%d”,&a,&b);} c=fun(&a,&b); prin t f(“最大值为:%d”,c);} 一定注意:传递的是值还是地址。比较上面两个例子大家很容易区别传递的是值 还是地址,第一个例子调用函数的时候传递的是值,因此在f u n函数的形参中x 和y仅仅是普通的变量,而第二个例子中调用函数的时候传递的是地址,因此在 f un 函数的形参中x和y就是两个指针变量。5.1、13.1、17.2、2 0.1、2 5.2依据刚 刚讲的方法很快就能找出出错点。(4)指针的使用: int fun(in t *x, in t *y){ if(*x>*y)return*x;else return*y;} 在此例中f un 函数的形参是x和y,它们的类型是整型的指针,那么在f un 函数的函数体中使用到x和y的值时一定要区分:x>y比较的是x和y所存变量地址的大小,*x>*y才是比较值的大小。如果存在*x=x+y;的类似情况一定是错误的,必须要将赋值号的左右两边的类型变为一致。5.2、5.3、20.2、24.2、35.1、44.1 一定特别注意!7.数组: (1)数组下标的初始值:数组下标从零开始,到长度减一结束。考试技巧:当出错行在定义部分时,先检查该定义语句有没有语法错误,再看定义语句中的变量有没有赋初始值,最后看变量赋的初始值有没有正确。定义初始值有窍门,遇到累加时,用来存放累加和的变量一般赋值为零,遇到求阶乘时,用来存放阶乘值的变量一般赋值为一。8.1中的变量k赋值为1,但是我们说了数组的下标只能从0开始,因此将1改为0就行了。 (2)普通变量的初始值:依照上面的技巧,我们同样可以将它延伸到普通变量中。4 1.1中需要将变量的值进行初始化,题目中有这样的要求:“输出主对角线元素之和”,既然这样我们很肯定初始值应该为零,接着我们看看是给哪个变量赋值?看printf(“su m =%d”,su m);输出的是sum 的值,说明sum 就是我们要补充在横线上的变量。10.1也可以用同样的方法来分析。 (3)数组和字符串长度减一:当使用数组中的元素时,最大值只能到数组长度减一。字符串以’’作为字符串的结束标志,因此取值最大也只能到字符串长度减一。25.2中输出p r in t f(“%d”,a a[i]);分析程序知道i在此时就是数组的长度,我们要输出数组中的元素,最大就只能到a a[i-1],因此改为printf(“%d”,aa[i-1]);就行了。4.1和38.2中考查的是字符串长度减一的问题。 (4)数组下标往后走:当题目要求将一个数组中的元素直接赋值或是以某种方式复制到另一个数组中时,接受数据的数组中的下标要自增9.。1中s[j]=s[i];将s[i]中的值复制给s[j],联系上下函数段发现i是一个变量被for语句控制,从0开始直到遇到’’结束。i一直在加但是j没有变化,因此改为s[j++]=s[i];即可。类似的题目有:25.2、26.2、32.1、50.2。8.特例: 部分题目的解答很特殊,需要根据上下文中的内容来解答题目;例如10.1,在此题目中,定义了变量t=0;根据下面程序中的循环,s=0.0,首先加的是变量t的值,即s的值是从0.0开始的,s得到的值是:s=0.0+0.0+1.0/3+„„,观察表达式的值,这个少加了第一项1.0的值,所以变量t的初值不能是0.0,只能是1.0,或者是1;21.2中的这个题目中,表达式的值是从2/1+3/2+5/3„„即表达式的规律是从第一项开始,以后的每一项的分子是前一项的分子和分母的和,分母为前一项的分子。所以该题目的第二个空中的内容应该是:c=a;a+=b;b+=c;这一条语句中a代表的变量的分子,b代表的是变量的分母,执行第一个表达式的值,把a的值赋值给变量c,第二个表达式是把分子和分母的和相加复制给变量a,第三个表达式是把原来a的值和变量b的值相加之后得到的值赋值给变量b,则变量b得到的值也为分子分母的和,不满足题意,而分母应该为原来分子的值。第三表达式的值应该改为:b=c;9.素数 掌握求素数的格式: 判断一个数是否是素数的方法很多,只用掌握一种即可,首先掌握判断一个素数的方法; 素数的定义: 1、是自然数 2、只能被1和本身整除,2到本身减一之间的数不能整除该数。判断m是否为素数的格式: for(i=2;i 求素数的题目只要把格式给带进去就可以了。1 1.1这个题目中只用改为“==”。29 题也是一样的解答方法。10、链表 链表的题目分为带头结点的和不带头结点的链表; 1、带头结点的链表 带头结点的链表的第一个空的改为:p=h->next;第二个空改为:p=p->next;.2、不带头结点的链表 PS:字符数组和字符串的最明显的区别就是字符串会被默认的加上结束符’’。void test3(char* str1){ char string[10]; if(strlen(str1)<=10) { strcpy(string, str1); } } 这里的问题仍是越界问题。strlen函数得到字符串除结束符外的长度。如果这里是<=10话,就很明显越界了。 小结:上面的三个找错的函数,主要是考查对字符串和字符数组的概念的掌握以及对strcpy函数和strlen函数的理解。 2、找错 DSN get_SRM_no(){ static int SRM_no; int I;“> for(I=0;I { SRM_no %= MAX_SRM; if(MY_SRM.state==IDLE) { break; } } if(I>=MAX_SRM) return(NULL_SRM); else return SRM_no;} 这里for循环的判断语句是后来我加上的,估计在网上流传的时候被人给弄丢了,根据对程序的分析,给补上了。估计错误应该不是这儿。 简单的阅读一下这个函数,可以大概的可以猜测出这个函数的功能是分配一个空闲的SRAM块。方法:从上次分配的RAM块后的RAM块开始检测SRAM每个RAM块,看是否是IDLE状态,如果是IDLE则返回当前的RAM块的号SRM_no。如果所有的RAM块都不是IDLE状态,则意味着无法分配一个RAM给函数调用者,返回一个表示没有RAM可分配的标志(NULL_SRM)。 经过上面的分析,则这里可以知道,这个函数的错误是for循环里面没有给SRM_no这个变量累加1。 3、写出程序运行结果 int sum(int a){ auto int c=0; static int b=3; c+=1;b+=2; return(a+b+c);} void main(){ int I; int a=2; for(I=0;I<5;I++) { printf(”%d,", sum(a)); } } 运行结果是:8,10,12,14,16, 在求和函数sum里面c是auto变量,根据auto变量特性知每次调用sum函数时变量c都会自动赋值为0。b是static变量,根据static变量特性知每次调用sum函数时变量b都会使用上次调用sum函数时b保存的值。 简单的分析一下函数,可以知道,若传入的参数不变,则每次调用sum函数返回的结果,都比上次多2。所以答案是:8,10,12,14,16,4、func(1)= ? int func(int a){ int b; switch(a) { case 1: 30; case 2: 20; case 3: 16; default: 0; } return b;} 在 case 语句中可能忘记了对变量b赋值。如果改为下面的代码: int func(int a){ int b; switch(a) { case 1: b = 30; case 2: b = 20; case 3: b = 16; default: b = 0; } return b;} 因为case语句中漏掉了break语句,所以无论传给函数的参数是多少,运行结果均为0。 5、a[qp] = a[2] = 2; 6、内存空间占用问题 定义 int **a[3][4], 则变量占有的内存空间为:16位系统24,32位编译系统中是48。PS:公式:3 * 4 * sizeof(int **)。 7、程序编写 编写一个函数,要求输入年月日时分秒,输出该年月日时分秒的下一秒。如输入2004年12月31日23时59分59秒,则输出2005年1月1日0时0分0秒。 void ResetTheTime(int *year,int *month,int *date,int *hour,int *minute,int*second){ int dayOfMonth[12]={31,28,31,30,31,30,31,31,30,31,30,31}; if(*year < 0 || *month < 1 || *month > 12 || *date < 1 || *date > 31 || *hour < 0 || *hour > 23 || *minute < 0 ||*minute > 59|| *second <0 || *second >60) return;if(*year%400 == 0 || *year%100!= 0 && *year%4 == 0) dayOfMonth[1] = 29; if(*second >= 60) { *second = 0; *minute += 1; if(*minute >= 60) { *minute = 0; *hour += 1; if(*hour >= 24) { *hour = 0; *date += 1; if(*date > dayOfMonth[*month-1]) { *date = 1; *month += 1; if(*month > 12) { *month=1; *year += 1; } } 关键词 递归;循环;C语言 中图分类号 TP 文献标识码 A 文章编号 1673-9671-(2010)121-0031-01 递归作为一种正对实际问题的程序设计解决方案,在整个编程语言学习及程序设计方面有着极其重要的地位。而在实际的教学环节中对于这样一种具有很高使用价值的编程技术,在讲解上和学生理解上还存在着一定困难。 1 递归的基本条件 循环结构是结构化程序设计的三种结构之一,主要应用在处理某些需要重复执行特定语句的情况下,通常在使用循环时我们需要明确三个重要条件: 1)循环的初始条件。可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的问题的规模有规律地递减。也就是说解决问题的方法相同,调用函数的参数每次不同(有规律地递减),如果没有规律也就不能运用递归方法求解。 2)循环的中止条件(或在什么情况下循环运行)。每个子问题必须比原来问题的规模更小,即使小一号也行,如果能够迅速减小规模更好。 3)程序有趋于结束的趋势。必定要有一个明确的结束递归的条件。当问题的规模达到一定程度时(一般的时候应该是规模足够小),问题的解是已知的,在这个地方能结束递归调用,不然可能导致系统崩溃或陷入死循环。 而递归作为一种算法,在数学上我们时这样定义它的,以n的阶乘为例: 0!=1(*) n!=n*(n-1) 当n>0时 (**) 其中(*)称为基本实例(基本实例的值必须是直接获得的);(**)称为递归归约。递归被定义为:自身定义为一种含有自身简化的形式。那么从形式上我们可以清楚的看到:①每个递归定义必须有一个(或者多个)基本实例;②递归归约最终归结到基本实例;③在基本实例处停止递归。从中我们不难发现递归的算法形式条件和循环的条件是非常类似的。 那么在运行递归函数时,逻辑上我们可以认为递归函数有无限的自身拷贝,完成某个递归调用后,控制返回到先前的调用环境。这同循环算法在思想上也是异曲同工的。所以我们也完全可以编写一个循环结构来替代递归。 造成这种可替代性的主要原因是因为目前为止的编译系统处理递归函数时,在编译之后都是自动将递归转化为循环的。但是和循环不同的是,编译后的递归需要创建一个内存栈来存储递归过程中的临时变量,对于递归函数的调用和返回操作,则分别对应栈的入栈和出栈。 因此任何一个递归程序都可以通过引入堆栈的形式来转化为循环,这种转化其实就是模拟计算机实现递归的过程。你可以考虑人脑来计算递归的过程:先倒过来向前递归,到达最初点以后再正过来向后递推,堆栈的作用就是记住过程中的临时变量。虽然这样做只不过是模拟递归的执行过程,将原来由编译器实现的事情在程序中用代码实现了一遍,但是确实可以通过循环和堆栈的数据结构特性来实现递归的算法和递归函数的功能。 2 递归问题分析 递归程序最少有两种方法可以写出。第一种是想办法把问题转化为数学表达式,能用数学表达式写出的,可以直接把数学表达式变为程序;第二种是按照递归的基本条件,分析所给问题的结构,一个问题总具有一定的规模,想办法把规模变小,使小规模的问题与原问题具有相同的解法,当规模足够小时,该问题的解是已知的,这时可以套用后面所讲的一般递归程序的结构来写递归程序。 例1:求n!。经过分析n!可以表示为 假定n!是一个long型,则直接写出程序如下: long func(int n) //求n! {if(n==1 || n==0) return 1; return n*func(n-1); } 例2:求Fibonacci数列(非波纳契)的第n项,该数列表示如下: 按照例1的方法,写出程序如下: longfib(int n) { if( (n==1||(n==2) ) ① return 1; else ② return fib(n-1)+fib(n-2); } 如输入n=4,程序(具体程序用行标号代替)的执行过程如图1所示,通过分析以上程序,大部分学生对递归函数的执行过程有了较为清晰的认识。 例3:用递归的方法将一个正整数n按位输出,如输入2008,输出2 0 0 8,n的位数不确定,可以是任意正整数。 分析:①函数应该是处理整数n,并且是输出,用PrintNum(int n)来实现这一功能;②如果n的规模很小(只有1位),可以直接输出后结束程序;(这里应该知道n的位数是问题的规模);③把问题的规模变小,去掉n的某1位(在实际讲解时是去掉个位,因为去掉个位比较容易一些,我们用k=n%10保存个位,n=n/10就可以去掉个位),余下位形成一个新的规模比原数小的数,再在新数上递归调用函数,这个新数按要求输出后,去掉的这一位因为是个位,所以应该把它输出到新数的后面。 通过以上分析,写出程序如下: void PrintNum(int n)/*该函数能把n按要求输出*/ { int k; if(n<10){ printf("%2d",n); return ;} /*如果规模很小,则直接处理后结束程序*/ k=n%10; /*在n中提出个位数字*/ PrintNum(n/10);/*把规模更小的子问题递归调用函数来处理*/ printf("%2d",k); } 通过以上三个例子,学生对递归函数有了较为清晰的认识,对递归程序的写法有了一定的掌握之后,我们可以总结出一个一般递归程序的结构如下,几乎所有递归程序都可以按照这个结构去分析并写程序。 ReturnType Function(问题描述及规模)/* 在程序内部我们应该认为函数Function已经具备处理这类问题的功能,它可以直接被调用 */ 我们根据这个结构,要求学生回过来简单看一下前面的三个例子,多数学生便会对递归有更为深入的认识。之后引入汉诺塔问题,该问题的分析方法类似于例3,并套用这个结构,让学生自己试着写该问题的程序,可以发现有一部分学生能自己写出递归函数来。 3 总结 在C语言中,函数一直是学习的重点和难点,递归又是最难掌握的一类函数,本文从实际教学需要出发,首先分析递归的基本条件,从由易到难的顺序,设计了一些教学实例,重点让学生了解递归函数的写法以及执行情况,最后总结出递归函数的一般程序结构,使学生能正确设计递归程序,在实际教学中取得了良好的效果。 参考文献 [1][美]Robert Sedgewick.C算法(第一卷).人民邮电出版社,2004 C语言的非数值处理功能很强,因此它被广泛地应用于数据库管理系统和应用软件。大多数的关系数据库管理系统,如dBASE,Fox$ASE,ORACLE等,都是由C语言编写的。各种不同部门的应用软件也大都是用G语言开发的,C语言在开发数据库应用软件方面应用很广,深受开发者的欢迎。 2.图形图像来统的应用程序方面 C语言在图形图像的开发中也有着广泛的市场。很多图形图像系统,如AutoCAD通用图形系统等,就是使用C语言开发的,并且在这些图形系统中可以直接使用C语言编程,实现某些功能。C语言编译系统带有许多绘图功能的函数,利用这些函数开发图形应用软件十分方便。所开发的应用程序常用C语言编写接口界面,这样既方便又灵活,效果很好。这是因为该语言提供有图形处理功能,便于实现图形图像的各种操作。因此,C语言在图形图像的应用方面很好地发挥了它的作用。 3.编写与设备的接。程序方面 C语言不仅在建立友好界面方面有着广泛应用,如下拉式菜单、弹出菜单、多窗口技术等;而且在编写与设备的接口程序方面也有着广泛应用。这是因为C语言不仅具有高级语言的特性还具有低级语言的功能,因此,在编写接口程序方面十分方便,有时它与汇编语言一起使用,会显示出更高的效率。 4.数据结构方面 由于C语言提供了十分丰富的数据类型,不仅有基本数据类型还有构造的数据类型,如数组、结构和联合等,把它们用于较复杂的数据结构〔例如,链表、队列、伐、树等)中显得十分方便,这方面已有许多成熟的例程供选择使用。 5.排序和检索方面 排序和检索是数据处理中最常遇到并较为复杂的间题。使用C语言来编写排序和检索各种算法的程序既方便又简洁。特别是有些排序算法采用了递归方法进行编程,更显得清晰明了。因此、人们喜欢使用G语言来编写这方面的程序。 上述列举了五个方面的应用,但绝不是说C语言的应用仅限如此,而是说在这几个方面目前使用得更多些。C,语言可以说在各个领域中都可以倪用,并且都会有较好的效果。所以,C语言是当前被用于编程的最广泛的语言之一。 另外,C语言是一种结构化程序设计语言,在编写大型程序中也很方便,特别是该语言又提供了预处理功能,其中文件包含在多人同时开发一个大程序时将带来减少重复和提高效率等好处,因此,越来越多的人喜欢用C语言来开发大型程序。 1.计算机二级C语言程序设计考点:C语言的特点 2.计算机二级C语言程序设计考点解析:C语言程序实例 3.计算机二级C语言程序设计考点:指针 4.计算机二级C语言程序设计考点:数组 5.计算机二级C语言程序设计考点:函数 6.计算机二级C语言考点:字符型数据 7.计算机二级c语言试题 8.计算机二级C语言程序设计考点解析:常用的输人函数 9.计算机二级C语言程序设计考点:单词及词法规则 【c语言程序设计补充题】推荐阅读: c语言程序设计基础题10-28 c语言设计程序11-11 c语言递归程序11-06 c语言程序设计实验11-19 c语言源程序10-03 c语言程序设计作业三06-24 C语言图形程序设计报告10-13 汉诺塔c语言程序07-15 c语言程序期末试卷09-22 c语言程序设计复习题10-28华为面试题C语言软件工程师 篇6
基于C语言的递归程序设计分析 篇7
c语言程序设计补充题 篇8