程序调试与常见程序错误汇总.docx
《程序调试与常见程序错误汇总.docx》由会员分享,可在线阅读,更多相关《程序调试与常见程序错误汇总.docx(27页珍藏版)》请在冰点文库上搜索。
程序调试与常见程序错误汇总
程序调试与常见程序错误
一、在Codeblocks中调试程序
1.注意事项
不允许工程路径中含有空格、汉字。
2.在相应行号后面点击鼠标左键设置断点
3.打开WATCHS窗口
4.点击调试按钮
5.可以再watchs窗口看到自动变量。
黄色三角表示程序暂停的位置
6.通过单步运行按钮进行单步运行。
7.也可以在debuger标签,command栏输入调试命令进行单步运行、打印变量值等操作。
8.程序运行
二、存储路径设置问题
1.如果不是默认安装路径,codeblocks就无法找到编译器和调试器,就会出现此类问题。
解决办法:
Settings——Compileranddebugger——Toolchainexecutables——Auto-detect。
2.输出信息为“某个命令执行失败或异常终止”。
解决方法:
通常是相应的编译器找不到,试着将其所在路径放到path环境变量中去。
3.输出编译错误,如某某文件找不到之类。
解决方法:
在项目中设置选项中加上路径(通常可利用customervariable项)。
4.在调试程序的时候弹出类似""XYZ-Debug":
Thecompiler'ssetup(GNUGCCCompiler)isinvalid,soCode:
:
Blockscannotfind/runthecompiler.
Probablythetoolchainpathwithinthecompileroptionsisnotsetupcorrectly?
!
Goto"Settings->Compileranddebugger...->Globalcompilersettings->GNUGCCCompiler->Toolchainexecutables"andfixthecompiler'ssetup.
Skipping...
Nothingtobedone(allitemsareup-to-date)."这种警告。
解决方法:
这个错误提示已经说得很清楚了,找不到编译器,到菜单Settings->Compileranddebugger...->Globalcompilersettings->GNUGCCCompiler->Toolchainexecutables下去修复编译选项前提是你已经装了gcc
如过没装,就去下个包含mingw的codeblocks,(mingw包含gcc编译器);
三、修改Codeblocks的设置
1.
2.
3.
4.
四、提示程序无法调试问题
多次调试一个程序时出现"cannotopenoutputfilmD:
\c++\文件名.exePermissiondenied"
解决方法:
启动任务管理器,找到文件.exe关闭就可以了。
五、常见错误分析
1.忘记定义变量。
例如:
main( )
{x=3;
y=6;
printf("%d\n ",x+y);
}
C要求对程序中用到的每一个变量都必须定义其类型,上面程序中没有对x、y进行定义。
应在函数体的开头加int x,y;这是学过BASIC和FORTRAN语言的读者写C程序时常见的一个错误。
在BASIC语言中,可以不必先定义变量类型就可直接使用。
在FORTRAN中,未经定义类型的变量按隐含的I-N规则决定其类型,而C语言则要求对用到的每一个变量都要在本函数中定义(除非已定义为外部变量)。
2.输入输出的数据的类型与所用格式说明符不一样。
例如:
若a已定义为整型,b已定义为实型。
a=3;b=4.5;
printf("%f %d\n",a,b);
编译时不给出出错信息,但运行结果将与原意不符,输出为
0.000000 16402
它们并不是按照赋值的规则进行转换(如把4.5转换成4),而是将数据在存储单元中的形式按格式符的要求组织输出(如b占4个字节,只把最后两个字节中的数据按%d,作为整数输出)。
3.未注意int型数据的数值范围。
例如:
一般微型计算机上使用的C编译系统,对一个整型数据分配两个字节。
因此一个整数的范围为
-215~215-1,即-32768~32767。
常见这样的程序段:
intnum;
num=89101;
printf("%d",num);
得到的却是23565,原因是89101已超过32767。
两个字节容纳不下89101,则将高位截去。
即将超过低16位的数截去。
即将89101减去216(即16位二进制所形成的模)。
89101-65536=23565。
有时还会出现负数。
例如
num=196607;
输出得-1。
因为196607的二进制形式为
00000000000000101111111111111111
去掉高位10,低16位的值是-1(-1的补码是:
1111111111111111)。
对于超过整个范围的数,要用long型,即改为
longint num;
num=89101;
printf("%ld",num);
请注意,如果只定义num为long型,而在输出时仍用“%d”说明符,仍会出现以上错误。
4.在输入语句scanf中忘记使用变量的地址符。
例如:
scanf("%d%d",a,b);
这是许多初学者刚学习C语言时一个常见的疏忽,或者说是习惯性的错误,因为在其他语言中在输入时只需写出变量名即可,而C语言要求指明“向哪个地址标识的单元送值”。
应写成scanf("%d%d",&a,&b);
5.输入时数据的组织与要求不符。
例如:
用scanf函数输入数据,应注意如何组织输入
数据。
假如有以下scanf函数:
scanf("%d%d",&a,&b);
有人按下面的方法输入数据:
3,4
这是错的。
数据间应该用空格来分隔。
读者可以用
printf("%d%d",a,b);
来验证一下。
应该用以下方法输入:
34
如果scanf函数为
scanf("%d,%d",&a,&b);
对scanf函数中格式字符串中除了格式说明符外,
对其他字符必须按原样输入。
因此,应按以下方法输入:
3,4
此时如果用“34”反而错了。
还应注意,不能企图用
scanf("inputa&b:
%d,%d",&a,&b);
想在屏幕上显示一行信息:
inputa&b:
然后在其后输入a和b的值,这是不行的。
这是由于有的读者以为scanf具有BASIC语言中的INPUT语句的功能(先输出一个字符串,再输入变量的值)。
如果想在屏幕上得到所需的提示信息,可以另加一个printf函数语句:
printf("inputa&b:
");
scanf("%d,%d",&a,&b);
6.误把“=”作为“等于”运算符。
例如:
在许多高级语言中,用“=”符号作为关系运算符“等于”。
例如,在BASIC或PASCAL程序中都可以写
if(a=b) then…
但在C语言中,“=”是赋值运算符,“==”才是关系运算符“等于”。
如果写成
if(a=b) printf("aequaltob");
C编译系统将(a=b)作为赋值表达式处理,将b的值赋给a,然后判断a的值是否零,若为非零,则作为“真”;若为零作为假。
如果a的值为3,b的值为4,
a≠b,按原意不应输出“aequaltob”。
而现在先将b的值赋给a,a也为4,赋值表达式的值为4。
if语句中的表达式值为真(非零),因此输出“aequaltob”。
这种错误在编译时是检查不出来的,但运行结果往往是错的。
而且由于习惯的影响,程序设计者自己往往也不易发觉。
7.语句后面漏分号。
例如:
C语言规定语句末尾必须有分号。
分号是C语句不可缺少的一部分。
这也是和其他语言不同的。
有的初学者往往忘记写这一分号。
如:
a=3
b=4
编译时,编译程序在“a=3”后面未发现分号,就把下一行“b=4”也作为上一行的语句的一部分,这就出现语法错误。
有时编译时指出某行有错,但在该行上并未发现错误,应该检查上一行是否漏了分号。
如果用复合语句,有的学过PASCAL语言的读者往往漏写最后一个语句的分号,如:
{t=a;
a=b;
b=t
}
在PASCAL中分号是两个语句间的分隔符而不是语句的一部分,而在C中,没有分号的就不是语句。
8.在不该加分号的地方加了分号。
例如:
if(a>b);
printf("a is largerthanb\n");
本意为当a>b时输出“a is largerthanb”的信息。
但由于在if(a>b)后加了分号,因此if语句到此结束。
即当(a>b)为真时,执行一个空语句。
本来想a≤b时不输出上述信息,但现在printf函数语句并不从属于if语句,而是与if语句平行的语句。
不论
a>b还是a≤b,都输出“aislargerthanb”。
又如:
for(i=0;i<10;i++);
{scanf("%d",&x);
printf("%d\n",x*x);
}
本意为先后输入10个数,每输入一个数后输出它的平方值。
由于在for( )后加了一个分号,使循环体变成了空语句。
只能输入一个整数并输出它的平方值。
总之,在if、for、while语句中,不要画蛇添足多加分号。
9.对应该有花括号的复合语句,忘记加花括号。
例如:
sum=0;
i=1;
while(i<=100)
sum=sum+i;
i++;
本意是实现1+2+…+100,即∑i。
但上面的语句只是重复了sum+1的操作,而且循环永不终止。
因为i的值始终没有改变。
错误在于没有写成复合语句形式。
因此while语句的范围到其后第一个分号为止。
语句“i++;”不属于循环体范围之内。
应改
为
while(i<=100)
{sum=sum+i;
i++;
}
10.括号不配对。
例如:
当一个语句中使用多层括弧时常出现这类错误,纯属粗心所致。
如:
while((c=getchar( )!
='#')
putchar(c);
少了一个右括弧。
11.在用标识符时,忘记了大写字母和小写字母的区别。
例如:
main( )
{int a,b,c;
a=2;b=3;
C=A+B;
printf("%d+%d=%",A,B,C);
}
编译时出错。
编译程序把a和A认作是两个不同的变量名处理,同样b和B,c和C都分别代表两个不同的变量。
12.引用数组元素时误用了圆括弧。
例如:
main( )
{inti,a(10);
for(i=0;i<10;i++)
scanf("%d",&a(i));
}
C语言中对数组的定义或引用数组元素时必须用方括弧[]。
13.在定义数组时,将定义的“元素个数”误认为是“可使用的最大下标值”。
例如:
main( )
{inta[10]={1,2,3,4,5,6,7,8,9,10};
inti;
for(i=1;i<=10;i++)
printf("%d",a[i]);
}
想输出a[1]到a[10],是一些初学者常犯的错误。
C语言规定定义时用a[10],表示a数组有10个元素,而不是可以用的最大下标值为10。
数组只包括a[0]到a[9]10个元素,因此用a[10]就超出a数组的范围了。
14.对二维或多维数组的定义和引用的方法不对。
例如:
main( )
{inta[5,4];
…
printf("%d",a[1+2,2+2]);
…
}
对二维数组和多维数组在定义和引用时必须将每一维的数据分别用方括弧括起来。
上面a[5,4]应改为a[5][4],a[1+2,2+2]应改为a[1+2][2+2]。
根据C的语法规则,在一个方括弧中的是一个维的下标表达式,a[1+2,2+2]中方括弧中的“1+2,2+2”是一个逗号表达式,它的值是第二个数值表达式的值,即2+2的值为4。
所以a[1+2,2+2]相当于a[4]。
而a[4]是a数组的第4行的首地址。
因此执行printf函数输出的结果并不是a[3][4]的值,而是a数组第4行的首地址。
15.误认为数组名代表数组中全部元素。
例如:
main( )
{inta[4]={1,3,5,7};
printf("%d%d%d%d\n",a);
}
企图用数组名代表全部元素。
在C语言中,数组名代表数组首地址,不能通过数组名输出4个整数。
16.混淆字符数组与字符指针的区别。
例如:
main( )
{charstr[4];
str="Computerandc";
printf("%s\n",str);
}
编译出错。
str是数组名,代表数组首地址。
在编译时对str数组分配了一段内存单元,因此在程序运行期间str是一个常量,不能再被赋值。
因此,
str=“Computerandc”是错误的。
如果把“charstr[4];”改成“charstr;”,则程序正确。
此时str是指向字符数据的指针变量,str=“Computerandc”是合法的,它将字符串的首地址赋给指针变量str,然后在printf函数语句中输出字符串“Computerandc”。
因此应当弄清楚字符数组与字符指针变量用法的区别。
17.在引用指针变量之前没有对它赋值。
例如:
main( )
{char*p;
scanf("%s",p);
…
}
没有给指针变量p赋值就引用它,编译时给出警告信息。
应当改为
Char*p,c[20];
p=c;
scanf("%s",p);
即先根据需要定义一个大小合适的字符数组c,然后将c数组的首地址赋给指针变量p,此时p有确定的值,指向数组c。
再执行scanf函数就没有问题了,把从键盘输入的字符串存放到字符数组c中。
18.Switch语句的各分支中漏写break语句。
例如:
switch(score)
{case5:
printf("Verygood!
");
case4:
printf("Good!
");
case3:
printf("Pass!
");
case2:
printf("Fail!
");
defult:
printf("dataerror!
");
}
上述switch语句的作用是希望根据score(成绩)打印出评语。
但当score的值为5时,输出为
VeryGood!
Good!
Pass!
Fail!
dataerror!
原因是漏写了break语句。
case只起标号的作用,而不起判断作用,因此在执行完第一个printf函数语句后接着执行第2、3、4、5个printf函数语句。
应改为
switch(score)
{case5:
printf("Verygood!
");break;
case4:
printf("Good!
");break;
case3:
printf("Pass!
");break;
case2:
print("Fail!
");break;
defult:
print("dataerror!
");
}
19.混淆字符和字符串的表现形式。
例如:
charsex;
sex="M";
…
sex是字符变量,只能存放一个字符。
而字符常量的形式是用单引号括起来的,应改为
sex='M';
“M”是用双引号括起来的字符串,它包括两个字符:
‘M’和‘\0’,无法存放到字符变量sex中。
20.使用自加(++)和自减(--)运算符时出的错误。
例如:
main( )
{intp,a[5]={1,3,5,7,9};
p=a;
printf("%d",*p++);
}
不少人认为“*p++”的作用是先使p加1,即指向第1个元素a[1]处,然后输出第一个元素a[1]的值3。
其实应该是先执行p++,而p++的作用是先用p的原值,用完后使p加1。
p的原值指向数组a的第0个元素a[0],
因此*p就是第0个元素a[0]的值1。
结论是先输出a[0]的值,然后再使p加1。
如果是*(++p),则先使p指向a[1],然后输出a[1]的值。
21.有人习惯用传统的方式对函数形参进行声明,但却把对函数的形参和函数中的局部变量混在一起定义。
例如:
max(x,y)
intx,y,z;
{z=x>y?
x,y;
return(z);
}
应改为
max(x,y)
intx,y;
{intz;
z=x>y?
x:
y;
return(z);
}
22.所调用的函数在调用语句之后才定义,而又在调用前未加说明。
例如:
main( )
{floatx,y,z;
x=3.5;y=-7.6;
z=max(x,y);
printf("%f\n",z);
}
floatmax(floatx,floaty)
{return(z=x>y?
x:
y);
}
这个程序乍看起来没有什么问题,但在编译时有出错信息。
原因是max函数是实型的,而且在main函数之后才定义,也就是max函数的定义位置在main函数中的调用max函数之后。
改错的方法可以用以下二者之一:
① 在main函数中增加一个对max函数的声明,即函数的原型:
main( )
{floatmax(float,float);/*声明将要用到的max函数为实型*/
floatx,y,z;
x=3.5;y=-7.6;
z=max(x,y);
printf("%f\n",z);
}
②将max函数的定义位置调到main函数之前。
即:
floatmax(floatx,floaty)
{return(z=x>y?
x:
y);}
main()
{floatx,y,z;
x=3.5;y=-7.6;
z=max(x,y);
printf("%f\n",z);
}
这样,编译时不会出错,程序运行结果是正确的。
23.误认为形参值的变化会影响实参的值。
例如:
main( )
{inta,b;
a=3;b=4;
swap(a,b);
printf("%d,%d\n",a,b);
}
swap(intx,inty)
{intt;
t=x;x=y;y=t;
}
p1=&a;p2=&b;
swap(p1,p2);
printf("%d,%d\n",a,b); /*a和b的值已对换*/
}
swap(int*pt1,int*pt2)
{intt;
t=*pt1;*pt1=*pt2;*pt2=t;
}
24.函数的实参和形参类型不一致。
例如:
main( )
{inta=3,b=4;
c=fun(a,b);
…
}
fun(floatx,floaty)
{
…
}
实参a、b为整型,形参x、y为实型。
a和b的值传递给x和y时,x和y的值并非3和4。
C要求实参与形参的类型一致。
如果在main函数中对fun作原型声明:
fun(float,float);
程序可以正常运行,此时,按不同类型间的赋值的规则处理,在虚实结合后x=3.0,y=4.0。
也可以将fun函数的位置调到main函数之前,也可获正确结果。
25.不同类型的指针混用。
例如:
main( )
{inti=3,*p1;
floata=1.5,*p2;
p1=&i; p2=&a;
p2=p1;
printf("%d,%d\n",*p1,*p2);
}
企图使p2也指向i,但p2是指向实型变量的指针,不能指向整型变量。
指向不同类型的指针间的赋值必须进行强制类型转换。
如:
p2=(float*)p1;
作用是先将p1的值转换成指向实型的指针,然后再赋给p2。
这种情况在C程序中是常见的。
例如,用malloc函数开辟内存单元,函数返回的是指向被分配内存空间的void*类型的指针。
而人们希望开辟的是存放一个结构体变量值的存储单元,要求得到指向该结构体变量的指针,可以进行如下的类型转换。
structstudent
{intnum;
charname[20];
floatscore;
};
structstudent student1,*p;
…
p=(structstudent*)malloc(LEN);
p是指向structstudent结构体类型数据的指针,将malloc函数返回的void*类型指针转换成指向structstudent类型变量的指针。
26.没有注意函数参数的求值顺序。
例如:
i=3;
printf("%d,%d,%d\n",i,++i,++i); 许多人认为输出必然是
3,4,5
实际不尽然。
在TurboC和其他一些C系统中输出是
5,5,4
因为这些系统是采取自右至左的顺序求函数参数的值。
先求出最右面一个参数(++i)的值为4,再求出第2个参数(++i)的值为5,最后求出最左面的参数(i)的值5。
C标准没有具体规定函数参数求值的顺序是自左而右还是自右而左。
但每个C编译程序都有自己的顺序,在有些情况下,从左到右求解和从右到左求解的结果是相同的。
例如
fun1(a+b,b+c,c+a);
fun1是一个函数名。
3个实参表达式a+b、b+c、c+a。
在一般情况下,自左至右地求这3个表达式的值和自右至左地求它们的值是一样的,但在前面举的例子是不相同的。
因此,建议最好不用会引起二义性的用法。
如果在上例中,希望输出“3,4,5”时,可以改用
i=3; j=i+1; k=j+1;
printf("%d,%d,%d\n",i,j,k);
27.混淆数组名与指针变量的区别。
例如:
main( )
{inti,a[5];
for(i=0;i<5;i++)
scanf("%d",a++);
…
}
企图通过a的改变使指针下移,每次指向欲输入数据的数组元素。
它的错误在于不了解数组名代表数组首地址,它的值是不能改变的,用a++是错误的,应当用指针变量来指向各数组元素。
即:
inti,a[5],*p;
p=a;
for(i=0;i<5;i++)
scanf("%d",p++);
或inta[5],*p;
for(p=a;p<a+5;p++)
scanf("%d",p);
28.混淆结构体类型与结构体变量的区别,对一个结构体类型赋值。
例如:
structworker
{longintnum;
charname[20];
charsex;
intage;
};
worker.num=187045;
strcpy(worker.name,"ZhangFun");
worker.sex='M';
worker.age=18;
这是错误的,只能对变量赋值而不能对类型赋值。
上面只定义