实验六函数与编译预处理(4学时)实验前必须做的操作...............——..新建文件夹:首先在各自对应的计算机ncre(k:)盘上对应座位号文件夹内新建一个文件夹,文件夹的名字为“班级+学号的后两位+姓名”,如座位号为K02,航海1111班、学号后两位是02的、姓名为“张强”的同学,则其对应的文件夹名字是:航海111102张强。
然后在刚才建好的文件夹里面再建立一个文件夹,文件夹为“实验6”。
【实验目的】1、掌握自定义函数的一般结构及定义函数的方法;2、掌握函数的形式参数、实际参数、函数返回值等重要概念;3、掌握函数实参与形参间的“值传递”方式;4、掌握函数声明、函数调用的一般方法;5、掌握模块化程序设计思想,会采用C语言中函数进行模块化程序设计;6、掌握全局变量和局部变量作用域以及全局变量和局部变量的使用方法;7、掌握函数的嵌套调用的程序的执行过程,会使用嵌套调用设计程序;8、掌握使用函数递归调用的程序的执行过程,会使用递归程序解决实际问题;9、理解宏的概念,掌握宏定义。
了解文件包含的概念,掌握其用法;10、学习对多文件程序的编译和运行;11、熟练掌握VC程序调试方法,包括断点的设置和取消,以及单步执行跟踪进入函数和跳出函数的方法。
【实验内容】[实验任务一]:断点的设置和取消、单步执行跟踪进入函数和跳出函数方法练习程序跟踪调试实例6-1:调试示例(请仔细按以下各步骤进行操作):从键盘输入一个正整数n,计算n! 的值,要求定义和调用函数fact(n),计算n!。
Input n: 1010! = 3628800说明:实验五中我们使用了程序运行到光标位置调试程序,本次实验需要掌握设置断点,以及单步调试进入函数和跳出函数的方法。
【操作步骤】:(1)输入源程序,并以error6_1.c文件名最终保存在实验6文件夹里,编译程序,出现警告信息:双击该警告信息,箭头指向“f=fact(n);”这一行,警告信息指出函数“fact”没有定义,而函数“fact”实际上已经定义了,这是为什么呢?因为函数在调用之前,必须先声明。
在主调函数的变量定义前面加上函数声明“long fact(int m);”后,重新编译,连接,都正确。
注意:如果将error6_1.c文件改为error6_1.cpp (即改为C++源程序文件)可见VC++系统对.c文件和.cpp文件在进行编译时,对语法的要求不完全一样,这一点希望同学们注意。
此处错误修改的方法与上面相同。
(2)调试开始,设置2个断点(断点的作用:程序执行到断点处暂停,使用户可以观察当前的变量或其它表达式的值,然后继续运行),先把光标定位到要设置断点的位置,然后单击编译工具条上的(Inert / Remove Breakpoint (F9)),断点就设置好了(如图6.1所示)。
如果要取消断点,只要把光标放到要取消的断点处,单击,这个断点就取消了。
图6.1 设置断点(3)单击编译工具条(go (F5)),运行程序,用户输入n的值10后,程序运行到第一个断点位置暂停(如图6.2所示)。
图6.2 程序运行到断点位置(4))单击(Step Into (F11))进入函数fact()调试,箭头表示程序已经执行到函数fact()内(如图6.3所示)。
(5)使用,在函数fact()中设置程序的第3个断点(如图6.3所示)。
图6.3 进入函数fact( )调试,并在运行时设置断点(6)单击(go (F5)),程序直接运行到断点处,暂停(如图6.4所示),在变量窗口观察到product 的值是-2903040,不正确,因为变量product未赋初值,加上语句product=1后,按以上的步骤,重新编译、连接,运行到第3个断点处,变量窗口中product的值正确。
图6.4 程序从函数fact()开始直接运行到第3个断点的位置(return前面)(7)现在需要从被调函数返回到主调函数,单击调试工具条中的(Step Out (Shift+F11)),程序返回主调函数继续执行(如图6.5所示)。
图6.5 程序回到了主调函数(8)继续单击(go (F5)),程序执行到最后(如图6.6所示),在运行窗口输出10!=3628800,与题目要求的结果一致。
图6.6 程序执行到最后(9)单击终止调试图标(Stop Debugging(Shaft+F5)),程序调试结束。
程序跟踪调试实例6-2:模仿示例调试以下程序,直到输出正确结果,将修改好的程序以error6_2.c为文件名最终保存在实验6文件夹里。
(需要存盘)程序功能:利用函数调用求“1!+2!+3!+4!+...+10!”的值(参见实验教材73页程序填空题第(2)小题)提示:改变main()函数中for循环的循环次数,让程序只求“1!+2!+3!”的值,更容易找到程序的错误。
程序跟踪调试实例6-3:综合调试、改错练习:以下程序有多处错误,请综合运用前面所学过的所有知识和VC++的调试手段,改正程序的各种错误,使其运行出正确的结果。
程序的功能是:利用函数调用求任意两个整数的和。
将修改好的程序以error6_3.c 为文件名最终保存在实验6文件夹里。
(需要存盘)【提示】:①本题中涉及到两个函数,分别是main()函数和sum()函数,而C语言程序中各个函数之间的关系是相互独立的,所以应该将int sum(a, b)函数写在主函数main()函数的外面,即将上面程序中的最后一行的}放在 int sum(a,b) 的上一行;②按以上方式修改以后,编译时会出现如下错误信息提示:Declaration syntax error in function main. 主函数中声明语法错误如何修改请参照ppt课件的C6 函数与编译预处理的第21~23的内容;③经过以上几步修改后,编译时会出现如下错误信息提示,且光标停留在scanf( )处:Possible use of 'x' before definition in function main. 在定义主函数之前可能已经使用了x 原因就是scanf()语句错误。
④修改后重新编译会出现错误信息提示如下所示:Redeclaration of ‘a' in function sum 在sum函数中重复声明了a造成此错误的原因是定义sum( )函数错误,如何修改,请参照ppt课件的C6函数与编译预处理的第7~8的内容;⑤修改完成以后,编译成功,但连接时(即按compile菜单下的Make EXE file选项)时,会出现如下错误信息提示:Undefined symbol '_sam' in module example2.c 在example2.c模块中没有定义符号sam 造成此错误的原因就是:被调函数的函数名与主调函数中调用被调函数的函数名不一致。
修改方法可将主调函数中的sam改成sum以保持一致;⑥修改后重新编译,出现信息提示:Too few parameters in call to ‘sum’ in functionmain 主函数中调用sum函数时参数太少因为被调函数sum(int a, int b)中有两个参数,主调函数main()中的sum(x+y)只有一个参数。
而实参应该与形参保证三一致:即顺序一致,类型一致,数量一致。
⑦因为程序中scanf("%d,%d", &a, &b)中两个%d之间有逗号,所以运行程序时输入的两个整数之间必须用逗号隔开,而不是用空格隔开。
[实验任务二]:补充完整程序,灵活掌握C语言中使用函数调用解决问题的方法。
注意:这里提供了以填空的形式出现源程序参考清单,目的是给同学们一个思路。
最好不要将此源程序清单复制到VC++中,否则会出现多处语法错误。
同学们可以据此思路来填写有关语句,从而完成程序。
在调试程序时,一定要运用前面学过的方法。
程序填空实例6-1:下面函数是实现求[1,3000]能被17或23整除的整数的个数。
请将程序补充完整,并调试运行,直到输出正确结果。
将填空后完整的程序以文件名为blank6_1.c保存在实验6文件夹里。
(需要存盘)程序填空实例6-2:编写一函数接受主调函数实参n(这里n==5),并输出如下图形。
n的值通过键盘输入,并由主函数中的语句来由实现。
将填空后完整的程序以文件名为print1.c保存在实验6文件夹里。
(需要存盘)0***0*0*0***0***0*0*0***0【算法提示】:①定义函数printg(),一个形参n,类型为整型,函数无返回值;②找出主、次对角线上‘0’的规律,使用双层循环控制循环n*n次;③循环体内部主、次对角线上打印‘0’,否则打印‘*’;④在主函数中调用函数printg()。
【程序框架】:本程序中main()是主调函数,printg()函数上被调函数。
程序结构形式如下:#include <stdio.h>void printg(int); /* 因为被调函数printg()在主调函数main()之后,需对被调函数声明*/void main ( ){int a;printf("please input a number: \n");scanf("%d", &a);printg(a); /* 在主调函数main()中调用被调函数printg(a) ,其中a为实参*/}void printg(int n){这部分可按照算法提示的①、②、③步补充完整。
相关提示:如果用i控制行标,用j控制列标,则主对角线的规律是:i==j, 即行标等于列标;次对角线的规律是:i+j==n+1,即行标与列标的和值等于方阵的维数+1;}【知识总结】:一般来说,编程时一个问题可以只用一个主函数来处理,但考虑到模块化程序的优越性(主要是大的问题,很小的问题体现不出优越性),所以在实际设计程序时(特别是大的问题),需要设计多个函数(模块),甚至多个文件。
虽然我们碰到的一般是小问题,完全可以用一个主函数来处理,但是我们必须从小问题中学会模块化程序设计的思想和方法,为将来处理大问题打下基础。
程序填空实例6-3:将一个正整数n以相反的顺序输出的递归算法reverse(n)描述如下所示:输出n:当0<=n<=9时;输出n%10,继续输出reverse(n/10):当n>9时据此算法,设计的递归算法如下,请补充完整,并上机调试。
(需要存盘)将补充完整的程序以文件名reverse.c最终保存在实验6文件夹里。
[实验任务三]:掌握函数的嵌套调用和递归调用的程序的执行过程,会使用嵌套调用和递归调用编写程序;编程实例6-1:写一个判断素数的函数,在主函数输入一个整数,输出是否素数的信息。