数学建模社区-数学中国

标题: [转帖]++C语言中常见错误++ [打印本页]

作者: solucky    时间: 2004-6-6 19:01
标题: [转帖]++C语言中常见错误++
<>C语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下“灵活的余地”,但还是由于这个灵活给程序的调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误。看着有错的程序,不知该如何改起,本人通过对C的学习,积累了一些C编程时常犯的错误,写给各位学员以供参考。
- H) w" ^" L0 Z6 V/ |. F7 ^1.书写标识符时,忽略了大小写字母的区别。( w. C, d' K; E; \
main()
9 ?/ O! n, u7 E2 a{0 h, m- O. y- g, \6 l* K' S9 Q5 h- D
int a=5;
# ~4 w5 F. [) P, Y9 P$ yprintf("%d",A);
, C) O+ ]0 V- O% c* M, d( J6 S}
: \$ @7 q& J# b" ^编译程序把a和A认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。3 A; L! B% G" G5 Z8 |
2.忽略了变量的类型,进行了不合法的运算。# A, u( Y4 g/ E9 O% A  {. `
main(): b- i0 V0 R1 b
{
; H! A0 V$ x5 Y: xfloat a,b;( _( C4 |' q) K: l& X1 g' e" n
printf("%d",a%b);
8 J  X6 G) B5 R, ~; h}
+ B1 p8 n& z# M7 @" S& c+ E, T1 y) w%是求余运算,得到a/b的整余数。整型变量a和b可以进行求余运算,而实型变量则不允许进行“求余”运算。* l  E& T2 B8 g; }9 x4 ]
3.将字符常量与字符串常量混淆。* n, f7 `( }6 b: Q& b% K4 |3 C
char c;$ F/ g6 j7 [% u- l
c="a";( x) F2 \6 f$ g1 Y4 F. H7 f1 G
在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“\”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a'和‘\',而把它赋给一个字符变量是不行的。
$ A% p: w/ h; F3 Z. s$ p4.忽略了“=”与“==”的区别。0 R! w4 N+ R3 a
在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中可以写( T/ K( k6 ]) M0 p; l4 |7 T" ~
if (a=3) then …' h4 q$ G9 G6 q% F$ R, x" _! W- Y/ S7 x
但C语言中,“=”是赋值运算符,“==”是关系运算符。如:
3 R( a/ D, a8 {5 M5 nif (a==3) a=b;
( c% I# l# F, O' w" W& y4 |6 W$ \前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a。由于习惯问题,初学者往往会犯这样的错误。
- W$ _- N( Y2 D( i: w( H5.忘记加分号。
( I' x6 c- _. T( |# C6 q! Y分号是C语句中不可缺少的一部分,语句末尾必须有分号。8 F; q- j  F# N( V$ j0 i
a=1
3 f4 S* s5 R; h0 ^" D* K2 c& F# f  ub=2
3 r4 Z; `: S' j+ e+ h/ e+ T. I编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。
! }7 `- u8 K% Y6 p/ P/ _{ z=x+y;
, x& i; n4 @9 K9 o' mt=z/100;
/ v0 n0 t8 n1 v: a( d7 k) S7 E- Jprintf("%f",t);# o% j' w7 j6 l* P; ?
}' Q5 Z# m0 s. C; y; T
对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)。
( e+ J& W. C5 S/ |0 c3 ?: ]& o6.多加分号。5 ?$ u" j  z1 M  x: U( j  F
对于一个复合语句,如:
/ h. Y, Q9 a. v) I. v1 K{ z=x+y;( g& P7 [# n* l6 t
t=z/100;, f8 A9 J/ M' J
printf("%f",t);
  Q5 l; \/ G% S- e% {0 t};) Z/ i7 x' z% h2 p1 q
复合语句的花括号后不应再加分号,否则将会画蛇添足。
8 q5 o. k& I+ o/ B+ F% l又如:
& E$ M0 f* f6 j* K  O% _1 qif (a%3==0);3 O/ Y) b0 d* c. a4 |3 e
I++;
8 {3 Y7 r9 f; e, S本是如果3整除a,则I加1。但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将执行I++语句,不论3是否整除a,I都将自动加1。
; B- g/ E1 \9 _# h  r再如:
& D$ B1 G# y  a' ]% d5 \+ H, Xfor (I=0;I&lt;5;I++);( }) \5 n3 [" p; x
{scanf("%d",&amp;x);
7 I0 ~3 X8 U; W. I: \% Rprintf("%d",x);}
0 Y& }/ I+ _9 Q  K本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循环体变为空语句,此时只能输入一个数并输出它。
+ j) i1 _$ S3 T( v+ |  F7.输入变量时忘记加地址运算符“&amp;”。
) U7 J. \' }4 C3 i1 x' ]2 r' [* yint a,b;
9 y- l+ m+ Q/ M: M" Nscanf("%d%d",a,b);3 J0 ]# B9 T8 f7 O- Z, K/ F
这是不合法的。Scanf函数的作用是:按照a、b在内存的地址将a、b的值存进去。“&amp;a”指a在内存中的地址。
( n# [/ v* D" q' m  S8.输入数据的方式与要求不符。①scanf("%d%d",&amp;a,&amp;b);
$ H6 x  v- P3 R5 j6 W输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法:* @) ]* r1 a+ W0 r1 d# J
3,4
1 D. ^- W( o) m- m+ a输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab。$ i# U2 T, I5 R3 D
②scanf("%d,%d",&amp;a,&amp;b);" A- E$ m( G. b3 ]0 E
C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。下面输入是合法的:
; S$ I# ]5 C& ?* w6 Q7 U3,4
6 S& [* y8 _9 [% Z, K此时不用逗号而用空格或其它字符是不对的。6 p$ t" Q( W- `" Q$ n
3 4 3:4
% H' k# r, `! v: r. ~又如:) F9 x$ f3 C) U9 P
scanf("a=%d,b=%d",&amp;a,&amp;b);' i0 {, p( c, Z3 \) p, ~" P. o
输入应如以下形式:% i2 U) P) h$ V& h6 D
a=3,b=4 6 I. o1 k: N6 |' B5 M$ s  v: k  y
9.输入字符的格式与要求不一致。
2 d7 U! c) j3 g- i( v& Q1 c5 t在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。) N  z% F) I1 x: l1 g0 x
scanf("%c%c%c",&amp;c1,&amp;c2,&amp;c3);0 X1 }" t0 r* h6 f8 J/ e
如输入a b c 2 w# N; e5 @" G" d* {
字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后面不需要用空格作为两个字符的间隔。9 U0 D9 V, a$ _8 U
10.输入输出的数据类型与所用格式说明符不一致。  K7 l* ?; C& h* P
例如,a已定义为整型,b定义为实型
0 l9 ^) c# h8 F  B' ^, Ja=3;b=4.5;
9 x+ }& V1 w* Dprintf("%f%d\n",a,b);
8 M1 g( e" Y; A  y. A3 D5 f编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。
4 {' o/ p. I+ V/ ?11.输入数据时,企图规定精度。' o9 N- w/ q2 y* G
scanf("%7.2f",&amp;a);0 _: A, d# L$ R, V. J
这样做是不合法的,输入数据时不能规定精度。
4 R/ l$ G' J/ C12.switch语句中漏写break语句。. [+ G/ N1 [3 l  h! E' u
例如:根据考试成绩的等级打印出百分制数段。
8 g; a* D) U- I; ^switch(grade): m  u/ E2 \7 x$ d7 N5 h' L
{ case 'A':printf("85~100\n");
, O5 \- y- K+ P9 C, vcase 'B':printf("70~84\n");
8 r5 ]  m9 `7 ]1 E, `" @' |( Vcase 'C':printf("60~69\n");
( m4 S6 i* T/ T$ Y0 H2 ?8 R* ocase 'D':printf("&lt;60\n");
, q: B  M, C3 {0 e( `% Wdefault:printf("error\n");# e1 N3 ]/ |. p$ ^6 E8 K5 i
由于漏写了break语句,case只起标号的作用,而不起判断作用。因此,当grade值为A时,printf函数在执行完第一个语句后接着执行第二、三、四、五个printf函数语句。正确写法应在每个分支后再加上“break;”。例如3 s; r. d8 n; D3 t9 a& O. w
case 'A':printf("85~100\n");break;' n$ {' @' T) q6 \4 P- j
13.忽视了while和do-while语句在细节上的区别。
: a4 {$ [% v* b5 ?1 L(1)main()
$ L! S* ~) y0 y$ r" a$ \) R$ q% {{int a=0,I;* a, D  ]& x$ l6 m' }
scanf("%d",&amp;I);" S/ k9 k. \% [6 \7 ~% d! ^8 A0 _+ V
while(I&lt;=10)8 ]7 q9 Z: V/ l/ b& L/ K' |
{a=a+I;2 _7 j6 g6 }# _( d% j( L2 H
I++;4 |. V5 R5 F. T8 q5 w
}
  c  x6 d# {# gprintf("%d",a);# J# F0 u5 B6 H3 `
}
0 C9 c$ O2 h( ~4 x(2)main()
1 ~8 K+ B# C5 }: p% x{int a=0,I;7 p5 ]0 \  o! g3 Z4 j0 i, ?
scanf("%d",&amp;I);9 B9 v# Q. z' G/ C
do
6 q7 N2 T0 D+ m' V  D# }{a=a+I;$ |7 c6 \8 M3 B) e/ J2 D  n
I++;
( A' E3 a  \  s  B3 W}while(I&lt;=10);
3 W& w  T' a0 Lprintf("%d",a);, ]+ V6 }  t/ @
}; ?# o/ y8 q7 T( v' |: C1 \$ W2 l
可以看到,当输入I的值小于或等于10时,二者得到的结果相同。而当I&gt;10时,二者结果就不同了。因为while循环是先判断后执行,而do-while循环是先执行后判断。对于大于10的数while循环一次也不执行循环体,而do-while语句则要执行一次循环体。0 k- L5 M% p* t7 P+ W5 k
14.定义数组时误用变量。" j3 a  s, ?8 Z4 }3 \5 g  f4 @0 v
int n;
1 J8 ^- M1 s- H; ^% Rscanf("%d",&amp;n);
5 B- s! O' u, o+ Rint a[n];
0 C3 Z. ]+ {6 S' n4 I数组名后用方括号括起来的是常量表达式,可以包括常量和符号常量。即C不允许对数组的大小作动态定义。
* H5 a' O( y+ m, @: M' c15.在定义数组时,将定义的“元素个数”误认为是可使的最大下标值。5 c3 B& m- S$ g! j$ d" S. E
main()
5 [. [/ v. ?8 H5 c{static int a[10]={1,2,3,4,5,6,7,8,9,10};
" H0 _' }! G4 g: b  P- C# @5 U. Rprintf("%d",a[10]);5 P: q% R( B3 d0 c8 o8 [! Z  M  m
}
1 G- e5 k5 A2 r8 x" qC语言规定:定义时用a[10],表示a数组有10个元素。其下标值由0开始,所以数组元素a[10]是不存在的。
3 J2 O" G' V1 a+ S16.初始化数组时,未使用静态存储。+ `1 g4 {! {3 y, x# o& J' `; X, B
int a[3]={0,1,2};
% u. M! k% l; x& G3 @这样初始化数组是不对的。C语言规定只有静态存储(static)数组和外部存储(exterm)数组才能初始化。应改为:# l: W2 i4 @0 b. G
static int a[3]={0,1,2};- Z! s. Z7 z+ G7 {
17.在不应加地址运算符&amp;的位置加了地址运算符。3 k1 R. J' F/ h7 m# x" R
scanf("%s",&amp;str);
/ N# g% T7 j9 }  V. T* vC语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入项是字符数组名,不必要再加地址符&amp;。应改为:
& Z4 C9 K  g. N' d  t$ d8 R0 s7 ?scanf("%s",str);* H7 g0 x- _5 J) Y1 z1 l$ Z
18.同时定义了形参和函数中的局部变量。
" |8 n+ A- Y2 L8 S  Xint max(x,y)" r+ c. S, z5 O" ?/ N; }
int x,y,z;
" J  {) h! j& T: r/ |{z=x&gt;y?x:y;
$ U( i6 ?+ c9 B- greturn(z);
3 h% v; G! O) v) r6 o- [% A}% P) J5 o! @9 t1 H5 g  G( A
形参应该在函数体外定义,而局部变量应该在函数体内定义。应改为:
& C$ b1 Q* {2 `, L4 p( `, u/ W, bint max(x,y)
$ \5 ^" F8 F+ xint x,y;
5 x5 ~- P$ @5 X2 K" }{int z;
" C: z/ ?9 z# w( Uz=x&gt;y?x:y;
- C# S  m; U  n: b$ K+ `return(z);0 H* j, f( d! v3 b
}</P>
; A" [$ O- l- D& H' d. o2 k<>以上错误中可能有些不符合新版的C语言,比如数组的初始化,新版中就可以是不是静态变量。由于是转贴就未加修改,以保持文章的原貌,请各位自加区别。<b>版权属于原创作者!!!</b></P>




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5