3 选择结构程序设计
学习方法:
课前 按照 授课计划 上指明的进度 预习
上课认真听讲,课件可下载作为笔记
课下 参照教材 理解 课件上所讲的内容和相关知识点
完成作业 &上机调试例 1:比较两个数
从键盘输入两个数,输出其中最大的一个数。
问题分析开 始输入两整数 a,b
a>b
输出 a 输出 b
结 束如果 a>b,
a是最大值,
否则,
b是最大值。
程序 1
/*比较两个数 */
#include <stdio.h>
int main()
{
int a,b;
printf("Please input two integers:\n");
scanf("%d %d",&a,&b);
if(a>b) /*比较后直接输出比较结果 */
printf(" max=%d ",a);
else
printf(" max=%d ",b);
return (0);
}
涉及的语法
选择结构 语句格式
if(条件表达式 )
{
… …
}
else
{
… …
}
else分支可以省略,此时为单分支结构涉及的语法
- 关系运算符
> < >= <= == !=
大于,小于,大于等于,小于等于,等于,
不等于
运算结果只有两个值,0和 非 0,0表示假,
即该关系不成立;非 0,表示真,即该关系成立
例如,2006%4==0的结果是?
0,表示假(关系不成立)
优先级:在算术运算符和赋值运算符之间。
结合性:自左向右程序 2
/*比较两个数 */
#include <stdio.h>
int main()
{
int a,b,max;/*max用来存储最大值 */
printf("Please input two integers:\n");
scanf("%d %d",&a,&b);
if(a>b)
max=a;
else
max=b;
printf(" max=%d ",max);
return (0);
}
开 始输入两整数 a,b
a>b
max=a max=b
输出 max
结 束请根据下列算法写程序。
例 2:判断闰年
从键盘上输入一个年份,然后判断这个年份是否为闰年。
分析结果(算法)
输入 year
判断 year是否为闰年
输出 yes或 no 如果 year能被 4整除且不能被 100
整除,或者 year能被 400整除,
year是闰年,
否则
year不是闰年。
分析
year能被 4整除 且 不能被 100整除,或者 year能被
400整除
(?)或者 (?)
(?且?)或者 (?)
(?&&?)||(?)
(year%4==0&&year%100!=0)||(year%400==0)
如果 year能被 4整除且不能被 100整除,或者 year能被 400整除,
year是闰年,
否则
year不是闰年。
if(year%4==0&&year%100!=0)||(year%400==0)
{
printf(“%d is a leap year!”,year);
}
else
{
printf(“%d is not a leap year!”,year);
}
结果
#include <stdio.h>
void main()/*判断闰年 */
{ int year;
printf(“please input year:”);
scanf(“%d”,&year);
if((year%4==0&&year%100!=0)||(year%400==0))
{
printf(“%d is a leap year!”,year);/*一般要缩进 */
}
else
{
printf(“%d is not a leap year!”,year); /*缩进与上边一致 */
}
}
输入 1998,输出结果是什么?
1998%4==0不成立,然后
1998%400==0不成立,所以,
整个条件不成立,不是闰年,
注意,1998%100==0并没有判断,这里采用了 懒惰求值法,
因为无论它是何值,第一个条件都不可能成立了。
输入 2004,会怎样?
2000%4==0成立,
2000%100!=0成立,所以左边的第一个条件成立,
2004%400==0不用再判断了,
同样是使用 懒惰求值法 。
输入 2100,又会怎样?
不是闰年简单的程序测试
一次输入,得到正确的输出结果并不能说明问题,
要精心选择不同的输入,反复进行测试,才能逐渐发现程序里的问题
程序测试分为结构测试和功能测试两类
结构测试又为分:
语句覆盖法
分支覆盖法
条件覆盖法
组合条件覆盖法
对于前一个例题,最好根据条件中的三个表达式的组合,即选择 2*2*2=8个输入进行程序的测试。
涉及的语法
-逻辑运算符
逻辑运算也被称为布尔( Boolean) 运算,即参与运算的 数据 和运算 结果 都只有两个值,0和 非 0,
0表示假 (条件不成立 );非 0表示真 (条件成立 )
与运算 &&(相当于而且 )
a && b当且仅当两者都为真,则结果为真
( a > b && b > c); a大于 b,并且 b大于 c
或运算 || (相当于或者 )
a || b只要两者中有一个为真,结果就为真
( a > b || b > c); a大于 b,或者 b大于 c
求反 !
单目运算符
( !a ); 如果 a是 0,结果非 0;如果 a是非 0,结果是 0
并不改变 a的值优先级,!优先级同其它单目运算符,&&和 ||比关系运算符低,&&又比 ||高。
结合性:自左向右
c是字母吗?
输出,是,
NO YES
输入 c
输出,不是,
结束开始练习 1:从键盘上输入一个字符,判断它是否是小写字母?
#include <stdio.h>
void main()
{ char c;
printf(“please input a character:”);
c=getchar();/*从键盘上输入一个字符并存储到变量 C中 */
if( (c>=?a?&&c<=?z?) )
{
printf(“%c is a letter!”,c);
}
else
{
printf(“%c is not a letter!”,c);
}
}
(letter>=97&&letter<=122)
a? <=c<=?z?对吗?
请上机验证,作为作业如何判断是否是英文字母?
(c>=?a?&&c<=?z?)||
(c>=?A?&&c<=?Z?)
作业 1
求三个数的最大值。
验证题:练习 1中的表达式写成?a? <=c<=?z?,
并分别选取三种数据:小于‘ a?的,小字字母,
大于‘ z?的字符输入,验证这种写法是否正确?
再写成 c>=?a?&&c<=?z?验证一下。
选做:求一元二次方程 ax2+bx+c=0的根,
整数系数从键盘输入。 (应该根据输入的系统判断是否为一元二次方程,是否有根等情况 )
作业 1讲评 求三个数的最大值
#include<stdio.h>
void main()/*方法 1*/
{
int a,b,c,max;
printf("Please input 3 integers:");
scanf("%d %d %d",&a,&b,&c);
if (a>b)/*求出 a,b的最大值,放入 max*/
max=a;
else
max=b;
if (max>c) /*求出 max,c的最大值并输出 */
printf("The Max is,%d\n",max);
else
printf("The Max is,%d\n",c);
getchar();
}
作业 1讲评 -写法 2
#include<stdio.h>
void main()/*方法 1*/
{
int a,b,c,max;
printf("Please input 3 integers:");
scanf("%d %d %d",&a,&b,&c);
(a>b)? max= a:max=b;
/*求出 a,b的最大值,放入 max。 或者用 max=(a>b)?a:b;*/
if (max>c) /*求出 max,c的最大值并输出 */
printf("The Max is,%d\n",max);
else
printf("The Max is,%d\n",c);
getchar();
}
唯一的三目运算符
代替简单的 if-else
优先级:刚好高于赋值运算符
结合性:自右向左作业 1讲评 -很多学生的写法
#include<stdio.h>
void main()/*方法 2*/
{
int a,b,c;
printf("Please input 3 integers:");
scanf("%d %d %d",&a,&b,&c);
if (a>b&&a>c)
printf("The Max is,%d\n",a);
else if (b>a&&b>c)
printf("The Max is,%d\n",b);
else
printf("The Max is,%d\n",c);
getchar();
}
如果分支中只有一条语句,{}可以省略,一般将复杂的分支放入 else里。
作业 1讲评 -验证题
#include <stdio.h>
void main()
{ char c;
printf(“please input a character:”);
c=getchar();
if(?a? <=c<=?z? )
{
printf(“%c is a letter!”,c);
}
else
{
printf(“%c is not a letter!”,c);
}
}
无论输入什么,输出都是,is a letter”
原因是:两个 <=的优先级相等,而它们的结合性又是自左向右,因此先算左边,无论输入什么字符,左边的判断结果不是 0(不成立 ),就是 1(成立 ),
总之,总是小于‘ z?的 ASCII码 122,
所以第二次判断的结果永远是成立,
因此执行 if的分支。
选做题算法输入 a,b,c
输出计算结束怎么求?
a=0?NO
非二次方程
YES
△ <0?
求解并输出
NO
无解
YES
#include <math.h>
#include <stdio.h>
main()
{
float a,b,c,delta,x1,x2,p,q;
printf("Please enter the coefficients a,b,c:");
scanf(“%f,%f,%f”,&a,&b,&c); /*输入三个系数 */
if(a==0)
{ printf(,not a quadratic!”);}
else
{ delta = b * b - 4 * a * c; /*计算判别式,应在判断之前求解 */
if(delta<0)
{ printf(“No root!”); } /*输出,无解” */
else
{ p = - b / (2 * a); q = sqrt(delta) / (2 * a);
x1 = p + q; x2 = p - q; /*求实根 x1,x2 */
printf(“x1=%7.4f,x2=%7.4f\n”,x1,x2); /*输出 x1和 x2*/
}
}
}
选做题答案 忠告:为避免出错,尽量将每个分支中的 语句用 {}括起来。
若不括会怎样呢?
答,不括的话,
只有紧挨在后边的一句才是 else的分支,其它语句不再属于 else分支例 3:计算分段函数的值
ex (x > 0)
计算分段函数 y= 0 (x = 0) 的值,x的值由键盘输入。
e-x (x < 0)
算法 1:
1,输入 x
2,若 x<0,则 y= e-x
3,若 x=0,则 y= 0
4,若 x>0,则 y= ex
5,输出 y
/*程序 1*/
#include<stdio.h>
#include<math.h>
void main()
{
float x,y;
scanf(“%f”,&x);
if(x<0) y=exp(-x);
if(x==0) y=0;
if(x>0) y=exp(x);
printf(“y=f(%f)=%f\n”,x,y);
}
当每个分支彼此能够分得很楚,可以用 多个单分支 解决问题。
算法 2
输入 x
输出 y
计算结束怎么求?
X>0?NO
y=ex
YES
X<0?
y=0
NO
y=e-x
YES
程序 2
#include<stdio.h>
#include<math.h>
void main() /*程序 2*/
{
float x,y;
scanf(“%f”,&x);
if(x>0)
{
y=exp(x);
}
else
{
if(x<0)
{
y= exp(e,-x);
}
else
{
y=0;
}
}
printf(“y=f(%f)=%f\n”,x,y);
}
嵌套,一层套一套
此时要注意 if-else的配对问题。
一般加上 { }会明确配对,对避免逻辑错误非常有好处
要注意内层的条件中还有隐含条件例 4
根据输入的百分制成绩 score,分段转换成相应的等级并打印输出。
分析:
输入 score
转换并输出 (五个分支 )
#include <stdio.h>
void main()
{ int score;
printf("Please enter score:");
scanf("%d",&score);
if (score < 0 || score > 100) /*对输入数据的合法性进行检查 */
printf("Input error!\n");
else if (score >= 90)
printf("%d--excellent\n",score);
else if (score >= 80)
printf("%d--good\n",score);
else if (score >= 70)
printf("%d—fit exactly\n",score);
else if (score >= 60)
printf("%d--pass\n",score);
else
printf("%d--fail\n",score);
}
方法一程序
在 else分支嵌套
没有 else会怎样?
答:例如,输入 100
分,会出现输出几个对应等级的情况。因为加上 else会过滤掉一些条件。
方法二输出 A
Score/10有 11种情况
10
输出 A9
输出 E0
…
switch(score/10)
{case 10:
case 9,printf(“%d----excellent\n”,score);break;
case 8,printf(“%d----good\n”,score);break;
case 7,printf(“%d----fit exactly\n”,score);break;
case 6,printf(“%d----pass\n”,score);break;
case 5:
case 4:
case 3:
case 2,
case 1:
case 0,printf(“%d----fail\n”,score);break;
}
default,printf(“%d----fail\n”,score);break;
用于判断分支的表达式
表达式的所有可能结果列举在 case后
当上面的情况与下边的情况相同,后边的分支语句可省略
default表示除上述情况以外的所有情况。
当遇到 break时,程序转到 switch语句的的 }后边
#include <stdio.h>
void main()
{ int score;
printf("Please enter score:");
scanf("%d",&score);
if(score<0||score>100)
printf(“Input error!\n”);
else
{ switch(score/10)
{ case 10:
case 9,printf(“%d----excellent\n”,score);break;
case 8,printf(“%d----good\n”,score);break;
case 7,printf(“%d----fit exactly\n”,score);break;
case 6,printf(“%d----pass\n”,score);break;
case 5:
case 4:
case 3:
case 2,
case 1:
case 0,printf(“%d----fail\n”,score);break;
}
}
}
方法二程序
default,printf(“%d----fail\n”,score);break;
执行过程??
涉及到的语法知识
-switch语句
一般用于菜单等多分支的情况
switch (表达式 )
{
case 常数 1:语句序列 1;
case 常数 2:语句序列 2;
…………
default:语句序列 n;
}
一般结果为多个离散值的情况更详细的语法知识请查阅 P96-97。
读程序
main()
{ int a=1,b=0;
switch(a)
{case 1:switch(b)
{case 0:putchar(?0?); break;
case 1,putchar(?1?); break;
} break;
case 2:printf(“a=2”);break;
}
} !!!读程序时一定注意有没有 break,并正确分析 break后转到了哪儿?
问题
if-else嵌套和 switch都可以解决多分支的情况,在遇到多分支的时候使用哪个呢?
当多个可能的情况是离散值时可以选用
switch,其他情况下用 if-else嵌套。
练习 2,计算器程序
编程设计一个简单的计算器程序,要求根据用户从键盘输入如下形式的表达式:
操作数 1 运算符 op 操作数 2
然后,计算并输出表达式的值指定的运算符为加( +)
减( -)
乘( *)
除( /)
#include <stdio.h>
Int main()
{
float data1,data2; /*定义两个操作符 */
char op; /*定义运算符 */
printf("Please enter the expression:");
scanf("%f%c%f",&data1,&op,&data2); /*输入运算表达式 */
switch (op)
{
case '+',/*处理加法 */
printf("%.2f+ %.2f= %.2f\n",data1,data2,data1 + data2);
break;
case '-',/*处理减法 */
printf("%.2f- %.2f= %.2f\n",data1,data2,data1 - data2);
break;
case '*',/*处理乘法 */
printf("%.2f* %.2f= %.2f\n",data1,data2,data1 * data2);
break;
case '/',/*处理除法 */
if (0 == data2)
printf("Division by zero!\n");
else
printf("%.2f/%.2f= %.2f\n",data1,data2,data1/data2);
break;
default,
printf("Unknown operator! \n");
}
}
练习 2
作业 2
某航空公司规定:在旅游旺季 7~9月份,如果订票 20张及其以上,优惠票价的 10%; 20张以下,优惠 5%;在旅游淡季 1~6月份,10~12月份,订票 20张及其以上,优惠
20%,20张以下,优惠 10%。编写一个 C程序能够根据月份和旅客订票张数决定优惠率。
要求:分别用 if-else和 switch语句完成
答案相关语法小结
If-else语句格式和语义
Switch语句格式和语义
关系运算符和逻辑运算符的用法、含义、
优先级与结合性本讲要求
学会分析具有选择结构的问题,写出算法
能够编写选择结构的程序
会验证结果的正确性。
补充内容
1,程序测试
结构测试
功能测试
2,调试方法程序测试 ——结构测试法
经编译、连接的程序文件,生成可执行文件,就可以让计算机执行了。但是,并不是就可以得到预期的结果而交付用户使用了,因为程序仍然会存在某些错误。因此,
每一个人编写出一个程序后,在正式交付使用前,总要试通一下。“试通”就是试运行程序,也就是对程序进行测试。
程序测试程序测试的目的
Glenford J.Myers把它归结为如下三句话:
测试是程序的执行过程,目的在于发现错误;
一个好的测试实例在于能发现至今未发现的错误;
一个成功的测试是发现了至今未发现的错误的测试。
程序测试测试方法和技术
著名计算机软件科学家 E.W.Dijkstra曾断定:
“程序测试只能证明错误的存在,而不能证明错误不存在。” 这就要求注重测试方法和技术。不同的测试方法所对应的测试用例的设计方法不同。
在实践中,人们已经总结出一些测试方法,归结起来主要是 结构测试 和 功能测试 。这些方法将在后面的章节中陆续介绍。
测试一般按照先模块测试、再组装测试、最后整体测试的步骤进行。找出错误后,还要再一次进行程序的编辑 —编译 —连接 —测试 —调试。
程序测试结构测试法结构测试法也称白箱测试法,是基于程序内部语句的逻辑结构的测试。
下面介绍几种不同层次的结构测试方法。
1,语句覆盖法
2,分支覆盖
3,条件覆盖
4,组合条件覆盖程序测试语句覆盖法
语句覆盖是企图用足够多的测试用例,使程序中的每个语句都执行一遍,以尽可能多地发现程序中的错误。这里,言外之意是,即使每个语句都执行了一遍,也还不一定能够发现全部错误。
程序测试程序片断
{
…
if(a > 1 && b == 0) x = x / a;
if(a == 2 || x > 1) x = x + 1;
}
程序测试程序片段的逻辑结构
a > 1&&b == 0
a == 2 || x> 1
x=x / a
A
y
n
C B
x=x + 1
y
n
DE
程序测试分支覆盖
分支覆盖也称为判定覆盖,它要求通过足够的测试用例使每个判定的每个分支至少通过一次。如在例 2.1中,可以选用两组数据:
· a=3,b=0,x=2(测试路径 ACE);
· a=1,x=1,b为任意(测试路径为 ABD)。
使用上述测试用例,经过两次测试后,便可以使每个分支都通过一次。
分支覆盖准则比语句覆盖准则严密了一些,但仍然不够充分。因为一个判定中往往包含有多个条件,而用分支覆盖并不一定能将每个条件都试一次。
程序测试条件覆盖
条件覆盖是通过执行充足的测试用例,使每个判定中的每个条件都至少使用一次。
对例 2.1来说,有四个条件:
,条件 1,a>1,
,条件 2,b==0,
,条件 3,a==2,
,条件 4,x>1。
程序测试组合条件覆盖
条件覆盖似乎比判定覆盖更好,但实际上未必这样。因为它有时候可能使组合条件不满足,如上述按条件覆盖准则给出的测试用例:
(1) a=2,b=1,x=1;
(2) a=1,b=0,x=3。
既不能使第一个判定框为“真”,也不能使第二个判定框为“假”。也就是说,条件覆盖有可能连分支覆盖准则也满足不了。为此,人们又提出一种更强的准则 ──组合条件覆盖准则,执行充足的测试用例,使判定中条件的各种可能组合至少出现一次。
程序测试测试用例设计
本程序是一个单条件嵌套选择结构,其成功的测试是选择合适的数据让每一条路径都执行一遍。由于程序的输入要求三个数,如 3,4,5,因此每一次要输入三个不等的数进行测试。下面分析要测试过程:
( 1)为了覆盖分支 ABDH,就需要满足条件 a>b和 a>c,即要求第一个数要最大 (为 5),后两个数的顺序无关系;
( 2)为了覆盖分支 ABEH,就需要满足条件 a>b和 c>a,即要求按顺序 4,3,5输入三个数;
( 3)为了覆盖分支 ACFH,就需要满足条件 a<b和 b>c,即要求第二个数要最大 (为 5),另两个数的顺序无关系;
( 4)为了覆盖分支 ACGH,就需要满足条件 a<b和 b<c,即要求按顺序 3,4,5输入三个数。
程序测试程序测试 ——等价分类法
等价分类法是一种功能测试法。功能测试以程序的功能为测试依据,因此在设计测试用例之前要通过阅读程序代码和文档来了解程序的功能。使用这种方法不必关心程序的内部流程,故功能测试也称为黑箱测试法。黑箱测试用例应当通过对程序功能的分析,推演出由有代表性的元素组成的测试数据集。用等价分类法设计测试用例可分两步进行:
( 1)划分等价类;
( 2)选定测试用例。
程序测试划分等价类
划分等价类的基本方法是,从程序的功能说明中,找出各个输入条件,然后为每个输入条件划分等价类。
等价类可以分为两种:有效等价类和无效等价类。有效等价是指属于程序的合理输入范围的那些数据。无效等价类是指非法的输入数据。
程序测试选定测试用例
利用等价类来确定测试用例应按如下步骤进行:
1 给每个等价类规定一个编号
2 设计一个测试用例,使其尽可能多地覆盖未被覆盖的有效等价类。重复这一步直到所有的有效等价类都被覆盖为止。
3 为每个无效等价类设计一个测试用例。
程序测试测试用例设计
本题的 switch中分为三种情形:数字、空白和字母,从而可以将输入条件分为三个种等价类:
( 1)数字类:有效数字等价类和无效数字等价类
( 2)空白类:有效空白等价类和无效空白等价类
( 3)字母类:有效字母等价类和无效字母等价类程序测试补充 2,程序的调试
语法错误及其调试
语义及逻辑错误及其调试程序的调试程序的调试
测试就是发现程序中的错误的过程,而调试则是找出程序中的错误并改正的过程。
这里介绍程序的调试方法。
程序中的错误可以分为两类:语法错误和语义(逻辑)错误。
程序的调试
1 语法错误及其调试
语法错误的确定与基本调试原则
常见语法错误程序的调试编译器报告的错误位置
第 6行的声明语句的最后,缺少了一个分号,
可是编译器报告的却是 Error
C:\TURBOC2\EX108.C 7,Declaration
syntax error in function main
如下页程序的调试一个编译器的错误报告程序的调试针对某个错误,发出一系列连带的错误信息报告
如下页,本来是主函数的 12行缺少了一个后话括号。但是编译器却给出了两个错误报告:
Error C:\TURBOC2\EX112.C 14,
Expression syntax in function main
Error C:\TURBOC2\EX112.C 18,
Compound statement missing }in
function main
程序的调试编译器错误报告示例程序的调试常见语法错误
语句末尾漏写分号
使用未经定义的变量
括号不匹配
字符串没有结束符
赋值号左面不是变量
非 void类型的函数缺少 return语句
没有在 printf中指定输出项对应的输出格式
scanf函数中要么在格式串中使用了非格式字符程序的调试语句末尾漏写分号
int x
float y;
程序的调试使用未经定义的变量
int x;
float y;
double bigNum;
bignum = x + y;
程序的调试括号不匹配
if(x > 0 ) || (y < 0))
或
Int main(){
int x,y;
if(x >= 0){
y = x;
return y;
{
else {
y = - x;
return y;
} 程序的调试字符串没有结束符
printf(“This is a string.);
程序的调试赋值号左面不是变量
int x,y;
x + y = 356;
程序的调试非 void类型的函数缺少 return语句
func(int x,int y)
{
float z;
if(x == 0)
printf(“division by zero!”);
else
return (z = y / x);
}
程序的调试没有在 printf中指定输出项对应的输出格式
printf(“x + y = %d”,x,y,x + y);
程序的调试
scanf函数中要么在格式串中使用了非格式字符
scanf(“input x,y”,x,y);
程序的调试
2 语义及逻辑错误及其调试
语义错误和逻辑错误
常见的语义错误
语义错误的发现和调试程序的调试语义错误和逻辑错误
语义错误,有人也称为逻辑错误。但是有时它们是有区别的。这里,这样界定它们:
语义错误指程序员对程序中的语义的理解与编译器理解的不同。
逻辑错误指程序在算法上出现错误。
程序的调试常见的语义错误
赋值号与等号用错
无限循环
if,while或 for结构后面使用多余的分号
错误地使用了关系运算符
数据的值超出了表示范围
运算符优先顺序错
else搭配不当
off-by-one(偏一)错误
将无关代码放到循环体中
使用整数除法程序的调试赋值号与等号用错
func(int x,int y)
{
float z;
If (x = 0) /* A处 */
return (0);
else
return (z == y * x); /* B处 */
}
程序的调试无限循环
float x;
printf(“input an digiter:”);
scanf(“%f”,&x);
while(--x) /* 无限循环 */
printf(“%f”,x);
程序的调试
if,while或 for结构后面使用多余的分号
for(i = 1; i<= 10; i ++);/* 多余分号 */
sum += i;
程序的调试错误地使用了关系运算符
int k = 10;
for (1 < k-- < 8) /* 关系运算错
*/
printf(“true”);
printf(“false”);
程序的调试数据的值超出了表示范围
float x = 1,y = 1000.0;
int i;
for( i = 1;n i < = 10; i ++)
x * = y;
printf(“%d”,x); /* 打印出的数据会出错
*/
程序的调试运算符优先顺序错
写成
printf(“%f”,x + 3 / y - 5);
x + 3
y - 5
程序的调试
else搭配不当
if(x > y)
if(y > z)
max = x;
else
if(y > z)
max = y;
else
max = z;
程序的调试
off-by-one(偏一)错误
通常指 for循环的次数多或少一次。如一个求 10内的自然数之和的程序段写成:
for(i = 1; i< 10; i ++); /* 多余分号 */
sum += i;
程序的调试将无关代码放到循环体中
一个求 100内的自然数之和,并最后打印结果的程序段写成:
for(i = 1; i<= 100; i ++); /* 多余分号 */
sum += i,printf(“\n%d”,sun);
程序的调试使用整数除法
int a = 2,b =10000;
printf(“\n%d”,a / 3 * b); /* 结果为 0 */
程序的调试语义错误的发现和调试
一般说来,进行语义调试的两大方法是:
手工跟踪法工具法
(这里主要介绍手工跟踪法 )
程序的调试手工跟踪法
在程序的一些关键部位插入一些输出语句,输出某些变量的中间值,来跟踪程序的执行过程。所谓关键部位有:
在 if-else语句的前后显示关键变量的值。
循环开始的前后和循环结构的前后显示:
关键变量的值;
循环变量的值。
每次进入函数前后所有参数的变化,每次退出函数前后所有参数的变化。
其他关键部位,以后会陆续介绍。
程序的调试
学习方法:
课前 按照 授课计划 上指明的进度 预习
上课认真听讲,课件可下载作为笔记
课下 参照教材 理解 课件上所讲的内容和相关知识点
完成作业 &上机调试例 1:比较两个数
从键盘输入两个数,输出其中最大的一个数。
问题分析开 始输入两整数 a,b
a>b
输出 a 输出 b
结 束如果 a>b,
a是最大值,
否则,
b是最大值。
程序 1
/*比较两个数 */
#include <stdio.h>
int main()
{
int a,b;
printf("Please input two integers:\n");
scanf("%d %d",&a,&b);
if(a>b) /*比较后直接输出比较结果 */
printf(" max=%d ",a);
else
printf(" max=%d ",b);
return (0);
}
涉及的语法
选择结构 语句格式
if(条件表达式 )
{
… …
}
else
{
… …
}
else分支可以省略,此时为单分支结构涉及的语法
- 关系运算符
> < >= <= == !=
大于,小于,大于等于,小于等于,等于,
不等于
运算结果只有两个值,0和 非 0,0表示假,
即该关系不成立;非 0,表示真,即该关系成立
例如,2006%4==0的结果是?
0,表示假(关系不成立)
优先级:在算术运算符和赋值运算符之间。
结合性:自左向右程序 2
/*比较两个数 */
#include <stdio.h>
int main()
{
int a,b,max;/*max用来存储最大值 */
printf("Please input two integers:\n");
scanf("%d %d",&a,&b);
if(a>b)
max=a;
else
max=b;
printf(" max=%d ",max);
return (0);
}
开 始输入两整数 a,b
a>b
max=a max=b
输出 max
结 束请根据下列算法写程序。
例 2:判断闰年
从键盘上输入一个年份,然后判断这个年份是否为闰年。
分析结果(算法)
输入 year
判断 year是否为闰年
输出 yes或 no 如果 year能被 4整除且不能被 100
整除,或者 year能被 400整除,
year是闰年,
否则
year不是闰年。
分析
year能被 4整除 且 不能被 100整除,或者 year能被
400整除
(?)或者 (?)
(?且?)或者 (?)
(?&&?)||(?)
(year%4==0&&year%100!=0)||(year%400==0)
如果 year能被 4整除且不能被 100整除,或者 year能被 400整除,
year是闰年,
否则
year不是闰年。
if(year%4==0&&year%100!=0)||(year%400==0)
{
printf(“%d is a leap year!”,year);
}
else
{
printf(“%d is not a leap year!”,year);
}
结果
#include <stdio.h>
void main()/*判断闰年 */
{ int year;
printf(“please input year:”);
scanf(“%d”,&year);
if((year%4==0&&year%100!=0)||(year%400==0))
{
printf(“%d is a leap year!”,year);/*一般要缩进 */
}
else
{
printf(“%d is not a leap year!”,year); /*缩进与上边一致 */
}
}
输入 1998,输出结果是什么?
1998%4==0不成立,然后
1998%400==0不成立,所以,
整个条件不成立,不是闰年,
注意,1998%100==0并没有判断,这里采用了 懒惰求值法,
因为无论它是何值,第一个条件都不可能成立了。
输入 2004,会怎样?
2000%4==0成立,
2000%100!=0成立,所以左边的第一个条件成立,
2004%400==0不用再判断了,
同样是使用 懒惰求值法 。
输入 2100,又会怎样?
不是闰年简单的程序测试
一次输入,得到正确的输出结果并不能说明问题,
要精心选择不同的输入,反复进行测试,才能逐渐发现程序里的问题
程序测试分为结构测试和功能测试两类
结构测试又为分:
语句覆盖法
分支覆盖法
条件覆盖法
组合条件覆盖法
对于前一个例题,最好根据条件中的三个表达式的组合,即选择 2*2*2=8个输入进行程序的测试。
涉及的语法
-逻辑运算符
逻辑运算也被称为布尔( Boolean) 运算,即参与运算的 数据 和运算 结果 都只有两个值,0和 非 0,
0表示假 (条件不成立 );非 0表示真 (条件成立 )
与运算 &&(相当于而且 )
a && b当且仅当两者都为真,则结果为真
( a > b && b > c); a大于 b,并且 b大于 c
或运算 || (相当于或者 )
a || b只要两者中有一个为真,结果就为真
( a > b || b > c); a大于 b,或者 b大于 c
求反 !
单目运算符
( !a ); 如果 a是 0,结果非 0;如果 a是非 0,结果是 0
并不改变 a的值优先级,!优先级同其它单目运算符,&&和 ||比关系运算符低,&&又比 ||高。
结合性:自左向右
c是字母吗?
输出,是,
NO YES
输入 c
输出,不是,
结束开始练习 1:从键盘上输入一个字符,判断它是否是小写字母?
#include <stdio.h>
void main()
{ char c;
printf(“please input a character:”);
c=getchar();/*从键盘上输入一个字符并存储到变量 C中 */
if( (c>=?a?&&c<=?z?) )
{
printf(“%c is a letter!”,c);
}
else
{
printf(“%c is not a letter!”,c);
}
}
(letter>=97&&letter<=122)
a? <=c<=?z?对吗?
请上机验证,作为作业如何判断是否是英文字母?
(c>=?a?&&c<=?z?)||
(c>=?A?&&c<=?Z?)
作业 1
求三个数的最大值。
验证题:练习 1中的表达式写成?a? <=c<=?z?,
并分别选取三种数据:小于‘ a?的,小字字母,
大于‘ z?的字符输入,验证这种写法是否正确?
再写成 c>=?a?&&c<=?z?验证一下。
选做:求一元二次方程 ax2+bx+c=0的根,
整数系数从键盘输入。 (应该根据输入的系统判断是否为一元二次方程,是否有根等情况 )
作业 1讲评 求三个数的最大值
#include<stdio.h>
void main()/*方法 1*/
{
int a,b,c,max;
printf("Please input 3 integers:");
scanf("%d %d %d",&a,&b,&c);
if (a>b)/*求出 a,b的最大值,放入 max*/
max=a;
else
max=b;
if (max>c) /*求出 max,c的最大值并输出 */
printf("The Max is,%d\n",max);
else
printf("The Max is,%d\n",c);
getchar();
}
作业 1讲评 -写法 2
#include<stdio.h>
void main()/*方法 1*/
{
int a,b,c,max;
printf("Please input 3 integers:");
scanf("%d %d %d",&a,&b,&c);
(a>b)? max= a:max=b;
/*求出 a,b的最大值,放入 max。 或者用 max=(a>b)?a:b;*/
if (max>c) /*求出 max,c的最大值并输出 */
printf("The Max is,%d\n",max);
else
printf("The Max is,%d\n",c);
getchar();
}
唯一的三目运算符
代替简单的 if-else
优先级:刚好高于赋值运算符
结合性:自右向左作业 1讲评 -很多学生的写法
#include<stdio.h>
void main()/*方法 2*/
{
int a,b,c;
printf("Please input 3 integers:");
scanf("%d %d %d",&a,&b,&c);
if (a>b&&a>c)
printf("The Max is,%d\n",a);
else if (b>a&&b>c)
printf("The Max is,%d\n",b);
else
printf("The Max is,%d\n",c);
getchar();
}
如果分支中只有一条语句,{}可以省略,一般将复杂的分支放入 else里。
作业 1讲评 -验证题
#include <stdio.h>
void main()
{ char c;
printf(“please input a character:”);
c=getchar();
if(?a? <=c<=?z? )
{
printf(“%c is a letter!”,c);
}
else
{
printf(“%c is not a letter!”,c);
}
}
无论输入什么,输出都是,is a letter”
原因是:两个 <=的优先级相等,而它们的结合性又是自左向右,因此先算左边,无论输入什么字符,左边的判断结果不是 0(不成立 ),就是 1(成立 ),
总之,总是小于‘ z?的 ASCII码 122,
所以第二次判断的结果永远是成立,
因此执行 if的分支。
选做题算法输入 a,b,c
输出计算结束怎么求?
a=0?NO
非二次方程
YES
△ <0?
求解并输出
NO
无解
YES
#include <math.h>
#include <stdio.h>
main()
{
float a,b,c,delta,x1,x2,p,q;
printf("Please enter the coefficients a,b,c:");
scanf(“%f,%f,%f”,&a,&b,&c); /*输入三个系数 */
if(a==0)
{ printf(,not a quadratic!”);}
else
{ delta = b * b - 4 * a * c; /*计算判别式,应在判断之前求解 */
if(delta<0)
{ printf(“No root!”); } /*输出,无解” */
else
{ p = - b / (2 * a); q = sqrt(delta) / (2 * a);
x1 = p + q; x2 = p - q; /*求实根 x1,x2 */
printf(“x1=%7.4f,x2=%7.4f\n”,x1,x2); /*输出 x1和 x2*/
}
}
}
选做题答案 忠告:为避免出错,尽量将每个分支中的 语句用 {}括起来。
若不括会怎样呢?
答,不括的话,
只有紧挨在后边的一句才是 else的分支,其它语句不再属于 else分支例 3:计算分段函数的值
ex (x > 0)
计算分段函数 y= 0 (x = 0) 的值,x的值由键盘输入。
e-x (x < 0)
算法 1:
1,输入 x
2,若 x<0,则 y= e-x
3,若 x=0,则 y= 0
4,若 x>0,则 y= ex
5,输出 y
/*程序 1*/
#include<stdio.h>
#include<math.h>
void main()
{
float x,y;
scanf(“%f”,&x);
if(x<0) y=exp(-x);
if(x==0) y=0;
if(x>0) y=exp(x);
printf(“y=f(%f)=%f\n”,x,y);
}
当每个分支彼此能够分得很楚,可以用 多个单分支 解决问题。
算法 2
输入 x
输出 y
计算结束怎么求?
X>0?NO
y=ex
YES
X<0?
y=0
NO
y=e-x
YES
程序 2
#include<stdio.h>
#include<math.h>
void main() /*程序 2*/
{
float x,y;
scanf(“%f”,&x);
if(x>0)
{
y=exp(x);
}
else
{
if(x<0)
{
y= exp(e,-x);
}
else
{
y=0;
}
}
printf(“y=f(%f)=%f\n”,x,y);
}
嵌套,一层套一套
此时要注意 if-else的配对问题。
一般加上 { }会明确配对,对避免逻辑错误非常有好处
要注意内层的条件中还有隐含条件例 4
根据输入的百分制成绩 score,分段转换成相应的等级并打印输出。
分析:
输入 score
转换并输出 (五个分支 )
#include <stdio.h>
void main()
{ int score;
printf("Please enter score:");
scanf("%d",&score);
if (score < 0 || score > 100) /*对输入数据的合法性进行检查 */
printf("Input error!\n");
else if (score >= 90)
printf("%d--excellent\n",score);
else if (score >= 80)
printf("%d--good\n",score);
else if (score >= 70)
printf("%d—fit exactly\n",score);
else if (score >= 60)
printf("%d--pass\n",score);
else
printf("%d--fail\n",score);
}
方法一程序
在 else分支嵌套
没有 else会怎样?
答:例如,输入 100
分,会出现输出几个对应等级的情况。因为加上 else会过滤掉一些条件。
方法二输出 A
Score/10有 11种情况
10
输出 A9
输出 E0
…
switch(score/10)
{case 10:
case 9,printf(“%d----excellent\n”,score);break;
case 8,printf(“%d----good\n”,score);break;
case 7,printf(“%d----fit exactly\n”,score);break;
case 6,printf(“%d----pass\n”,score);break;
case 5:
case 4:
case 3:
case 2,
case 1:
case 0,printf(“%d----fail\n”,score);break;
}
default,printf(“%d----fail\n”,score);break;
用于判断分支的表达式
表达式的所有可能结果列举在 case后
当上面的情况与下边的情况相同,后边的分支语句可省略
default表示除上述情况以外的所有情况。
当遇到 break时,程序转到 switch语句的的 }后边
#include <stdio.h>
void main()
{ int score;
printf("Please enter score:");
scanf("%d",&score);
if(score<0||score>100)
printf(“Input error!\n”);
else
{ switch(score/10)
{ case 10:
case 9,printf(“%d----excellent\n”,score);break;
case 8,printf(“%d----good\n”,score);break;
case 7,printf(“%d----fit exactly\n”,score);break;
case 6,printf(“%d----pass\n”,score);break;
case 5:
case 4:
case 3:
case 2,
case 1:
case 0,printf(“%d----fail\n”,score);break;
}
}
}
方法二程序
default,printf(“%d----fail\n”,score);break;
执行过程??
涉及到的语法知识
-switch语句
一般用于菜单等多分支的情况
switch (表达式 )
{
case 常数 1:语句序列 1;
case 常数 2:语句序列 2;
…………
default:语句序列 n;
}
一般结果为多个离散值的情况更详细的语法知识请查阅 P96-97。
读程序
main()
{ int a=1,b=0;
switch(a)
{case 1:switch(b)
{case 0:putchar(?0?); break;
case 1,putchar(?1?); break;
} break;
case 2:printf(“a=2”);break;
}
} !!!读程序时一定注意有没有 break,并正确分析 break后转到了哪儿?
问题
if-else嵌套和 switch都可以解决多分支的情况,在遇到多分支的时候使用哪个呢?
当多个可能的情况是离散值时可以选用
switch,其他情况下用 if-else嵌套。
练习 2,计算器程序
编程设计一个简单的计算器程序,要求根据用户从键盘输入如下形式的表达式:
操作数 1 运算符 op 操作数 2
然后,计算并输出表达式的值指定的运算符为加( +)
减( -)
乘( *)
除( /)
#include <stdio.h>
Int main()
{
float data1,data2; /*定义两个操作符 */
char op; /*定义运算符 */
printf("Please enter the expression:");
scanf("%f%c%f",&data1,&op,&data2); /*输入运算表达式 */
switch (op)
{
case '+',/*处理加法 */
printf("%.2f+ %.2f= %.2f\n",data1,data2,data1 + data2);
break;
case '-',/*处理减法 */
printf("%.2f- %.2f= %.2f\n",data1,data2,data1 - data2);
break;
case '*',/*处理乘法 */
printf("%.2f* %.2f= %.2f\n",data1,data2,data1 * data2);
break;
case '/',/*处理除法 */
if (0 == data2)
printf("Division by zero!\n");
else
printf("%.2f/%.2f= %.2f\n",data1,data2,data1/data2);
break;
default,
printf("Unknown operator! \n");
}
}
练习 2
作业 2
某航空公司规定:在旅游旺季 7~9月份,如果订票 20张及其以上,优惠票价的 10%; 20张以下,优惠 5%;在旅游淡季 1~6月份,10~12月份,订票 20张及其以上,优惠
20%,20张以下,优惠 10%。编写一个 C程序能够根据月份和旅客订票张数决定优惠率。
要求:分别用 if-else和 switch语句完成
答案相关语法小结
If-else语句格式和语义
Switch语句格式和语义
关系运算符和逻辑运算符的用法、含义、
优先级与结合性本讲要求
学会分析具有选择结构的问题,写出算法
能够编写选择结构的程序
会验证结果的正确性。
补充内容
1,程序测试
结构测试
功能测试
2,调试方法程序测试 ——结构测试法
经编译、连接的程序文件,生成可执行文件,就可以让计算机执行了。但是,并不是就可以得到预期的结果而交付用户使用了,因为程序仍然会存在某些错误。因此,
每一个人编写出一个程序后,在正式交付使用前,总要试通一下。“试通”就是试运行程序,也就是对程序进行测试。
程序测试程序测试的目的
Glenford J.Myers把它归结为如下三句话:
测试是程序的执行过程,目的在于发现错误;
一个好的测试实例在于能发现至今未发现的错误;
一个成功的测试是发现了至今未发现的错误的测试。
程序测试测试方法和技术
著名计算机软件科学家 E.W.Dijkstra曾断定:
“程序测试只能证明错误的存在,而不能证明错误不存在。” 这就要求注重测试方法和技术。不同的测试方法所对应的测试用例的设计方法不同。
在实践中,人们已经总结出一些测试方法,归结起来主要是 结构测试 和 功能测试 。这些方法将在后面的章节中陆续介绍。
测试一般按照先模块测试、再组装测试、最后整体测试的步骤进行。找出错误后,还要再一次进行程序的编辑 —编译 —连接 —测试 —调试。
程序测试结构测试法结构测试法也称白箱测试法,是基于程序内部语句的逻辑结构的测试。
下面介绍几种不同层次的结构测试方法。
1,语句覆盖法
2,分支覆盖
3,条件覆盖
4,组合条件覆盖程序测试语句覆盖法
语句覆盖是企图用足够多的测试用例,使程序中的每个语句都执行一遍,以尽可能多地发现程序中的错误。这里,言外之意是,即使每个语句都执行了一遍,也还不一定能够发现全部错误。
程序测试程序片断
{
…
if(a > 1 && b == 0) x = x / a;
if(a == 2 || x > 1) x = x + 1;
}
程序测试程序片段的逻辑结构
a > 1&&b == 0
a == 2 || x> 1
x=x / a
A
y
n
C B
x=x + 1
y
n
DE
程序测试分支覆盖
分支覆盖也称为判定覆盖,它要求通过足够的测试用例使每个判定的每个分支至少通过一次。如在例 2.1中,可以选用两组数据:
· a=3,b=0,x=2(测试路径 ACE);
· a=1,x=1,b为任意(测试路径为 ABD)。
使用上述测试用例,经过两次测试后,便可以使每个分支都通过一次。
分支覆盖准则比语句覆盖准则严密了一些,但仍然不够充分。因为一个判定中往往包含有多个条件,而用分支覆盖并不一定能将每个条件都试一次。
程序测试条件覆盖
条件覆盖是通过执行充足的测试用例,使每个判定中的每个条件都至少使用一次。
对例 2.1来说,有四个条件:
,条件 1,a>1,
,条件 2,b==0,
,条件 3,a==2,
,条件 4,x>1。
程序测试组合条件覆盖
条件覆盖似乎比判定覆盖更好,但实际上未必这样。因为它有时候可能使组合条件不满足,如上述按条件覆盖准则给出的测试用例:
(1) a=2,b=1,x=1;
(2) a=1,b=0,x=3。
既不能使第一个判定框为“真”,也不能使第二个判定框为“假”。也就是说,条件覆盖有可能连分支覆盖准则也满足不了。为此,人们又提出一种更强的准则 ──组合条件覆盖准则,执行充足的测试用例,使判定中条件的各种可能组合至少出现一次。
程序测试测试用例设计
本程序是一个单条件嵌套选择结构,其成功的测试是选择合适的数据让每一条路径都执行一遍。由于程序的输入要求三个数,如 3,4,5,因此每一次要输入三个不等的数进行测试。下面分析要测试过程:
( 1)为了覆盖分支 ABDH,就需要满足条件 a>b和 a>c,即要求第一个数要最大 (为 5),后两个数的顺序无关系;
( 2)为了覆盖分支 ABEH,就需要满足条件 a>b和 c>a,即要求按顺序 4,3,5输入三个数;
( 3)为了覆盖分支 ACFH,就需要满足条件 a<b和 b>c,即要求第二个数要最大 (为 5),另两个数的顺序无关系;
( 4)为了覆盖分支 ACGH,就需要满足条件 a<b和 b<c,即要求按顺序 3,4,5输入三个数。
程序测试程序测试 ——等价分类法
等价分类法是一种功能测试法。功能测试以程序的功能为测试依据,因此在设计测试用例之前要通过阅读程序代码和文档来了解程序的功能。使用这种方法不必关心程序的内部流程,故功能测试也称为黑箱测试法。黑箱测试用例应当通过对程序功能的分析,推演出由有代表性的元素组成的测试数据集。用等价分类法设计测试用例可分两步进行:
( 1)划分等价类;
( 2)选定测试用例。
程序测试划分等价类
划分等价类的基本方法是,从程序的功能说明中,找出各个输入条件,然后为每个输入条件划分等价类。
等价类可以分为两种:有效等价类和无效等价类。有效等价是指属于程序的合理输入范围的那些数据。无效等价类是指非法的输入数据。
程序测试选定测试用例
利用等价类来确定测试用例应按如下步骤进行:
1 给每个等价类规定一个编号
2 设计一个测试用例,使其尽可能多地覆盖未被覆盖的有效等价类。重复这一步直到所有的有效等价类都被覆盖为止。
3 为每个无效等价类设计一个测试用例。
程序测试测试用例设计
本题的 switch中分为三种情形:数字、空白和字母,从而可以将输入条件分为三个种等价类:
( 1)数字类:有效数字等价类和无效数字等价类
( 2)空白类:有效空白等价类和无效空白等价类
( 3)字母类:有效字母等价类和无效字母等价类程序测试补充 2,程序的调试
语法错误及其调试
语义及逻辑错误及其调试程序的调试程序的调试
测试就是发现程序中的错误的过程,而调试则是找出程序中的错误并改正的过程。
这里介绍程序的调试方法。
程序中的错误可以分为两类:语法错误和语义(逻辑)错误。
程序的调试
1 语法错误及其调试
语法错误的确定与基本调试原则
常见语法错误程序的调试编译器报告的错误位置
第 6行的声明语句的最后,缺少了一个分号,
可是编译器报告的却是 Error
C:\TURBOC2\EX108.C 7,Declaration
syntax error in function main
如下页程序的调试一个编译器的错误报告程序的调试针对某个错误,发出一系列连带的错误信息报告
如下页,本来是主函数的 12行缺少了一个后话括号。但是编译器却给出了两个错误报告:
Error C:\TURBOC2\EX112.C 14,
Expression syntax in function main
Error C:\TURBOC2\EX112.C 18,
Compound statement missing }in
function main
程序的调试编译器错误报告示例程序的调试常见语法错误
语句末尾漏写分号
使用未经定义的变量
括号不匹配
字符串没有结束符
赋值号左面不是变量
非 void类型的函数缺少 return语句
没有在 printf中指定输出项对应的输出格式
scanf函数中要么在格式串中使用了非格式字符程序的调试语句末尾漏写分号
int x
float y;
程序的调试使用未经定义的变量
int x;
float y;
double bigNum;
bignum = x + y;
程序的调试括号不匹配
if(x > 0 ) || (y < 0))
或
Int main(){
int x,y;
if(x >= 0){
y = x;
return y;
{
else {
y = - x;
return y;
} 程序的调试字符串没有结束符
printf(“This is a string.);
程序的调试赋值号左面不是变量
int x,y;
x + y = 356;
程序的调试非 void类型的函数缺少 return语句
func(int x,int y)
{
float z;
if(x == 0)
printf(“division by zero!”);
else
return (z = y / x);
}
程序的调试没有在 printf中指定输出项对应的输出格式
printf(“x + y = %d”,x,y,x + y);
程序的调试
scanf函数中要么在格式串中使用了非格式字符
scanf(“input x,y”,x,y);
程序的调试
2 语义及逻辑错误及其调试
语义错误和逻辑错误
常见的语义错误
语义错误的发现和调试程序的调试语义错误和逻辑错误
语义错误,有人也称为逻辑错误。但是有时它们是有区别的。这里,这样界定它们:
语义错误指程序员对程序中的语义的理解与编译器理解的不同。
逻辑错误指程序在算法上出现错误。
程序的调试常见的语义错误
赋值号与等号用错
无限循环
if,while或 for结构后面使用多余的分号
错误地使用了关系运算符
数据的值超出了表示范围
运算符优先顺序错
else搭配不当
off-by-one(偏一)错误
将无关代码放到循环体中
使用整数除法程序的调试赋值号与等号用错
func(int x,int y)
{
float z;
If (x = 0) /* A处 */
return (0);
else
return (z == y * x); /* B处 */
}
程序的调试无限循环
float x;
printf(“input an digiter:”);
scanf(“%f”,&x);
while(--x) /* 无限循环 */
printf(“%f”,x);
程序的调试
if,while或 for结构后面使用多余的分号
for(i = 1; i<= 10; i ++);/* 多余分号 */
sum += i;
程序的调试错误地使用了关系运算符
int k = 10;
for (1 < k-- < 8) /* 关系运算错
*/
printf(“true”);
printf(“false”);
程序的调试数据的值超出了表示范围
float x = 1,y = 1000.0;
int i;
for( i = 1;n i < = 10; i ++)
x * = y;
printf(“%d”,x); /* 打印出的数据会出错
*/
程序的调试运算符优先顺序错
写成
printf(“%f”,x + 3 / y - 5);
x + 3
y - 5
程序的调试
else搭配不当
if(x > y)
if(y > z)
max = x;
else
if(y > z)
max = y;
else
max = z;
程序的调试
off-by-one(偏一)错误
通常指 for循环的次数多或少一次。如一个求 10内的自然数之和的程序段写成:
for(i = 1; i< 10; i ++); /* 多余分号 */
sum += i;
程序的调试将无关代码放到循环体中
一个求 100内的自然数之和,并最后打印结果的程序段写成:
for(i = 1; i<= 100; i ++); /* 多余分号 */
sum += i,printf(“\n%d”,sun);
程序的调试使用整数除法
int a = 2,b =10000;
printf(“\n%d”,a / 3 * b); /* 结果为 0 */
程序的调试语义错误的发现和调试
一般说来,进行语义调试的两大方法是:
手工跟踪法工具法
(这里主要介绍手工跟踪法 )
程序的调试手工跟踪法
在程序的一些关键部位插入一些输出语句,输出某些变量的中间值,来跟踪程序的执行过程。所谓关键部位有:
在 if-else语句的前后显示关键变量的值。
循环开始的前后和循环结构的前后显示:
关键变量的值;
循环变量的值。
每次进入函数前后所有参数的变化,每次退出函数前后所有参数的变化。
其他关键部位,以后会陆续介绍。
程序的调试