【精品】C语言的运行环境48
目录
实验一 C语言的运行环境、运行过程 .................................. 1 实验二 C语言的基础知识 ............................................ 7 实验三 简单的C程序设计........................................... 10 实验四 选择结构程序设计........................................... 12 实验五 循环结构程序设计........................................... 16 实验六 条件型与计数型循环结构..................................... 21 实验七 一维数组................................................... 24 实验八 二维数组................................................... 27 实验九 函 数..................................................... 29 实验十 文 件..................................................... 34 实验十一 综合练习(一)............................................ 36 实验十二 综合练习(二)............................................ 41
附录一: Turbo C 程序设计上机指导 .............................. 46
Turbo C 程序设计初步........................................ 46
Turbo C 常用的编辑命令...................................... 49
Turbo C 程序的调试.......................................... 50
Turbo C编译、连接和运行时的常见错误 ........................ 52
附录二:C语言程序设计调试技术.................................. 55
C语言程序设计调试技术(一) ................................ 55
C语言程序设计调试技术(二) ................................ 55
C语言调试技术(三) ........................................ 56
C语言调试技术(四) ........................................ 57
I
实验一 C语言的运行环境、运行过程 【目的与要求】
1(了解Dos、Windows环境下C语言的运行环境,了解所用的计算机系统的基本操
作方法,学会独立使用该系统。
2(了解在该系统上如何编辑、编译、连接和运行一个C程序。
3(通过运行简单的C程序,初步了解C源程序的特点。
【上机内容】
【基础知识】
通过课堂上学习,我们对C语言已有了初步了解,对C语言源程序结构有了总体的认识,那么如何在机器上运行C语言源程序呢,任何高级语言源程序都要“翻译”成机器语言,才能在机器上运行。“翻译”的方式有两种,一种是解释方式,即对源程序解释一句执行一句;另一种是编译方式,即先把源程序“翻译”成目标程序(用机器代码组成的程序),再经过连接装配后生成可执行文件,最后执行可执行文件而得到结果。
C语言是一种编译型的程序设计语言,它采用编译的方式将源程序翻译成目的程序(机器代码)。运行一个C程序,从输入源程序开始,要经过编辑源程序文件(?C)、编译生成目标文件(?obj)、连接生成可执行文件(?exe)和执行四个步骤。
下面主要介绍Turbo C下运行C语言源程序。
Turbo C是美国Borland公司推出的IBM PC系列机的C语言编译程序。它具有方便、直观、易用的界面和丰富的库函数。它向用户提供了集成环境,把程序的编辑、编译、连接和运行等操作全部集中在一个界面上进行,使用十分方便。
1 Turbo C工作环境介绍
一个C语言程序的实施是从进入Turbo C的集成环境开始的,而进入C语言的环境,一般有两种途径:从DOS环境进入和从Windows环境进入。
(1) 从DOS环境进入:
在DOS命令行上键入:
C>CD \TC?(指定当前目录为TC子目录)
C>TC? (进入Turbo C环境)
这时进入Turbo C集成环境的主菜单窗口,屏幕显示如下图所示。
(2) 从Windows环境进入:
在Windows 95/98环境中,如果本机中已安装了Turbo C,可以在桌面上建立一个快捷方式,双击该快捷图标即可进入C语言环境。或者从开始菜单中找到“运行”,在运行对话框中键入“C:\TC\TC”,“确定”即可。
需要说明的是,以上两种方式有一个共同的前提,即Turbo C的安装路径为C:\TC,如果你的计算机中Turbo C的安装路径不同的话,在上述方式中改变相应路径即可。 刚进入TC环境时,光带覆盖在“File”上,整个屏幕由四部分组成,依次为:主菜单、编辑窗口、信息窗口和功能提示行(或称快速参考行)。
1
? 主菜单
显示屏的顶部是主菜单条,它提供了8个选择项:
File 处理文件(装入、存盘、选择、建立、换名存盘、写盘),目录操作(列
、
改变工作目录),退出Turbo C,返回DOS状态。
Edit 建立、编辑源文件。
Run 自动编辑、连接并运行程序。
Compile 编辑、生成目标文件组合成工作文件。
Project 将多个源文件和目标文件组合成工作文件。
Option 提供集成环境下的多种选择和设置(如设置存储模式、选择编参数、诊断及
连接任选项)以及定义宏;也可记录Include、Output及Library文件目录,保存编译任
选项和从配置文件加载任选项。
Debug 检查、改变变量的值、查找函数,程序运行时查看调用栈。选择程序编译时
是否在执行代码中插入调试信息。
Break/Watch 增加、删除、编辑监视表达式,及设置、清除、执行至断点。
在主菜单中,Edit选项仅仅是一条进入编辑器的命令。其他选项均为下拉式菜单,包含
许多命令选项,使用方向键移动光带来选择某个选项时,按回车键,表示执行该命令,
若屏幕上弹出一个下拉菜单,以提供进一步选择。
? 编辑窗口
编辑窗口是在主菜单下,信息窗口之上的区域,其顶行中间有“Edit”标志。在此窗口中可以建立、编辑一个源文件。功能键F5可以扩大编辑窗口到整个屏幕,或恢复分屏式环境。
进入编辑窗口的方式有两种:
(A)按F10功能键,激活主菜单,然后用光标移动键将光带移到“Edit”上,按回车键,或者在激活主菜单后直接按字母键E,均可进入编辑窗口;
(B)按Alt+E无条件的进入编辑窗口。
进入编辑窗口后,编辑窗口的名字是高亮度的,表示它是活动窗口。窗口的顶部第一行是状态行,给出有关正在被编辑文件的信息,如当前光标所在的行、列;编辑模式(插入/改写);
2
正在编辑文件的文件名等等,当需要编辑修改时,在编辑窗口中灵活地使用光标移动键以及编辑命令即可达到预期的效果。常用的编辑命令见下表:
命令 功能
Home 将光标移到行首
End 将光标移到行尾
Ins 插入/改写两种状态的切换
Del 删除光标所在的字符
PgUp 向上翻页正文
PgDn 向下翻页正文
? ? 光标左右移动
? ? 删除光标所在的行
Ctrl+Y 删除光标所指的一个单词
Ctrl+T 放弃操作
Ctrl+U 删除光标左边字符
Backspac 设置(开始)块头标志
Ctrl+KB 设置(结尾)块尾标志
Ctrl+KK 块搬动到光标处
Ctrl+KV 块拷贝到光标处
Ctrl+KC 块删除
Ctrl+KY 取消块定义
Ctrl+KH
在插入模式下(用Ins键转换)向编辑窗口内输入代码,按回车键结束一行(TC编辑器不能自动换行)。一行最多为248个字符,窗口宽77列,当一行内字符多于77列时,窗口随着字符的输入而左右移动,若发现有错误,可移动光标到出错处更正之;再按一次Ins键,屏幕转换成为替换模式,输入的字符将替换光标所在位置的字符。
? 信息窗口
编译和调试源程序时,信息窗口显示诊断信息、警告、出错信息、错误在源程序中的位置。功能键F5可以扩大和恢复信息窗口,按F6或Alt+E,光标从信息窗口跳到编辑窗口。 ? 功能键提示行
屏幕低行是功能键提示行,显示当前状态下功能键(俗称Turbo C热键)的作用,见下表。应当注意,在不同状态下功能键的作用是不同的。正确使用功能键可以简化操作。
热键 简单说明
F1 Help,以分页的形式显示帮助信息
F2 保存当前正在编辑窗口中的文件
F3 装入一个文件。按F3,屏幕上弹出一个输入框,
输入要装入的文件名
F5 Zoom,缩放活动窗口
F6 Switch,活动窗口开关,按F6键,光标从编辑
窗口跳到信息窗口,或从信息窗口跳到编辑窗口
F7 Trace,跟踪到函数中
F8 Step,单步跟踪,但不进入函数内部
F9 Make,对当前文档进行编辑、连接
3
F10 Menu,激活主菜单,光标跳到主菜单
2 Turbo C环境中运行C语言源程序的步骤
(1) 编辑源文件
在主菜单下,直接按Alt+F键,或按F10后将光带移到“File”选项上,按回车键,在“File”下面出现一个下拉菜单,菜单中有以下选项:
Load F3 表示加载或装入一个文件
Pick Alt+F3 从指定的文件列表中选择文件装入编辑器。
New 表示新文件,缺省文件名为NONAME.C。
Save F2 将正在编辑的文件存盘。
Write to 将正在编辑的文件写入指定的文件中,若文件名已存在,则重写。
Directory 表示文件目录。
Change Dir 改变驱动器及目录
OS shell 进入Turbo C命令行模式,命令EXIT可返回集成环境
Quit Alt+x 退出Turbo C,返回DOS状态。
建立一个新文件,可用光标移动键将“File”菜单中的光带移到“New”处,按回车键,即可打开编辑窗口。此时,编辑窗口是空白的,光标位于编辑窗口的左上角,屏幕自动处于插入模式,可以输入源程序。屏幕右上角显示缺省文件名为NONAME.C,编辑完成之后,可用F2或选择“Save”或“Write to”进行存盘操作,此时系统将提示用户将文件名修改成为所需要的文件名。
(2) 源程序的编译、连接
直接按F9键,或将菜单“Compile”中的光带移到“Make EXE file”项上,按回车键,
就可实现对源程序的编译、连接。若有错误,则在信息窗口显示出相应的信息或警告,按任意键返回编辑窗口,光标停在出错位置上,可立即进行编辑修改。修改后,再按F9键进行编辑、连接。如此反复,直到没有错误为止,即可生成可执行文件。
注意:C程序的连接是在编译后自动完成的。
(3) 执行程序
直接按Ctrl+F9键,即可执行.EXE文件;或在主菜单中(按F10进入主菜单)将光带移到“Run”选项,按回车键,弹出一个菜单,选择“Run”选项,回车。 这时并不能直接看到输出结果。输出结果是显示在用户屏幕上,在TC屏幕上看不到,直接按复合键Alt+F5,或选择“Run”菜单中的“User Screen”选项,即可出现用户屏幕,查看输出结果。按任意键返回TC集成环境。
“Run”项,或直接按Ctrl+F9键,可将C程序的编译、连接、另外,选择“Run”菜单下的
运行一次性完成,即第3步中包含有第2步的工作。
如果程序需要输入数据,则在运行程序后,光标停留在用户屏幕上,要求在用户输入数据,数据输入完成后程序继续运行,直至输出结果。
如果运行结果不正确或其他原因需要重新修改源程序,则需重新进入编辑状态。修改源程序,重复以上步骤,直到结果正确为止。
(4) 退出Turbo C集成环境
退出Turbo C环境,返回操作系统状态。可在主菜单选择“File”菜单的“Quit”选项,或者直接按Alt+x。
在执行退出Turbo C环境时,系统将检查一下当前编辑窗口的程序是否已经存盘,若未存盘,系统将弹出一个提示窗口,提示是否将文件存盘,若按“Y”则将当前窗口内的文件存盘后退出;若按“N”则不存盘退出。
4
【一般实例】
【例1】编程实现在屏幕上显示如下三行文字
Hello, world !
Wolcome to the C language world!
Everyone has been waiting for.
在Turbo C的集成环境下,键入如下源文件。敲Alt+F组合键打开File菜单,File菜单中Write to选项可将默认noname.c文件名改为任意文件名。程序example.c如下: main()
{
printf("Hello,World!\n");
printf("Wolcome to the C language world!\n"); printf("Everyone has been waiting for.\n"); }
然后用Ctrl+F9编辑执行example.c,用Alt+F5查看结果,即在屏幕上显示题目要求的三行文字。按回车键重新返回Turbo C的编辑环境。注意,在运行程序之前最好先存盘。 【例2】输入并运行程序,写出运行结果。
main()
{
int a,b,sum;
a=123;b=456;
sum=a+b;
printf(“sum is %d\n”,sum);
}
运行方法同上,最后结果为:sum is 579。
【例3】
输入并运行程序,写出运行结果。
main()
{
int a,b,c;
scanf(“%d,%d”,&a,&b);
c=max(a,b);
printf(“max=%d”,c);
}
int max(int x,int y)
{
int z;
if (x>y) z=x;
else z=y;
return(z);
}
这个程序的功能是对于任意输入的两个整数,输出较大的那个数。所以程序运行之后,光标将停留在用户屏幕上,等待用户输入两个整数,比如输入“3,5”,回车,在用户屏幕上就
5
会输出“max=5”。
【作业】
1(输入并运行例题中程序,熟悉调试C程序的的方法与步骤。 2(写出一个C程序的构成。
3(参照例题,编写一个C程序,输出以下信息:
****************************
Hello,World~
**************************** 4(编写一个C程序,输入a、b、c三个数,输出其中最大者。试想,如果求四个数中的最
大者,则程序该如何编写。
6
实验二 C语言的基础知识 【目的与要求】
1(掌握C语言数据类型,熟悉如何定义一个整型、字符型、实型变量,以及对它们赋
值的方法,了解以上类型数据输出时所用的格式转换符。
2(学会使用C的有关算术运算符,以及包含这些运算符的表达式,特别是自加(++)
和自减(--)运算符的使用。
3(进一步熟悉C程序的编辑、编译、连接和运行的过程。 【上机内容】
【一般实例】
【例1】输入并运行下程序,写出运行结果。
mian()
{
char c1 , c2;
c1=97;c2=98;
printf(“%c %c”, c1, c2);
}
在此基础上
1)加一个printf语句,并运行之。
printf(“%d,%d”, c1, c2 );
2)再将第二行改为:
int c1, c2;
再使之运行。
3)再将第三行改为:
c1=300; c2=400;
再使之运行,
其运行结果。
在该程序中,说明了字符型数据在特定情况下可作为整型数据处理,整型数据有时也可以作
为字符型数据处理。
【例2】分析下程序,写出运行结果,再输入计算机运行,将得到的结果与你分析得到的结
果比较对照。
main()
{
char c1=?a?,c2=?b?,c3=?c?,c4=?\101?,c5=?\116?;
printf(“a%c b%c\tabc\n”,c1,c2,c3);
printf(“\t\b%c %c”,c4,c5);
}
在该程序中,主要考查对转义字符的掌握情况。
【例3】
分析下程序,写出运行结果,再输入计算机运行,将得到的结果与你分析得到的结果比较对
照。
7
main()
{
int i , j , m , n ; i=8; j=10;
m=++i; n=j++;
printf(“%d,%d,%d,%d”,i,j,m,n);
}
分别作以下改动之后,先分析再运行:
1)将第四行改为:
m=i++; n= ++ j;
2)程序改为:
main()
{
int i , j ;
i=8; j=10;
printf(“%d,%d”, i++, j++);
}
3)在2)的基础上,将printf语句改为:
printf(“%d,%d”, ++ i, ++ j );
4)再将printf语句改为:
printf(“%d,%d,%d,%d”,i,j,i++,j++); 5)程序改为:
main()
{
int i , j , m=0 , n=0 ; i=8; j=10;
m+= i ++; n -= --j; printf(“i=%d,j=%d,m=%d,n=%d”,i,j,m,n);
}
此程序主要考查自加、自减运算符以及复合运算符的用法。 【作业】
1(要讲“China”译成密码,密码规律是:用原来的字母后面第5个字母代替原来的字母。
例如,字母“A”后面第5个是“F”,用“F”代替“A”。因此,“China”应译为“Hmnsf”。
请编一程序,用赋初值的方法使c1、c2、c3、c4、c5五个变量的值分别为?C?、?h?、?i?、?n?、?a?,
经过运算,使其分别变为?H?、?m?、?n?、?s?、?f?,并输出。 2(求下面算术表达式的值。
(1)x+a%3*(x+y)%2/4
设x=2.5,a=7,y=4.7
(2)(float)(a+b)/2+(int)x%(int)y
设a=2,b=3,x=3.5,y=2.5
先自己分析,再试着用程序求解,看得到的结果是否一致。 3(写出下面表达式运算后a的值,设原来a=10。设a和n已定义成整型变量。
(1)a+=a (2)a-=2
8
(3)a*=2+3 (4)a/=a+a (5)a%=(n%=2),n的值等于5 (6)a+=a-=a*=a 先自己分析,再试着用程序求解,看得到的结果是否一致。
9
实验三 简单的C程序设计
【目的与要求】
1(理解C语言程序的顺序结构
2(掌握常用的C语言语句,熟练应用赋值、输入、输出语句。
【上机内容】
【一般实例】
【例1】按格式要求输入/输出数据
#include "stdio.h" main()
{
int a,b;
flost x,y;
char c1,c2;
scanf(“a=%d,b=%d”,&a,&b);
scanf(“%f, %e”,&x,&y);
scanf(“&c &c”,&c1,&c2);
printf(“a=%d,b=%d,x=%f,y=%f,c1=%c,c2=%c\n”,a,b,x,y,c1,c2);
}
运行该程序,必须按如下方式在键盘上输入数据 a=3,b=7 ?
8.5,71.82 ?
a A ?
请同学们写出输出结果
【例2】输入三角形三边长,求三角形面积。 #include “math.h”
main()
{
float a,b,c,s,area;
scanf(“%f ,%f,%f”,&a,&b,&c);
s=1.0/2*(a+b+c);
area=sqrt(s*(s-a)*(s-b)*(s-c));
printf(“area=%f”,area);
}
【例3】已知圆半径,圆柱高,求圆周长,圆柱体积。
main()
{
float r,h,l,v,pi;
pi=3.1415926;
scanf(“%f,%f”,&r,&h);
10
l=2*pi*r;
v=pi*r*r*h;
printf(“圆周长为:%6.2f\n”,l);
printf(“%圆柱体积为:6.2f”,v);
}
【例4】输入一个摄氏温度,要求输出华氏温度。公式为f=5/9*c+32.
main()
{
float c,f;
scanf(“%f”,&c);
f=5.0/9*c+32;
printf(“%5.2f\n”,c);
}
【作业】
1. 已知圆半径r=1.5,圆柱高h=3, 求圆周长,圆面积,圆球表面积,圆球体积,圆柱体积。
2. 输入一个华氏温度,要求输出摄氏温度。公式为c=5/9*(f-32).
11
实验四 选择结构程序设计 【目的与要求】
1(了解C语句表示逻辑量的方法(以0代表“假”以1代表“真”);
2(学会正确使用逻辑运算符和逻辑表达式;
3( 熟练掌握if语句和switch语句。
【上机内容】
【一般实例】
【例1】 已知三个数a,b,c,找出最大值放于max中。 分析:由已知可得在变量定义时定义四个变量a,b,c和max,a,b,c是任意输入的三个
数,max是用来存放结果最大值的。第一次比较a和b,把大数存入max中,因a,b都可
能是大值,所以用if语句中if——else 形式。第二次比较max和c,把最大数存入max中,
用if语句的第一种形式if形式。Max即为a,b,c中的最大值。 ,include "stdio.h"
main()
{
int a,b,c,max; /*定义四个整型变量*/
scanf(“a=%d,b=%d,c=%d”,&a,&b,&c);
if (a>=b)
max=a; /*a>=b*/ else
max=b; /*a
max)
max=c; /*c是最大值*/
printf("max=%d",max);
}
若输入下列数据,分析程序的执行顺序并写出运行结果 (1)a=1,b=2,c=3
(2)a=2,b=1,c=3
(3)a=3,b=2,c=1
(4)a=3,b=1,c=2
(5)a=3,b=3,c=2
(6)a=2,b=1,c=2
【例2】输入某学生的成绩,经处理后给出学生的等级,等级分类如下:
90分以上(包括90): A
80至90分(包括80):B
70至80分(包括70):C
60至70分(包括60):D
60分以下: E
方法一:(用if嵌套`)
12
分析:
由题意知如果某学生成绩在90分以上,等级为A;否则,如果成绩大于80分,等级为B;否则,如果成绩大于70分,等级为C;否则,如果成绩大于60分为D;否则,如果成绩小于60分,等级为E;但当我们输入成绩时也可能输错,出现小于0或大于100,这时也要做处理,输出出错信息。因此,再用if嵌套前,应先判断输入的成绩是否在0~100之间。 #include"stdio.h"
main()
{
int score;
char grade;
printf("\nplease input a student score:");
scanf("%f",&score);
if(score>100||score<0)
printf("\ninput error!");
else
{ if(score>=90)
grade='A';
else
{ if(score>=80)
grade='B';
else
{if(score>=70)
grade='C';
else
{ if(score>=60)
grade='D';
else grade='E';
}
}
}
printf("\nthe student grade:%c",grade);
}
}
输入测试数据,调试程序。测试数据要覆盖所有路径,注意临界值,例如此题中得100分,60分,0分以及小于0和大于100的数据。
方法二:用switch语句
分析:
switch 语句是用于处理多分支的语句。注意,case后的表达式必须是一个常量表达式,所以在以用switch语句之前,必须把0~100之间的成绩分别化成相关的常量。所有A(除100以外),B,C,D类的成绩的共同特点是十位数相同,此外都是E类。则由此可得把score除十取整,化为相应的常数。
#include"stdio.h"
main()
13
{
int g,s;
char ch;
printf("\ninput a student grade:"); scanf("%d",&g);
s=g/10;
if(s<0||s>10)
printf("\ninput error!"); else
{ switch (s)
{ case 10:
case 9: ch='A'; break;
case 8: ch='B'; break;
case 7: ch='C'; break;
case 6: ch='D'; break;
default: ch='E';
}
printf("\nthe student scort:%c",ch);
}
} 输入测试数据,同方法一一样调试程序并写出结果。
【例3】有一函数:
x (x<1)
y= 2x-1 (1?x<10)
3x-11 (x?10)
用scanf函数输入x的值(分别为x<1,1~10,>=10三种情况),求y值。 分析:y是一个分段表达式。要根据x的不同区间来计算y的值。所以应使用If语句。
main()
{
int x,y;
printf(“please input x :”);
scanf(“%d”,&x);
if (x<1)
{
y=x;
printf(“y=%d\n”,y);
}
else if (x<10)
{
y=2*x-1;
printf(“y=%d\n”,y);
14
}
else
{
y=3*x-11;
printf(“y=%d\n”,y);
}
【作业】
1. c语言如何表示“真”与“假”,系统如何判断一个量的“真”与“假”,
2. 写出下面各逻辑表达式的值。设a=3,b=4,c=5
(1) a+b>c&&b= =c
(2) a||b+c&&b-c
(3) ~(a>b)&&!c||1
(4) !(x=a)&&(y=b)&&0
(5) !(a+b)+c-1&&b+c/2
3. 有3个整数a、b、c,由键盘输入,输出其中最大的数。 4(有一函数:
x (x<1)
y= 2x-1 (1?x<10)
3x-11 (x?10)
写一程序,输入x, 输出y。
5(给出一百分制成绩,要求输出成绩等级A、B、C、D、E。90分以上为A,80~89为B,
70~79分为C,60~69分为D,60分以下为E。 6( 给一个不多于5位的正整数,要求:
a. 求出它是几位数;
b. 分别打出每一位数字;
c. 按逆序打出各位数字,例如原数为321,应输出123。 7(输入4个整数,要求按由小到大的顺序输出。
15
实验五 循环结构程序设计
【目的与要求】
1(熟练掌握while语句的应用;
2(熟练掌握do—while语句的应用;
3(熟练掌握for语句的应用。 【上机内容】
【一般示例】
【例1】求5~
main()
{
int n,t;
n=1;
t=1;
while(t<=5)
{
n=n*t;
t=t+1;
}
printf(“%d”,n);
}
【例2】求和s=1!+2!+3! main()
{
int n,s=0,t=1;
for(n=1;n<=3;n++)
{
t=t*n;
s=s+t;
}
printf(“%d”,s);
}
【例3】求和s=1!+3!+5! main()
{
int s=0,t,i,j; for(i=1;i<=3;i++)
{
t=1;
for(j=1;j<=2*i-1;j++)
16
t=t*j;
s=s+t;
}
printf(“%d”,s);
}
【例4】求和s= 3+33+333
main()
{
int s=0,t=3,i; for(i=1;i<=3;i++)
{
s=s+t;
t=10*t+3;
}
printf(“%d”,s);
}
【例5】有一数列:2/1,3/2,5/3,8/5,„求出这个数列的前10项之和。
main()
{
int n,t,;
float a=2,b=1,s=0; for(n=1;n<=10;n++) {
s=s+a/b;
t=a;
a=a+b;
b=t;
}
printf(“%f”,s);
}
【例6】打印500以内的“水仙花数”。“水仙花数”是一个三位数,其各位数立方和等于该
数本身。
main()
{
int i,j,k,n;
for(n=100;n<=500;n++)
{
i=n/100;
j=n/10-i*10;
k=n%10;
if(n= =i*i*i+j*j*j+k*k*k)
printf(“ %d”,n);
}
17
}
【例7】一个数如果恰好等于它的因子之和,这个数就称为完数。求100之内的所有完数。
main()
{
int n,s,j;
for(n=1;n<=100;n++)
{
s=0;
for(j=1;jn);其次,算法:使k为m除以n的余数,
如果m能被n整除,则k值为0,n为这两个数的最大公约数,否则,使k代替n,n代替
m,重复以上过程,直到k值为0。
#include"stdio.h"
main()
{
int m,n,k,result;
printf("Enter two numbers:");
scanf("%d,%d",&m,&n);
if(m>0&&n>0) /*限定两个正整数*/
{
do
{
k=n%m;
if(k= =0)
result=m;
else
{
n=m;
m=k;
}
}while(k>0); /*循环取余求出最大公因子*/
printf("The greatest common divistor is:%d\n",result);
}
else printf("Nonpositive values not allowed\n");
}
32【例2】用牛顿迭代求方程2x-4x+3x-6=0在1.0附近的根。
18
#include “math.h”
main()
{
float x,x0,f,f1;
x=1.0;
do
{ x0=x;
f=((2*x0-4)*x0+3)*x0-6; f1=(6*x0-8)*x0+3; x=x0-f/f1;
}
while(fabs(x-x0)>=1e-5); printf(“%6.2f”,x);
}
【例3】打印下列图案
*
***
*****
*******
main()
{ int i,j;
for(i=1; i<=4;i++)
{for(j=1;j<= 4-i;j++)
printf(“ ”);
for(j=1;j<=2*i-1;j++)
printf(“*”);
printf(“\n”);
}
}
【例4】判断m是否是素数。
程序:
#include main()
{
int m,i,k;
scanf(“%d”,&m);
k=sqrt(m+1);
for(i=2;i<=k;i++)
if (m%i= =0)
break;
if(i>=k+1)
printf(“%d is a prime number\n”,m); else
printf(“%d is not a prime number\n”,m);
19
}
【作业】
1(输入两个正整数m和n,求其最大公约数和最小公倍数。
2(求S=a+aa+aaa+…+aa…a 之值,其中a是一个数字。例如:2+22+222+2222+22222(此n
时n=5), n由键盘输入。
~+2~+3~+4~+„20~ 3(求和1
4( 打印100-999之间所有的“水仙花数”。“水仙花数”是一个三位数,其各位数立方和等
于该数本身。
5( 一个数如果恰好等于它的因子之和,这个数就称为完数。求1000之内的所有完数。
6( 有一数列:2/1,3/2,5/3,8/5,„求出这个数列的前20项之和。 7( 用迭代法求x= ?a 。求平方根的迭公式为
x=1/2(x+a/x) n+1nn-5 要求前后两次求出的x的差的绝对值小于10
328( 用牛顿迭代求方程2x-4x+3x-6=0在1.5附近的根。 9( 打印下列图案
*
***
*****
***
*
20
实验六 条件型与计数型循环结构
【目的与要求】
1.掌握在程序设计条件型循环结构时,如何正确地设定循环条件,以及如何控制循环
的次数。
2.了解条件型循环结构的基本测试方法。
3.掌握如何正确地控制计数型循环结构的次数
4.了解对计数型循环结构进行测试的基本方法
【上机内容】
【一般实例】
条件型循环结构:
1、下面是一个计算e的近似值(使误差小于给定的δ)的程序。
main()
{double e=1.0,x=1.0,y,detax;
int i=1;
printf(“\n please input enter a error:”);
scanf(“%lf”,&detax);
y=1/x;
while(y>=detax)
{
x=x*I;
y=1/x;
e=e+y;
++i;
}
printf(“%12.10lf”,e);
}
2、实验步骤
(1)、阅读上面的程序,写出程序所依据的计算公式。
(2)、当输入的detax各是什么值时,能分别使程序按下面的要求运行:
.不进入循环;
.只循环一次;
.只循环两次;
.进入死循环(程序将永远循环下去)。
为了能知道程序循环了多少次,应该在程序中增加一条什么样的语句, (3)、原程序中while语句中的y>=detax,分别换成y>detax,y=detax,y=3;j++
.j=1;j>3;j++
.j=1;j<=3;j--
.j=1;j>3;j--
.j=1;j<3;j++
.j=0;j<=3;j+=
.j=0;j<3;j++
j=1;j<=3;j++
j=1;j<3;++j
观察程序的执行结果有无变化,
【作业】
(1)一下测试条件循环结构的一般方法。
(2) 如何测试计数型循环结构的控制表达式中的错误,
(3)从实验中你得到了哪些提高嵌套循环程序效率的启示,
23
实验七 一维数组 【目的与要求】
1(掌握一维数组的定义、赋值和输入输出的方法;
2(掌握字符数组的使用;
3(掌握与数组有关的算法(例如排序算法)。
【上机内容】
【一般实例】
【例1】在键盘上输入N个整数,试编制程序使该数组中的数按照从大到小的次序排列。 分析:C中数组长度必须是确定大小,即指定N的值。排序的方法有多种,我们取出其中两种作为参考。
方法一:起泡排序
从第一个数开始依次对相邻两数进行比较,如次序对则不做任何操作;如次序不对则使这两个数交换位置。第一遍的(N-1)次比较后,最大的数已放在最后,第二遍只需考虑(N-1)个数,以此类推直到第(N-1)遍比较后就可以完成排序。
源程序如下:
#define N 10
#include"stdio.h"
main()
{
int a[N],i,j,temp;
printf("please input %d numbers\n",N); for(i=0;ia[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
printf("the array after sort:\n"); for(i=0;ia[j])
min=j;
temp=a[i];
25
a[i]=a[min];
a[min]=temp;
}
for(i=2;i<=9;i++) /*注意i值的变化范围*/
ave=ave+a[i];
printf("选手所得最后分数:%6.1f",ave/8);
printf("\n");
}
【例3】输入一串字符,计算其中空格的个数。
#include
main( )
{char c[30];
int i,sum=0;
gets(c);
for(i=0;imax)
{max= a[i][j];
r=i;
c=j;
}
printf(“max=%d , row =%d , colum=%d \n”,max , r, c);
}
【例2】打印以下图案:
* * * * *
* * * * *
* * * * *
* * * * *
* * * * *
main()
{char a[5]={' * ' , ' * ' , ' * ' , ' * ' , ' * '};
int i,j,k;
char space=' ';
for(i=0;i<5;i++) /*输出5行*/
{printf("\n"); /* 输出每行前先换行* /
printf(" "); /* 每行前面留5个空格 */
for (j=1;j<=i;j++)
printf("%c",space); /* 每行再留1个空格*/
for ( k=0;k<5;k++)
27
printf("%c",a[k]); /*每行输入5个*号*/
}
}
【例3】求矩阵下三角形元素之和。
#define N 6
main()
{int i,j,sum=0;
int a[N][N]={0};
printf("input 5×5 data:\n");
for(i=1;iv)
{
t=u; u=v; v=t;
}
30
a=u; b=v;
while((r=b%a)!=0)
{
b=a; a=r;
}
return(a);
}
lcd(u,v,h)
int u,v,h;
{
return(u*v/h); }
main()
{
int u,v,h,l;
scanf("%d,%d",&u,&v); h=hcf(u,v);
printf("H.C.F=%d\n",h);
l=lcd(u,v,h);
printf("L.C.D=%d\n",l);
}
这是一个十分典型的算法,同学们一定要认真分析、学习。
【例2】写一函数,用“起泡法”对输入的10个字符按由小到大顺序排序。
源程序如下:
/*起泡法排序*/
#define N 10
char str[N];
main()
{
int i,flag;
for (flag=1; flag = =1;)
{
printf(“\n输入字符串,长度为10:\n”); scanf(“%s”,&str);
if (strlen(str)>N)
printf(“超过长度,请重输~”);
else
flag = 0;
}
sort(str);
printf(“\n排序结果:”):
for (i=0;istr[i+1])
{
t=str[i]; str[i]=str[i+1]; str[i+1]=t;
}
}
32【例3】用牛顿迭代法求根。方程为ax+bx+cx+d=0,系数a、b、c、d由主函数输入。求x
在1附近的一个实根。求出根后,由主函数输出。 源程序如下:
/*牛顿迭代法解方程*/
#include “math.h”
float solut(a,b,c,d) float a,b,c,d;
{
float x=1,x0,f,f1;
do
{
x0=x;
f=((a*x0+b)*x0+c)*x0+d; f1=(3*a*x0+2*b)*x0+c; x=x0-f/f1;
}while(fabs(x-x0)>=1e-5); return(x);
}
main()
{
float a,b,c,d;
printf(“\n输入方程的系数a,b,c,d\n”);
scanf(“%f,%f,%f,%f”,&a,&b,&c,&d);
printf(“\n方程是:%5.2fx^3+%5.2fx^2+%5.2fx+%5.2f=0”,a,b,c,d); printf(“\nX=%10.7f\n”,solut(a,b,c,d));
}
在此题目中,方程的系数是未知的,这是与以前我们用牛顿迭代法的时候不同的地方。但原
理、算法是相同的,这里我们用函数来求方程的根。也就是把牛顿迭代法用函数的形式表现
32
出来。
【作业】
1(上机调试下面的程序,记录系统给出的出错信息,并指出出错原因。 main()
{
int x,y;
printf("%d\n",sum(x+y));
int sum(a,b);
{
int a,b;
return(a+b);
}
}
2(定义一个函数,功能是计算n个学生的成绩中,高于平均成绩的人数,并作为函数值。用主函数来调用它,统计50个学生成绩中,高于平均成绩的有多少人, 3(编写一个对n个数据从大到小的排序C函数,再编写一个计算最后得分的C函数,计算方法是:去除一个最高分,去除一个最低分,其余的平均分为参赛选手的最后得分。并在主函数中调用它们对有n个评委评分,m个选手参赛的最后得分,从大到小排序输出。 4(编写一个计算n!的函数,用主函数调用它,使之输出7阶杨辉三角形: 1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
n杨辉三角形是二项展开式(a+b)的系数,共有n+1项,n=0,1,…。杨辉三角形在数学上具有重要的意义,在高中阶段已学过,系数是按照公式:a=,其中a是展开式中mm的第m项系数。
5(编写一个程序,包括主函数和如下子函数。
(1) 输入10个无序的整数;
(2) 用起泡方法从大到小排序;
(3) 要求输入一个整数,用折半查找法找出该数,若存在,在主函数中输出其所处的位置,否则,插入适当位置。
分析:input函数完成10个整数的录入。sort函数完成起泡法排序,search函数完成输入数的查找。
33
实验十 文 件 【目的与要求】
1(掌握文件和文件指针的概念以及文件的定义方法。
2(了解文件打开和关闭的概念和方法。
3(掌握有关文件的函数。
【上机内容】
【一般实例】
【例1】对data.dat文件写入100条记录。
#include
main()
{FILE *fp;
int i;
float x;
fp=fopen("date.dat","w");
for(i=1;i<=100;i++)
{scanf("%f",&x);
fprintf(fp,"%f\n",x);
}
fclose(fp);
}
【例2】设有一文件cj.dat存放了50个人的成绩(英语、计算机、数学),存放格式为:每
人一行,成绩间由逗号分隔。计算三门课平均成绩,统计个人平均成绩大于或等于90分的
学生人数。
#include
main()
{FILE *fp;
int num;
float x , y , z , s1 , s2 , s3 ;
fp=fopen ("cj.dat","r");
{fscanf (fp,"%f,%f,%f",&x,&y,&z);
s1=s1+x;
s2=s2+y;
s3=s3+z;
if((x+y+z)/3>=90)
num=num+1;
}
printf("分数高于90的人数为:%.2d",num);
fclose(fp);
}
34
【例3】统计上题cj.dat文件中每个学生的总成绩,并将原有数据和计算出的总分数存放在磁盘文件“stud”中。
#include "stdio.h"
main()
{
FILE *fp1,*fp2;
float x,y,z;
fp1=fopen("cj.dat","r");
fp2=fopen("stud","w");
while(!feof(fp1))
{
fscanf (fp1,"%f,%f,%f",&x,&y,&z);
printf("%f,%f,%f,%f\n",x,y,z,x+y+z);
fprintf(fp2,"%f,%f,%f,%f\n",x,y,z,x+y+z);
}
fclose(fp1);
fclose(fp2);
}
【作业】
1. 有 5个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括学生号,姓名,
三门课成绩),计算出平均成绩,将原有数据和计算出的平均分数存放在磁盘文件“stud”
中。
2. 将上题“stud”文件中的学生数据,按平均分进行排序处理,将已排序的学生数据存入
一个新文件“stu_sort”中。
35
实验十一 综合练习(一)
【目的与要求】
1、熟悉C语言的端口及硬件控制的方法与原理。
2、了解用C语言实现发声程序所要访问的端口。
【上机内容】
1、调试并总结程序中所用的知识点,写出乐谱文件播放的是什么乐谱,
乐谱实例:
6000 h5 1 h3 2 h2 0.5 h3 0.5 h1 4 h2 1 m7 2 m6 0.5 h1 0.5 m5 4 m3 2 m5 1.5
m6 0.5 h1 1.5 h2 0.5 m6 0.5 h1 0.5 m5 1 h5 1.5 h1 0.5 h6 0.5 h5 0.5 h3 0.5
h5 0.5 h2 4 h2 1.5 h3 0.5 m7 1 m6 1 m5 1.5 m6 0.5 h1 1 h2 1 m3 1 h1 1 m6 0
m5 0.5 m6 0.5 h1 0.5 m5 4 h3 1.5 h5 0.5 m7 1 h2 1 m6 0.5 h1 0.5 m5 3 m3 0.
m5 0.5 m3 0.5 m5 0.5 m5 0.5 m6 0.5 m7 0.5 h2 0.5 m6 3 m5 0.5 m6 0.5 h1 1.5
h2 0.5 h5 1 h3 1 h2 1 h3 0.5 h2 0.5 h1 1 m6 0.5 m5 0.5 m3 2 h1 2 m6 0.5 h1
0.5 m6 0.5 m5 0.5 m3 0.5 m5 0.5 m6 0.5 h1 0.5 m5 3 h3 0.5 h5 0.5 h2 0.5 h3
0.5 h2 0.5 h1 0.5 m7 1 m6 1 h5 4##
void play_music(char *filename) /*filename为音乐文件名格式见上*/
{
FILE *fp;
int rate;
char sound_high[3];
float sound_long;
register int i=0,j;
int sign=0;
float str[1000][2];
if ((fp=fopen(filename,"r"))==NULL) {
printf("Open file music.doc Errors!\n"); exit(1);
}
fscanf(fp,"%d",&rate);
while(!feof(fp)&&!sign)
{
fscanf(fp,"%s%f",&sound_high,&sound_long); str[i][1]=rate*sound_long;
switch(sound_high[0])
{
case 'h':
switch(sound_high[1])
{
case '1':
str[i++][0]=1046.5;
break;
case '2':
str[i++][0]=1174.7;
break;
case '3':
str[i++][0]=1318.5;
36
break;
case '4':
str[i++][0]=1396.9; break;
case '5':
str[i++][0]=1568; break;
case '6':
str[i++][0]=1760; break;
case '7':
str[i++][0]=1975.5; break;
default: printf("\n Error Music.doc\n");
break;
}
break;
case 'm':
switch(sound_high[1]) {
case '1':
str[i++][0]=523.3; break;
case '2':
str[i++][0]=587.3; break;
case '3':
str[i++][0]=659.3; break;
case '4':
str[i++][0]=698.5; break;
case '5':
str[i++][0]=784.0; break;
case '6':
str[i++][0]=880; break;
case '7':
str[i++][0]=987.8; break;
default: printf("\n Error music.doc.\n");
break;
}
break;
case 'l':
switch(sound_high[1]) {
case '1':
str[i++][0]=262; break;
case '2':
str[i++][0]=296; break;
case '3':
str[i++][0]=329.6; break;
37
case '4':
str[i++][0]=349.2; break;
case '5':
str[i++][0]=392; break;
case '6':
str[i++][0]=440; break;
case '7':
str[i++][0]=493.9; break;
default: printf("\n Error music.doc.\n");
break;
}
break;
case '*':
switch(sound_high[1]) {
case '1':
str[i++][0]=131; break;
case '2':
str[i++][0]=147; break;
case '3':
str[i++][0]=165; break;
case '4':
str[i++][0]=176; break;
case '5':
str[i++][0]=196; break;
case '6':
str[i++][0]=220; break;
case '7':
str[i++][0]=247; break;
default: printf("\n Error music.doc\n");
break;
}
break;
case '#':
if (sound_high[1]=='#') sign=1;
break;
case 'x':
if (sound_high[1]=='0') str[i++][0]=20;
break;
default:printf("\n ERRor music.doc\n");
exit(1);
}
}
for(j=0;j<=i-1;j++) {
sound(str[j][0]);
38
delay(str[j][1]);
}
nosound();
}
2、文件复制
/*拷贝一个文件*/
#include
main(argc,argv)
int argc;
char *argv[];
{
FILE *in,*out;
if (argc!=3)
{
printf("Wrong file structure!\n");
exit(0);
}
if ((in=fopen(argv[1],"rb"))==NULL) /*注意是"rb"*/
{
printf("Can't open infile.\n");
exit(0);
}
if ((out=fopen(argv[2],"wb"))==NULL) /*注意是"wb"*/
{
printf("Can't open outfile.\n");
exit(0);
}
while (!feof(in)) fputc(fgetc(in),out);
fclose(in);
fclose(out);
}
成批拷贝文件
int CopyFile(char *sfile,char *dfile,int f2d,int barlong,int height,int x,int y)
{
int Copyfile(char *sf,char *df); int MakeNdir(char *Dir);
char filename[200][13],d[40],s[40],s1[40];
struct ffblk ffblk;
int done,i,j,l,len;
i=0;
done=findfirst(sfile,&ffblk,0); if (!done) strcpy(filename[i],ffblk.ff_name);
while(!done)
{
done=findnext(&ffblk);
if (!done)
{
i++;
strcpy(filename[i],ffblk.ff_name); }
}
if (f2d)
{
Copyfile(sfile,dfile);
39
return 1;
}
strcpy(s,sfile); l=strlen(sfile); for(j=l-1;j>=0;j--) if (s[j]=='\\') {
s[j+1]=0;
break;
}
40
实验十二 综合练习(二) 【目的与要求】
1、掌握用C语言进行简单的数据库管理。
2、了解C程序菜单设计的一般方法。
3、理解链表的概念及应用。
【上机内容】
建立一个链表,每个结点包括学号、姓名、及三科成绩、平均成绩。除平均成绩 外,各项均由键盘输入。
要求:
(A).计算平均成绩。
(B).要实现插入功能。
(C).要有排序功能。
(D).要有存取功能。
参考程序:
#include
struct SStudent
{
unsigned number;
char name[11];
int score1, score2, score3;
struct SStudent * link;
};
struct SStudent * CreateTable(); void AppendToTable(struct SStudent * stu); void InsertToTable(struct SStudent * stu); void QueryTable(struct SStudent * stu, unsigned age);
void SortTable(struct SStudent * stu); void PrintTable(struct SStudent * stu); void Save(struct SStudent * stu); void Load(struct SStudent * stu); void Help();
void main()
{
struct SStudent * student;
unsigned number;
char keyValue;
student = CreateTable();
clrscr();
Help();
printf("\n= ");
while((keyValue = getche()) != 'q' && keyValue != 'Q' && keyValue != 27)
{
puts("");
switch(keyValue)
{
case 'l': case 'L':
PrintTable(student); break;
case 'd': case 'D':
printf("Please input the number you want delete: ");
41
scanf("%d", &number);
QueryTable(student, number);
break;
case 'a': case 'A':
AppendToTable(student); break;
case 'i': case 'I':
InsertToTable(student); break;
case 's': case 'S':
SortTable(student);
puts("Sort complished! Please use command L to list.");
break;
case 'v': case 'V':
Save(student); break;
case 'o': case 'O':
Load(student); break;
case 'c': case 'C':
clrscr(); break;
case 'h': case 'H':
Help(); break;
default: puts("Error command!");
}
printf("\n= ");
}
}
struct SStudent * CreateTable()
{
struct SStudent * stu;
stu = (struct SStudent *) malloc(sizeof(struct SStudent));
stu->number = 0;
stu->score1 = stu->score2 = stu->score3 = 0;
stu->name[0] = '\0';
stu->link = NULL;
return(stu);
}
void AppendToTable(struct SStudent * stu) {
struct SStudent * next, * last;
int number;
last = stu;
while(last->link) last = last->link;
printf("Please input the number (0 to quit): ");
scanf("%d", &number);
while(number)
{
next = (struct SStudent *) malloc(sizeof(struct SStudent));
next->number = number;
printf("Please input name: ");
scanf("%10s", next->name);
printf("Please input the first score: ");
scanf("%d", &next->score1);
printf("Please input the second score: ");
scanf("%d", &next->score2);
printf("Please input the third score: ");
scanf("%d", &next->score3);
last->link = next;
last = last->link;
printf("\nPlease input the number (0 to quit): ");
scanf("%d", &number);
42
}
last->link = NULL;
}
void InsertToTable(struct SStudent * stu) {
struct SStudent * next, * last;
int number;
printf("Please input the number (0 to quit): ");
scanf("%d", &number);
while(number)
{
next = (struct SStudent *) malloc(sizeof(struct SStudent));
next->number = number;
printf("Please input name: ");
scanf("%10s", next->name);
printf("Please input the first score: ");
scanf("%d", &next->score1);
printf("Please input the second score: ");
scanf("%d", &next->score2);
printf("Please input the third score: ");
scanf("%d", &next->score3);
last = stu;
while(last->link)
{
if(last->link->number > next->number)
{
next->link = last->link;
last->link = next;
break;
}
else last = last->link;
}
printf("\nPlease input the number (0 to quit): ");
scanf("%d", &number);
}
}
void QueryTable(struct SStudent * stu, unsigned number)
{
struct SStudent * temp, * next;
next = stu;
while(next->link)
{
if(next->link->number == number)
{
temp = next->link;
next->link = next->link->link;
free(temp);
}
else next = next->link;
}
}
void PrintTable(struct SStudent * stu) {
stu = stu->link;
if(!stu)
{
puts("The table is EMPTY!");
43
return;
}
printf("number\tname\t\tscore1\tscore2\tscore3\n");
while(stu)
{
printf("%3d\t", stu->number);
printf("%-10s\t", stu->name);
printf("%4d\t", stu->score1);
printf("%4d\t", stu->score2);
printf("%4d\t\n", stu->score3);
stu = stu->link;
}
}
void SortTable(struct SStudent * stu) {
struct SStudent * next, * last, * temp;
int flag;
last = stu;
while(last->link)
{
next = stu; flag = 1;
while(next->link != last->link)
{
if(next->link->number > last->link->number)
{
temp = last->link;
last->link = last->link->link;
temp->link = next->link;
next->link = temp;
flag = 0;
break;
}
else next = next->link;
}
if(flag) last = last->link;
}
}
void Save(struct SStudent * stu) {
unsigned char filename[13];
FILE * fileSave;
printf("Please input the filename you want save in: ");
scanf("%s", filename);
if((fileSave = fopen(filename, "wb")) == 0)
{
printf("Cannot open file %s !\n", filename);
return;
}
puts("Saveing ...");
stu = stu->link;
while(stu)
{
fwrite(stu, sizeof(struct SStudent), 1, fileSave);
stu = stu->link;
}
puts("Saveing is finished!"); }
44
void Load(struct SStudent * stu)
{
unsigned char filename[13];
FILE * fileLoad;
struct SStudent * temp;
while(stu->link)
{
temp = stu->link;
stu->link = stu->link->link;
free(temp);
}
temp = (struct SStudent *) malloc(sizeof(struct SStudent));
printf("Please input the filename you want load from: ");
scanf("%s", filename);
if((fileLoad = fopen(filename, "rb")) == 0)
{
printf("Cannot open file %s !\n", filename);
return;
}
puts("Loading ...");
while(fread(temp, sizeof(struct SStudent), 1, fileLoad))
{
stu->link = temp;
stu = stu->link;
temp = (struct SStudent *) malloc(sizeof(struct SStudent));
}
stu->link = NULL;
puts("loading is finished!");
}
void Help()
{ puts(" *********************************************");
puts(" * Student System Command Help *");
puts(" *********************************************");
puts(" * L = List all records *");
puts(" * D = Delete a record seleced by number *");
puts(" * A = Append records *");
puts(" * I = Insert records *");
puts(" * S = Sort records *");
puts(" * H = Show this help message *");
puts(" * V = Save records to a file *");
puts(" * O = Load records from a file *");
puts(" * C = Clear screen *");
puts(" * Q = Quit System *");
puts(" *********************************************");
puts(" * Copyright (C) 1999.09.09 By Kcliu *");
puts(" *********************************************"); }
45
附录一: Turbo C 程序设计上机指导 Turbo C 程序设计初步
本节主要介绍Turbo C程序设计的基本步骤及如何编译、调试和运行源程序。并给出Turbo C的常用编辑命令。最后介绍Turbo C编译、连接和运行时的常见错误。 一、Turbo C程序设计基本步骤
程序设计方法包括三个基本步骤:
第一步: 分析问题。
第二步: 画出程序的基本轮廓。
第三步: 实现该程序。
(A). 编写程序
(B). 测试和调试程序
(C). 提供数据打印结果
下面, 我们来说明每一步的具体细节。
第一步: 分析问题
在这一步, 你必须:
a. 作为解决问题的一种方法, 确定要产生的数据(输出)。作为这一步的一部分, 你应定义表示输出的变量。
b. 确定需产生输出的数据(称为输入), 作为这一步的一部分, 你应定义表示输入的变量。
c. 设计一种算法, 从有限步的输入中获取输出。 这种算法定义为结构化的顺序操作, 以便在有限步内解决问题。就数字问题而言, 这种算法包括获取输出的计算, 但对非数字问题来说, 这种算法包括许多文本和图象处理操作。
第二步: 画出程序的基本轮廓
在这一步, 你要用一些句子(伪代码)来画出程序的基本轮廓。每个句子对应一个简单的程序操作。对一个简单的程序来说, 通过列出程序顺序执行的动作,便可直接产生伪代码。然而, 对复杂一些的程序来说, 则需要将大致过程有条理地进行组织。对此, 应使用自上而下的设计方法。
当使用自上而下的设计方法时, 你要把程序分割成几段来完成。列出每段要实现的任务, 程序的轮廓也就有了, 这称之为主模块。当一项任务列在主模块时,仅用其名加以标识, 并未指出该任务将如何完成。这方面的内容留给程序设计的下一阶段来讨论。将程序分为几项任务只是对程序的初步设计。整个程序设计归结为下图所示的流程图1。
46
?????????????????
? 主模块 ?
????????? ? 输入数据 ?
? 主模块 ? ? 计算购房所需的金额 ?
?任务1 ? ? 计算装修所需的金额 ?
?任务2 ? ? 计算总金额 ?
?任务3 ? ? 输出计算结果 ?
?任务4 ? ? ?
????????? ?????????????????
????????????????????????
?????????????????????????????
?输入数据??购房额..??装修额..??总额..??输出结果?
?????????????????????????????
图1. 程序初步设计 图2. 第二级程序设计
如果把主模块的每项任务扩展成一个模块, 并根据子任务进行定义的话, 那么, 程序设计就更为详细了(见图2.)。这些模块称为主模块的子模块。程序中许多子模块之间的关系可象图2.中那样归结为一张图。这种图称为结构图。
要画出模块的轮廓, 你可不考虑细节。如果这样的话, 你必须使用子模块,将各个模块求精, 达到第三级设计。继续这一过程, 直至说明程序的全部细节。
这一级一级的设计过程称为逐步求精法。在编写程序之前, 对你的程序进行逐步求精, 对你来说, 是很好的程序设计实践, 会使你养成良好的设计习惯。
我们刚才描述了程序设计中自上而下的设计方法。实际上就是说, 我们设计程序是从程序的"顶部"开始一直考虑到程序的"底部"。
第三步: 实现该程序
程序设计的最后一步是编写源程序。 在这一步, 把模块的伪代码翻译成Turbo C语句。
对于源程序, 你应包含注释方式的文件编制, 以描述程序各个部分做何种工作。此外,源程序还应包含调试程序段, 以测试程序的运行情况, 并允许查找编程错误。一旦程序运行情况良好, 可去掉调试程序段, 然而, 文件编制应做为源程序的固定部分保留下来, 便于你或其他人维护和修改。
二、源程序的输入、编译和运行
C语言是一种中级语言, 用户用C语言编写的程序称为源程序, 存放用C 语言所写源程序文件名字最后的两个字符一般必须为".c"。计算机硬件不能直接执行源程序, 必须将源程序翻译成二进制目标程序。翻译工作是由一个程序完成的, 这个程序称为编译程序, 翻译的过程称为编译, 编译的结果称为目标程序, 存放目标程序文件名字的最后四个字符一般为".OBJ"或".O"。程序翻译成目标程序后, 便可进行连接。"连接"的目的是使程序变成在计算机上可以执行的最终形式。在这一阶段, 从系统程序库来的程序要与目标程序连接, 连接的结果称为执行程序, 存放执行程序文件名字一般以".EXE"结尾。
在Turbo C集成开发环境中建立一个新程序通常有以下几个步骤:
(1). 在编辑器中编写源文件。
(2). 生成可执行文件。
在DOS提示符下键入TC, 即可进入Turbo C了。进入主TC屏后, 按F3键, 即可。在随之出现的框中输入文件名, 文件名可以带".C"也可以不带( 此时系统会自动加上)。输入文件名后, 按回车, 即可将文件调入, 如果文件不存在, 就建立一个新文件(也可用下面例子中的方法输入文件名)。系统随之进入编辑状态。就可以输入或修改源程序了, 源程序
47
输入或修改完毕以后, 按Ctrl+F9(同时按下Ctrl键和F9键), 则立即进行编译、连接和执行, 这三项工作是连续完成的。
下面我们试着建立一个Turbo C名为"HELLO.C"的源程序(因程序很小, 这里就不画出该程序的轮廓图了):
1. 操作步骤:
(1). 将系统置于DOS提示符下:
(2). 键入命令:
tc hello.c
使系统进入Turbo C集成开发环境, 并建立一个名为HELLO.C的文件。这时, 系统进入Turbo C编辑环境。
(3). 通过键盘输入程序, 例如:
main()
{
printf("Hello, world\n");
}
则程序进入计算机存贮器。
2. 程序存盘
为防止意外事故丢失程序, 最好将输入的程序存贮到磁盘中。在编辑窗口下,可直接按F2键或按F10键, 再按F键进入File菜单项, 再按S或W键将文件存盘。存盘时屏幕最底行会显示:
"saving edit file"
3. 编译一个程序
对源程序进行编译有两种方法: (1). 直接按Alt+F9即可。(2). 按F10 键返回主菜单, 选择Compile项, 屏幕显示Compile 下拉菜单, 从下拉菜单中选择 Compile to .OBJ项, 按回车键。
进入编译状态后, 屏幕会出现一个编译窗口, 几秒钟后, 屏幕显示一闪烁信息:
Success: press any key 表示编译成功。此时可按任意键, 编译窗口消失, 光标返回主菜单。
如果编译时产生警告Warning或出错Error信息, 这些具体错误信息会显示在屏幕下部的信息窗中, 必须纠正这些错误。对源程序进行修改, 重新进行编译。
4. 运行程序
源程序经编译无误后, 可以投入运行。具体操作如下:
(1). 如果当前还在编辑状态, 可按Alt+R, 再选择RUN项即可。
(2). 按Ctrl+F9。
程序投入运行时, 屏幕会出现一个连接窗口, 显示Turbo C 正在连接和程序所需的库函数。连接完毕后, 会出现屏幕突然一闪, 后又回到TC主屏幕, 发生了什么? 让我们按Alt+F5看看, 此时屏幕被清除, 在顶部显示"Hello, world"字样。再按任意键, 即可又回到TC主屏幕。
5. 列磁盘文件目录
现在请按Alt+X退出Turbo C, 返回DOS提示符, 键入dir hello.*, 回车, 则屏幕显示:
HELLO C 42 1-09-93 10:18
HELLO OBJ 221 1-09-93 10:22
HELLO EXE 4486 1-09-93 10:25
...
第一个文件HELLO.C是源文件文本, 在DOS提示符下键入TYPE HELLO.C命令,可在屏幕上显示该文件的内容。可看到该程序只有42个字节。
第二个文件HELLO.OBJ是Turbo C编译程序产生的二进制机器指令(目标码),如果用DOS命令TYPE显示该文件, 屏幕可能会出现混乱的信息。
第三个文件HELLO.EXE是Turbo C连接程序产生的实际可执行文件。在DOS 提示符下键入HELLO并按回车, 屏幕将显示"Hello, world"。
48
Turbo C 常用的编辑命令
Turbo C编辑程序大约有50条命令, 用以移动光标, 按页查看正文, 查找并替换字符
串等。如下表所示。
表1. Turbo C编辑程序命令 ???????????????????????????
? 默认键 类别 ? 功能
???????????????????????????
? 字符左 ? Ctrl+S或Left
基 ? 字符右 ? Ctrl+D或Right
本 ? 字左 ? Ctrl+A
光 ? 字右 ? Ctrl+F
标 ? 上行 ? Ctrl+E或Up
? 下行 ? Ctrl+X或Down 移
动 ? 上滚 ? Ctrl+W
命 ? 下滚 ? Ctrl+Z
令 ? 上一页 ? Ctrl+R或PgUp
? 下一页 ? Ctrl+C或PgDn ???????????????????????????
快 ? 行头 ? Ctrl+QS或Home
速 ? 行尾 ? Ctrl+QD或End
光 ? 窗口头 ? Ctrl+QE
标 ? 窗口底 ? Ctrl+QX
移 ? 文件头 ? Ctrl+QR
动 ? 文件尾 ? Ctrl+QC
命 ? 块头 ? Ctrl+QB
令 ? 块尾 ? Ctrl+QK
? 上次光标位置 ? Ctrl+QP ???????????????????????????
输 ? 插入模式 ? Ctrl+V或Ins
入 ? 插入行 ? Ctrl+N
与 ? 删除行 ? Ctrl+Y
删 ? 删除至行尾 ? Ctrl+QY
除 ? 删除光标左边字符? Ctrl+H或Backspace
命 ? 删除光标处字符 ? Ctrl+G或Del
令 ? 删除光标右边字符? Ctrl+T ???????????????????????????
? 标记块首 ? Ctrl+KB
? 标记块尾 ? Ctrl+KK
? 标记单个字 ? Ctrl+KT
块 ? 复制块 ? Ctrl+KC
命 ? 删除块 ? Ctrl+KY
令 ? 块取消 ? Ctrl+KH
? 块移动 ? Ctrl+KV
? 读块 ? Ctrl+KR
? 写块 ? Ctrl+KW ???????????????????????????
? 异常结束操作 ? Ctrl+U或Ctrl+Break
49
? 制表 ? Ctrl+L或Tab
其 ? 自动缩进 ? Ctrl+OI
? 定界符配对 ? Ctrl+Q[或Ctrl+Q]
它 ? 查找 ? Ctrl+QF
? 查找并替换 ? Ctrl+QA
? 查找标记 ? Ctrl+QN
? 退出编辑 ? Ctrl+KQ
???????????????????????????
Turbo C 程序的调试
一个程序设计好了以后, 通常会有一些错误, 查找和修改程序中的错误是令人头痛的事。Turbo C集成开发环境提供了一调试装置, 使得这一个工作容易了许多, 程序调试达到了编译和运行级。
一、TC消息窗口
使用TC最好的理由之一是它允许用户修改语法错误(编译时) 和评估编译器给出的警告。TC将编译器和连接器发出的消息收集到一缓冲区中, 然后在消息窗口中显示, 这样在访问源代码的同时, 还能一下看到这些消息。
现将上面的HELLO.C制造一点语法错误, 将第一行包含语句的#去掉, 再去掉第五行printf语句中的后引号。现在程序看上去是这样的:
include
main()
{
printf("Hello, world\n);
}
按CTRL+F9重新编译之。编译窗口将显示有多少错误和警告: 应为两个错误, 0个警告。
当看见编译窗口中的Press anykey提示时, 按空格键, 消息窗口立刻被激活,亮条出现在第一个错误或警告上, 这时编辑窗口中也会有一亮条--- 它标志着编译器给出的错误或警告在源代码中的相应位置。
这时可用光标键将消息窗口中的亮条上下移动, 注意到编辑窗口中的亮条也随着跟踪源代码中错误发生的位置。如果将亮条置于"compile"上, 则编辑器显示文件的最后位置。
如果消息窗口太长看不见, 可用左、右光标水平滚动消息, 为了一次能够多看点信息, 可按F5放大消息窗口。放大后, 编辑窗口不可见了, 因此不进行错误跟踪。现在, 保持分屏模式。
为了改正错误, 将消息窗口中的亮条置于第一个错误消息上, 回车, 光标移到编辑窗口中错误产生处, 注意, 编辑器状态给出所选消息( 这在放大模式下是有用的)改正之。(将第一行拿走的#重新写上)。
当不只一个错误时, 可用两种方法来修改下一错误。
第一种方法和前面一样, 按F6回到消息窗口选择想修改的下一条消息。
第二种方法不用回到消息窗口, 只要按Alt+F8, 编译器就会将光标移至消息 窗口中列的下一个错误。按Alt+F7可移至前一个错误。
这两种方法各有长短, 视情况而定。有时源代码中一个愚蠢的错误把编译弄糊涂了, 产生好多消息, 这时选择修改第一条消息就使得其余的一些错误消息没有什么意义了, 这种情况发生时, 使用方法也会方便些, 修改完第一个错误之后回到消息窗口, 再滚动到下一个有意义的消息上, 选择之。在别的情况下, 按Alt+F8会方便得多。
记住, Alt+F7和Alt+F8是热键, TC中无论何时均起作用。因此在消息窗口中按Alt+F8得到的不是当前亮行消息, 而是下一个消息(按Enter选择当前消息)。但如果没别的编译消息, Alt+F8就不起作用了。
50
注: 可以此法选择连接消息, 但它们不跟踪源文件。在修改语法错误的过程当中, 经常需要增加、删除正文, 编辑器是能够记住的, 依然能正确定位错误位置。 没有必要记住行号和增加、删除的正文行。
二、Turbo C集成调试器
一旦修改好语法错误之后程序编译就没什么问题了, 但还有可能不按要求运行, 因为可能有逻辑错误(运行错误)。这种错误跟踪就无助于发现错误位置了。TC有一个集成调试器可以跟踪运行错误。通过调试器可以运行, 在断点处暂停, 检查变量的值, 甚至可以改变之, 以看程序会有什么反应。
Turbo C集成调试器是源程序级的调试器, 即用同你编写程序一样的" 语言" 来控制调试器。例如, 为了显示数组中的一个元素的值, 可告诉调试器显示这样的表达式的值: Ctrl+F4 Debug/Eavluate 计算表达式, 允许修改变量的值。
Debug/Find Function 查找函数定义, 显示在编辑窗口中。 仅
在调试时才有效。
Debug/Call Stack 显示调用栈, 可显示任何函数的当前执 Ctrl+F3
行位置, 其方法是在调用栈中选择相应的
函数名。仅在调试时有效。 Debug/Source Debugging 控制是否允许调试: 置为On时, 集成调
试器和单独调试器均可用 ; 置为
Standalone时, 只能用单独调试器
调试, 虽然还能在TC中运行; 置为
None时, 在.EXE文件不置调试信息,
两种调试均不能调试。 Ctrl+F4 Break/Watch/Add Watch 增加一监视表达式。
Break/Watch/Delete Watch 删除一监视表达式。
Break/Watch/Edit 编辑一监视表达式。
Break/Watch/Remove All 删除所有监视表达式。
Watches
Ctrl+F8 Break/Watch/Toggle 设置或清除光标所在行的断点。
Breakpoint
Break/Watch/Clear 删除程序中所有断点。
Breakpoint
Break/Watch/Next 显示下一断点
Breakpoint
???????????????????????????????????
表3. 调试器菜单命令及其热键
???????????????????????????????????
热键 菜单命令 功能
???????????????????????????????????
F5 在整屏和分屏之间放大缩小活动窗口。 Alt+F5 将显示转到用户屏, 击任意键返回。
F6 在编辑窗口与监视窗口或消息窗口间
切换。
Alt+F6 若编辑窗口是活动的, 转到最近一次
装入编辑器的文件; 若下面窗口是活
动的, 则在监视窗口和消息窗口间切换。 Ctrl+F9 Run/Run 调试运行或不调试运行程序, 必要时
编译、连接源文件, 若编译、 连接时
Debug/Source Debuging和O/C/C/OBJ
51
Debug Information为On, 则运行程序
到断点或程序末尾。
Project/Remove Messages 删除消息窗口中的内容。
???????????????????????????????????
判断程序是否出错或者为什么出错是编程中最有挑战意义的一方面。这里建义你进行预
防性设计, 具体做法为:
(1). 将代码写清楚点, 应作适当缩进, 使用文字说明和描述性的变量名。
(2). 代码应简单, 把精力放在简单语句中的复杂表达式, 而不是一些复杂语句。Turbo C的代码优化机制将大大提高代码的效率, 而且调试、阅读、 修改起来容易。
(3). 尽量用目的简单、好定义的函数构建程序。 这会便于编制调试程序和分析结果, 而且阅读、修改起来也要容易一些。
(4). 应尽量减少各个函数要求的数据和改变数据的元素个数。这也会便于编制测试程序和分析结果; 同样便于阅读、修改程序。并且还限制了出错函数可能造成的巨大混乱的牵涉面, 使得能在一个调试器中多运行函数几次。
(5). 要留心编写程序中的公共函数, 或者说在其它程序中可再用的函数。编写、调试
一个一般性的函数通常要比编写、调试两个或更多的特殊函数要容易。 Turbo C编译、连接和运行时的常见错误
一、编译时的常见错误
1. 数据类型错误。此类错误是初学者编程时的常见现象, 下面是一些常见的数据类型错误:
(1) 所有变量和常量必须要加以说明。
(2) 变量只能赋给相同类型的数据。
(3) 对scanf()语句, 用户可能输入错误类型的数据项, 这将导致运行时出错, 并报出错信息。为避免这样的错误出现, 你就提示用户输入正确类型的数据。
(4) 在执行算术运算时要注意:
a. 根据语法规则书写双精度数字。要写0.5, 而不是写.5; 要写1.0, 而不是1。尽管C语言会自动地把整型转换成双精度型, 但书写双精度型是个好习惯。让C语言为你做强行转换这是一种效率不高的程序设计风格。 这有可能导致转换产生错误。
b. 不要用0除。这是一个灾难性的错误, 它会导致程序失败, 不管C 语言的什么版本, 都是如此, 执行除法运算要特别小心。
c. 确保所有的双精度数(包括那些程序输入用的双精度数) 是在实数范围之内。
d. 所有整数必须在整数允许的范围内。这适用于所有计算结果, 包括中间结果。
2. 将函数后面的";"忘掉。此时错误提示色棒将停在该语句下的一行, 并显示:
Statement missing ; in function <函数名>
3. 给宏指令如#include, #define等语句尾加了";"号。
4. "{"和"}"、"("和")"、"/*"和"*/"不匹配。引时色棒将位于错误所在的行, 并提示出有关丢掉括号的信息。
5. 没有用#include指令说明头文件, 错误信息提示有关该函数所使用的参数未定义。
6. 使用了Turbo C保留关键字作为标识符, 此时将提示定义了太多数据类型。
7. 将定义变量语句放在了执行语句后面。此时会提示语法错误。
52
8. 使用了未定义的变量, 此时屏幕显示:
Undefined symbol '<变量名>' in function <函数名>
9. 警告错误太多。忽略这些警告错误并不影响程序的执行和结果。编译时当警告错误数目大于某一规定值时(缺省为100)便退出编译器, 这时应改变集成开发环境Options/Compiler/Errors中的有关警告错误检查开关为off。
10. 将关系符"=="误用作赋值号"="。此时屏幕显示:
Lvalue required in function <函数名>
二、连接时的常见错误
1. 将Turbo C库函数名写错。这种情况下在连接时将会认为此函数是用户自定义函数。此时屏幕显示:
Undefined symbol '<函数名>' in <程序名>
2. 多个文件连接时, 没有在"Project/Project name中指定项目文件 (.PRJ文件), 此时出现找不到函数的错误。
3. 子函数在说明和定义时类型不一致。
4. 程序调用的子函数没有定义。
三、运行时的常见错误
1. 路径名错误。在MS-DOS中, 斜杠(\)表示一个目录名; 而在Turbo C 中斜杠是个某个字符串的一个转义字符, 这样, 在用Turbo C 字符串给出一个路径名时应考虑"\"的转义的作用。例如, 有这样一条语句:
file=fopen("c:\new\tbc.dat", "rb"); 目的是打开C盘中NEW目录中的TBC.DAT文件, 但做不到。这里"\"后面紧接的分别是"n"及"t", "\n"及"\t"将被分别编译为换行及tab字符, DOS将认为它是不正确的文件名而拒绝接受, 因为文件名中不能和换行或tab字符。正确的写法应为:
file=fopen("c:\\new\\tbc.dat", "rb");
2. 格式化输入输出时, 规定的类型与变量本身的类型不一致。例如:
float l;
printf("%c", l);
3. scanf()函数中将变量地址写成变量。例如:
int l;
scanf("%d", l);
4. 循环语句中, 循环控制变量在每次循环未进行修改, 使循环成为无限循环。
5. switch语句中没有使用break语句。
6. 将赋值号"="误用作关系符"=="。
7. 多层条件语句的if和else不配对。
53
8. 用动态内存分配函数malloc()或calloc()分配的内存区使用完之后, 未用free()函数释放, 会导致函数前几次调用正常, 而后面调用时发生死机现象, 不能返回操作系统。其原因是因为没用空间可供分配, 而占用了操作系统在内存中的某些空间。
9. 使用了动态分配内存不成功的指针, 造成系统破坏。
10. 在对文件操作时, 没有在使用完及时关闭打开的文件。
54
附录二:C语言程序设计调试技术 C语言程序设计调试技术(一)—— 运行错误的判断与调试
通常所说的运行错误有两种,一种是逻辑错误,即程序实际运行结果和对程序结果的期望不符;另一种是程序设计上的错误,但它也躲过了编译程序和连接程序的检查,通常表现为突然死机、自行热启动或者输出信息混乱。
相对于编译和连接错误来说,运行错误的查找和判断更为困难。编译和连接错误分别由编译程序和连接程序检查,尽管有时它们的出错信息和错误的实际原因之间有一些差距,但总还可以作为查错时的一种参考。而运行错误就不同了,很少或根本没有提示信息,只能靠程序员的经验来判断错误的性质和位置。下面简单的介绍一些常见运行错误的调试方法。
一种逻辑错误是由于在设计程序的算法时考虑欠周到而引起的,如对边界和特殊情况未作妥善处理。例如下面所示的循环:
while(count)
{
……….
count=count-1;
}
程序员的构思是进行count次循环。但是,如果count中原来的值为负数时,此循环就成了一个“死循环”而导致无法停机,显然是错误的。但是编译程序无法查出这类错误,只有到了程序运行之后才有可能发现。再如,在利用海伦公式计算三角形面积时,首先应该检查给出的三条边长是否确实可以构成一个三角形,否则计算结果时没有意义的;而在编写求解一般实系数一元二次方程的程序时,必须在程序中设计处理复数情况的程序段,以免对负数求平方根。通常在手算时不用事先考虑这些问题,可以在确实发生了问题以后在提出解决的方法。但是程序是为计算机设计的,而计算机并没有自行应变的能力,程序员必须事先将一切可能遇到的情况考虑周全,尤其是对于那些受用户委托设计或者作为商品出售的软件更是如此。
另一种常见的逻辑错误是由于程序输入时的打字错误造成的,例如将判断条件中的“〉=”误输入为“〉”,将相等判断“= =”误输入为赋值号“=”等。含有这类错误的程序在运行时出现的故障现象多种多样,而且通常很难与错误的原因联系起来。
输入的数据中包含错误或者输入数据的格式不符合要求当然也会影响到程序的运行结果,特别是在数据量比较大,而又采用键盘直接输入数据时更容易产生这类错误。我们建议在数据量比较大时采用文件方式存放数据,程序通过文件读写来输入输出数据,这样可以通过编辑数据文件来修改其中的错误,并且在重复计算或者调试程序时也就不用反复输入数据了。这种方法非常适宜于科学和工程计算类应用程序中的数据处理。另一种方法是避免使用C语言的scanf( )函数输入数据,而代之以自行编写的、具有比较完善的数据校验功能的输入模块。这种方法通常用于数据处理、事务管理等比较复杂的应用程序的开发中,通常将数据输入和用户界面等模块结合起来统一进行设计,这需要较高的编程技巧。 C语言程序设计调试技术(二)——基本调试手段
程序的基本调试手段有以下几种:数据校验、程序跟踪、边界检查和简化循环次数等。下面分别介绍之。
55
, 标准数据校验:在程序编译、连接通过以后,就进入了运行调试阶段。运行调试
的第一步就是用若干组已知结果的标准数据对程序进行检验。标准数据的选择非常重
要,一是要有代表性,接近实际数据;二是比较简洁,容易对其结果的正确性进行分析。
另外,对重要的临界数据也必须进行检验。
, 程序跟踪:对于比较复杂的大型程序来说,上述标准数据检验一次就完全通过的
可能性很小。通常程序中总是存在许多各种各样的错误(就好像出错是程序的基本特性,
一个错误也没有的程序反倒是罕见的意外),还需要多程序进行细致的调试工作。
程序跟踪则是最重要的调试手段。程序跟踪的基本原则是让程序一句一句的执行(按F7 F8),通过观察和分析程序执行过程中数据和程序执行流程的变化查找错误。就Turbo C而言,程序跟踪可以采用两种方法,一种是直接利用其集成环境中的分布执行、断点设置、变量内容显示等功能对程序进行跟踪,这种方法留在下一单元的编程与调试部分介绍;另一种是传统方法,通过在程序中直接设置断点、打印重要变量内容等来掌握程序的运行情况。例如,可以在程序中的关键部位插入这样的代码段:
/*--调试代码段-------------------------------------------------*/
printf(“break point 10:line 120---count>100\n”);
printf(“variable count=%d,x=%f,sum=%f\n,count,x,sum”);
getch();
/*--调试代码段结束-------------------------------------------*/
其中的变量可以根据程序的实际情况进行设计。使用getch( )函数的目的是要程序在执行到这一行时暂时停下来,从而可以让我们看清楚调试代码段所显示的信息。然后可以选择是否让程序继续执行。如果到这一断点时尚未发现错误,则可以按下任何一个键让程序继续运行到下一个断点;否则可使用组合键Ctrl+Break或者Ctrl+C键来中断程序,再使用编辑器对程序进行修改。在程序中的所有的问题都解决了之后,再将程序中所有的调试代码段统统删去,这种方法不但适用于 Turbo C,而且对于那些没有集成环境的C语言编译器来说就更为重要了。
, 边界检查:在设计检查用的数据时,要重点检查边界的特殊情况。例如,对于循环:
while(count<1000)
{
…………..
}
就应该设计数据检查count等于999、等于1000、等于0或者负数等情况。如果程序中有由if-else语句、switch语句等组成的分支结构,也应该设计相应的数据,使得分支中的每一条路径都要通过检查。
, 简化:在调试时,有时可以通过对程序进行某种简化来加快调试速度。例如减少循环次数、缩小数组规模、屏蔽某些次要程序段(如一些用于显示提示信息的子程序)等。但在进行简化工作时,一定要注意这种简化不能太过分,以至于无法代表原来程序的真实情况。例如,对于一个求解N元一次方程组的程序来说,仅将N等于2的情况调通是不够的,还不能保证该程序对于较大的方程组也能给出正确的结果。如果对于N=3或4的情况该程序也能正常工作,则在该程序中因为矩阵规模而出错的可能性就大大减少了(但这不说明该程序就一定没有错误了,例如如果该程序使用某种消元法,则还要考虑主对角线元素的绝对值过小或者为0的情况)。
C语言调试技术(三) -Turbo C 集成环境的调试功能
由于程序中存在着分支、循环等结构,造成了程序运行时的变化规律和其静态结构之间存在着一定的差异,因此仅靠阅读程序本身很难掌握程序运行时各变量内容的动态变化,这就给我们调试程序中的运行错误带来了很大的困难。如果能够在程序运行过程中动态地显示程序执行的流向和各变量的内容,则有助于程序员了解程序的动态运行情况,从而更好、更快地调试程序。TurboC集成环境有很强的动态调试能力,下面介绍其中最主要的几种手段。
56
(1)运行(Run: Run,ctrl-F9):运行程序员编写的应用程序。该选项的功能非常强大,如果源程序尚未编译,或者在编译以后又修改了源程序,则会在运行程序之前先自动对源程序进行编译和连接工作。如果源程序中设置有断点(参阅(2)),则只执行到断点处就停下来,以便程序员调试程序。若再次调用该选项,则从当前断点开始运行程序,直到程序结束或者到下一个断点处。 ?
(2)设置断点 (break,watch: toggle breakpoint, Ctrl-F8) :设置断点的作用是使程序可以分段运行。如果在程序中的某个语句处设置了断点,则使用上述运行选项执行程序时就会在断点处停下来,这时可以利用下面介绍的其它调试功能观察程序的运行情况,包括各数据区和变量的当前值。在程序中可以设置多处断点,这时每调用一次运行功能,则程序从当前位置执行到下一个断点处:如果断点是设置在循环中的,则每循环一次、程序就中断一次。为了管理断点,在集成环境的断点与观察(break/watch)子菜单中还有两个辅助功能:清除所有断点(clear all breakpoints)和查看下一个断点(View nextbreakpiont)。 (3)变量查看及修改 (Debug:Evaluate, CtrL-F4):该项功能用于在程序运行到断点处时查看变量或其它数据项的内容。对于变量来说,还可以改变其内容,便于下一步继续调试。在调用本功能时,屏幕上弹出一个窗口,窗口分为三栏:,最上面是设置(Eva1uate)栏,用于输入要观察的变量名或表达式;中间是结果(ResdO栏,用于显示要观察的变量或表达式的值;而最下方是修改(New va1ue)栏,用于修改变量的值。在查看或修改完毕时可以使用退出键(ESC)返回编辑状态。
(4)查看函数调用情况(Debug: Call stack, Ctrl-F3):该功能用于查看当前调用栈的情况。如果断点设置在函数中,则调用该功能会在屏幕上弹出一个窗口,显示出程序运行到断点时的函数调用顺序(最下方是主函数,最上方是当前正在执行的函数)。 (5)查找函数(Debug: Find function):可用于在程序中快速查找某个函数的位置。如果一个程序很大,或者包括多个源程序文件,则使用该功能是相当方便的。 调用该功能的结果是光标移到指定函数的开始。
(6)更新屏幕内容(Debug: Refresh disp1ay):在调试程序的过程中,有时程序的输出结果会破坏集成环境的编辑版面显示内容,这时可以使用该功能恢复正确的屏幕内容。 (7)设置观察对象(break/watch:Add watch,ctrl-F7):使用该项功能可以将变量或表达式设置为观察对象,这些观察对象的值在调试过程中会在屏幕下方的信息显示窗口中显示出
变量查看与修改(Evaluate)" 功能,但更直观、更方便,只来。该功能类似于上面介绍的“
是不能修改变量的值、另外,在断点与„观察(Break/watch)子菜单中还有几项用于管理观对象的功能选项:删除观察对象(Delete watch)选项 ,它用于删除一个观察对象;使用该项功能时,首先应使用屏幕窗口切换键(F6)将光标切换到信息显示窗口中,然后使用光标选定要删除的观察对象,再使用本功能选项删除选定的观察对象。编辑观察对象(Edit watch)选项,它用于修改观察对象,用法和删除观察对象相同。删除所有删除对象(remove all watches)选项,它可以删除所有的观察对象。
(8)执行到当前光标位置(Run: Go to cursor, F4):使程序执行到当前光标位置。 C语言调试技术(四)--图形程序运行的条件
Turbo C为用户提供了一个功能很强的画图软件库,它又称为BorLand图形接口(BGI),它包括图形库文件(graphics.lib),图形头文件(graphics.h)和许多图形显示器(图形终端)的驱动程序(如CGA.BGI、EGAVGA.BGI等)。还有一些字符集的字体驱动程序(如goth.chr黑体字符集等)。编写图形程序时用到的一些图形库函数均在graphics.lib 中,执行这些函数时,所需的有关信息(如宏定义等)则包含在graphics.h头文件中。因此用户在自已的画图源程序中必须包括graphics.h头文件,在进行目标程序连接时,要将graphics.lib连接到自己的目标程序中去。
图形系统的初始化
57
在编制图形程序时,进入图形方式前,首先要在程序中对使用的图形系统初始化,即要用什么类型的图形显示适配器的驱动程序,采用什么模式的图形方式(也就是相应程序的入口地址),以及该适配器驱动程序的寻找路径名。所用系统的显示适配器一定要支持所选用的显示模式,否则将出错。Turbo.C提供了一个图形系统初始化函数initgraph可完成这些功能。
图形系统的初始化函数
void far initgraph(int far *driver, int far *mode, char for path-for-driver);当我们使用的存储模式为tiny(微型)、 small(小型)或medium(中型)时,不需要远指针,因而可以将初始化函数调用格式写成如下形式(该说明适用于后面所述的任一函数): intitgraph(&graphdriver, &graphmode,,””); 其中驱动程序目录路径为空字符“”时,表示就在当前目录下,参数graphmode用所示的模式号或标示符来定义,参数graphdriver是一个枚举变量{DETECT,CGA,VGA,…},一般: graphdriver=DETECT
一旦执行了初始化,显示器即被设置成相应模式的图形方式。下面是某画图程序的开始部分,它包括对图形系统的初始化:
#include”graphics.h”
main()
{
int graphdriver=DETECT;
int graphmode;
intgraph(& graphdriver, & graphmode,””);
上面初始化过程中,将由DETECT检测所用适配器类型,并将当前目录下相应的驱动程序装入,并采用最高分辩率显示模式作为graphmode的值。如检测到为CGA适配器时,则graphmode等于4或为CGAHI,若检测到VGA适配器,则graphmode等于2或为VGAHI。
图形程序运行的条件
由于图形程序运行并显示图象直接与显示器有关,而如何控制驱动显示器进行显示,Tubro C并没有向用户提供这种技术,而这也是不必要的,因为它与显示器硬件结构息息相关,编程者并不需要知道这些东西,否则太复杂了~但用户的图形程序要能运行并显示,则必须要包含有驱动显示器的这种程序。不同种类的显示器因硬件结构不同,因而驱动程序也不同,这些驱动程序已经在Tubro C系统盘上提供。在用户的图形程序中,进行图形系统初始化时,即执行函数。
initgraph(&graphdriver,&graphmode,char path-for-driver);
程序按照path-for-driver所指的路径将图形驱动程序装入内存。这样,以后的图形功能才能被支持。若在所指路径下找不到相应显示器的驱动程序,或没有对驱动程序进行装入操作,则运行图形程序时,就会在屏幕上显示出错信息:
BGI Error: Graphics not initialized(use”initgraph”)
当Tubro C2.0安装在软盘上时,没有安装上图形驱动程序(如CGA,BGI,EGAVGA.BGI等),所以必须在工作盘上复制上这些文件,否则图形程序就无法运行,而出现上述的错误信息。
那么,能不能不用egavga.bgi而运行图形程序,用以下步骤就可以:
\tc\bgiobj egavga
\tc\tlib lib\graphics.lib+egavga.obj 编程序时在用initgraph()之前先调用registerbgidriver(EGAVGA_driver);
58
59