C程序常见错误30例

我们在学习C语言的过程中常常容易犯一些错误,下面就将一些错误列举出来,仅供参考:

1、忘记定义变量例如:

void main()
{
     x=3;
     y=6;
     printf("%d\n",x+y);
}
C要求对程序中用到的每一个变量都必须定义其类型,而上面程序中没有对 x,y进行定义。应该在函数的开头加:

int x,y;

2、输入输出的数据类型与所用格式说明符不一致。例如:

int a=3;
float b=4.5;
printf("%f %d\n",a,b);
对上面的程序,编译时不会出现错误,但是运行结果与意愿不符,输出为:

0.000000   16402

它们并不是按照赋值的规则进行转换(如把4.5转换成4),而是将数据在存在单元中的形式按照格式符的要求组织输出(如b占4个字节,只把最后两个字节中的数据按照%d作为整数输出)。

3、未注意int 数据的数值范围。Turbo C等编译系统,对一个整型数据分配2个字节。因此一个整数的范围为-2的15次方到2的15次方-1,即132768~32767。常见这样的程序段:

int num;
num=89101;
printf("%d\n",num);
得到的确实23565,原因是89101已超过32767,。2个字节容不下89101,则将高位截去,即将89101减去2的16次方,即89101-65536=23565。

4、在输入语句scanf中忘记使用变量的地址符,例如:

scanf("%d%d",a,b);
这是许多初学者常见的错误,或者说是习惯性的错误,因为在其他语句中只需要写出变量名即可。而C语言要求指明“向哪个地址标识的单元送值”。应写成

scanf("%d%d",&a,&b);
5、输入数据的形式与要求不符。用scanf 函数输入数据,应注意如何组织输入数据。假如有以下scanf 函数:

scanf("%d%d",&a,&b);
有人按下面的方法输入数据:

3,4↙

这是错误的。数据间应该用空格(或Tab 键,或者回车符)来分隔。

6、误把“=”作为“等于”运算符。在C语言中,“=”是赋值运算符,“==”才是关系运算符"等于"如果写成:

if(a=b)
   printf("a equal to b !");
C编译系统将(a=b)作为赋表达式处理,将b的值赋给a,然后判断a的值是否为零,若为非零,则作为“真”;若为零,则作为" 假"。

这种错误在编译时是检查不出来的,但运行结果往往是错误的。而且程序设计者自己往往也不容易发现。
7、语句后面漏分号。例如:

a=3
b=4.5
在编译时,编译程序在“a=3”后面未发现分号,就把下一行“b=4.5”也作为上一行的语句的一部分,这就出现语法错误。

8、在不该加分号的地方加了分号。例如:

if(a>b);
   printf("a  is larger than b !");
程序在运行到了if语句就结束了,接着执行下一条语句,即不论a 与b 的关系如何都会出现打印语句。
9、对应该有花括号的复合语句,忘记加花括号。例如:

<pre name="code" class="cpp">sun=0;
i=0;
while(i<=100)
     sum=sum+i;
      i++;

上面的语句只是重复了sun=sum+i 的操作,而且循环永不终止。应该写成:
sun=0;
i=0;
while(i<=100)
{
     sum=sum+i;
      i++;
}
10、括号不配对。当一个语句中使用多层括号时常出现这类错误,纯属粗心所致。例如:

while((c=getchar()!='#')
    putchar(c);
少了一个右括号。

11、在用标示符时,忘记了大写字母和小写字母的区别。例如:

void main()
{
      int a,b,c;
      a=2;b=3;
      C=A+B;
      printf("%d+%d=%d",A,B,C);
}
编译时出错。编译程序把a和A认作两个不同的变量名处理,是不同的变量。

12、引用数组元素时误用了圆括号。例如:

void main()
{
    int i,a[10];
    for(i=0;i<10;i++)
         scanf("%d",&a(i));
}

C语言中对数组的定义和引用都必须使用方括号。

13、在定义数组时,将定义的“元素个数”误认为是“可使用的最大下标值”。例如:

void main()
{
    int a[10]={1,2,3,4,5,6,7,8,9,10};
    int i;
    for(i=0;i<=10;i++)
         printf("%d",a[i]);
}
数组只包括a[0]到a[9]这10个数,因此用a[10]就超出a数组的范围了。

14、对二维或多维数组的定义或引用方法不对。例如:

<pre name="code" class="cpp">int main()
{
    int a[4,5];
       .
       .
    printf("%d",a[1+2,2+2]);
}


上面程序中的a[4,5]应该改为a[4][5],a[1+2,2+2]应该改为a[1+2][2+2]。

15、误以为数组名代表数组中的全部元素。例如:

int main()
{
    int a[4]={1,3,5,7};
    printf("%d %d %d %d",a);
}
企图用数组名代表全部元素。在C语言中,数组名代表数组首地址,不能通过数组名输出4个整数。

16、混淆字符数组和字符指针的区别。例如:

int main()
{ 
    char str[15];
    str="computer and c";
    printf("%s\n",str);
}
编译出错。str是数组名,代表这数组首地址。在编译时对str数组分配了一段内存单元,因此在程序运行期间str是一个常量,不能再被赋值。所以,str="computer and c"是错误的。如果把“char str[15];" 改成“char *str;”则程序正确。此时,str是指向字符数据的指针变量,str=“computer and c”是合法的,它将字符串的首地址赋给指针变量str,然后在printf 函数语句中输出字符串。

17、在指针变量之前没有对它赋予正确的值。例如:

int main()
{ 
    char *str;
    scanf(“%s”,str);
}
没有给指针变量赋值就引用它,编译时给出警告信息。应当改成:

char *str,c[20];
str=c;
scanf("%s",str);
先根据需要定义一个大小合适的字符数组c,然后将c数组的首地址赋给指针变量str ,此时str有确定的值,指向数组c。再执行scanf函数就没有问题了,把从键盘输入的字符串存放到字符数组c中。

18、switch语句中的各分支漏写break语句。例如:

switch(score)
{
  case 5:printf("Very good!");
  case 4:printf("Good!");
  case 3:printf("Pass!");
  case 2:printf("Fail!");
  default:printf("Data error!");
}
上述switch语句的作用是希望根据score(成绩)输出评语。但当score的值为5时,输出为:

Very good ! Good! Pass! Fail! Data error!

原因是漏写break语句。case只起标号的作用,而不起判断作用,因此在执行完第一个printf函数后接着执行第2、3、4、5个printf函数。应该为:

switch(score)
{
  case 5:printf("Very good!");
             break;
  case 4:printf("Good!");
             break;
  case 3:printf("Pass!");
             break;
  case 2:printf("Fail!");
             break;
  default:printf("Data error!");
             break;
}
19、混淆字符和字符串的表示形式。例如:

char sex;
sex="M";
   .
   .
   .
sex是字符变量,只能存放一个字符。而字符常量的形式是用单撇号括起来的,应该改为:

sex='M';
"M"是用双撇号括起来的字符串,它包括两个字符:’M‘ 、’\0‘,无法存放到字符变量sex中。
20、使用自加(++)和自减(--)运算符时出的错误。例如:

void main()
{
    int *p,a[5]={1,3,5,7,9};
    p=a;
    printf("%d",*p++);
}
不少人认为“*p++”的作用是先是p加1,即指向第一个元素a[1],然后输出第一元素a[1]的值 3.其实应该是先执行p++,而p++ 的作用是先用p 的原值,用完后使p加1,p的原值指向数组a的第0个元素a[0],因此,*p就是第0个元素a[0]的 值1。如果是*(p++),则先使p指向a[1],然后输出a[1]的值。

21、所调用的函数在调用语句之后才定义,而又在调用前未声明。例如:

void main()
{
   float x,y,z;
   x=3.5;y=4.5;
   z=max(x,y);
   printf("%f\n",z);
}
float max(float x,float y)
{
   return (x>y?x:y);
}
编译出错。max函数在main之后才定义,改错的方法可以用以下二值之一:

(1)在 main函数中增加一个队max函数的声明,即:

void main()
{
   float max(float x,float y);   //声明将要用到的max函数为实型
   float x,y,z;
   x=3.5;y=4.5;
   z=max(x,y);
   printf("%f\n",z);
}
float max(float x,float y)
{
   return (x>y?x:y);
}
(2)将max函数的定义位置调到main函数之前,即:

float max(float x,float y)
{
   return (x>y?x:y);
}
void main()
{
   float x,y,z;
   x=3.5;y=4.5;
   z=max(x,y);
   printf("%f\n",z);
}
这样,编译时不会出错,程序运行时也是正确的。

22、对函数声明与函数定义不匹配。例如:定义了一个fun 函数

int fun(int x,float y, long z);
在主调函数中作下面的声明都将出错。

fun(int x,float y, long z);         //漏写函数类型
float fun(int x,float y, long z);   //函数类型不匹配
int fun(int x,int y, long z);       //参数类型不匹配
int fun(int x,float y);             //参数数目不匹配
int fun(int x,long z,float y);      //参数顺序不匹配
下面的声明是正确的。

int fun(int x,float y, long z);
int fun(int ,float , long);        //可以不写形参名
int fun(int a,float b, long c);    //编译时不检查函数原型中的形参名
23、在需要加头文件时没有用# include命令去包含头文件。例如:

程序中要用到fabs函数,没有用 # include <math.h>

程序中用到输入输出函数,没有用 # include <stdio.h>

24、误认为形参值的改变会影响实参的值例如:

<pre name="code" class="cpp">
void main()
{
   void swap(int x,int y);   
   int a,b;
   a=3;b=4;
   swap(a,b);
   printf("%d %d\n",a,b);
}
void swap(int x,int y)
{
   int t;
   t=x;x=y;y=t;
}


原意是通过调用swap函数使a 和 b的值对换,然后在main函数中输出已对换的值。但是这个程序打不到这样的目的,因为x和y的值的变化是不传回实参a 和 b 的,main 函数中的a 和 b的值并未改变。如果要达到这样的目的,应该使用指针变量,用指针变量作函数参数,使指针变量指向的变量的值发生变化。即:

void main()
{
   void swap(int *x,int *y);   
   int a,b;
   a=3;b=4;
   swap(&a,&b);
   printf("%d %d\n",a,b);
}
void swap(int *x,int *y)
{
   int t;
   t=*x;*x=*y;*y=t;
}
25、函数的实参和形参类型不一致。例如:
void main()
{
   int fun(int x,int y);
   float a=3.5,b=4.6,c;
   c=fun(a,b);
    .....
    
}
int fun(int x,int y)
{
  return x+y;
}
实参a 、b为float型,形参x、y为int 型。C要求形参与实参的类型一致。程序在编译时给出“ 警告”信息,程序可以“带病运行”。

26、不同类型的指针混用。例如:
void main()
{
   int i=3,*p1;
   float a=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 *l类型的指针。而人们希望开辟的是存放一个结构体变量值的存储单元,要求得到指向该结构体变量的指针,可以进行如下所示的类型转换。

struct student
{
  int num;
  char name[10];
  float score;
};

struct student stdent1,*p;
   .....
p=(struct student *)malloc(sizeof(struct student));
 p是指向 struct student 结构体类型数据的指针,将malloc函数返回的 void *类型指针转换成指向 struct student 类型变量的指针。

27、没有注意函数参数的求值顺序。例如:

int i=3;
printf("%d,%d,%d",i,++i,++i);
许多人认为输出必然是:

3,4,5

实际不尽然,在Turbo C、Visual C++和其他一些C系统中输出的是:

5,5,4

因为这些系统采用的自右向左的顺序求函数参数的值。

28、混淆数组名和指针变量的区别。例如:

void main()
{ 
   int i,a[5];
   for(i=0;i<5;i++)
   {
       scanf("%d",a++);
   }
}
企图通过a的改变使指针下移,每次指向欲输出数据的数组元素。错误在于不了解数组名代表函数的首地址,它的值是不能改变的,用a++是错误的,应该用指针变量来实现,即:

void main()
{ 
   int i,a[5],*p;
   p=a;
   for(i=0;i<5;i++)
   {
       scanf("%d",p++);
   }
}
或者:
void main()
{ 
   int a[5],*p;
   for(p=a;p<a+5;p++)
   {
       scanf("%d",p);
   }
}
29、混淆结构体类型与结构体变量的区别,对一个结构体类型赋值。例如:

struct worker
{
    long int num; 
    char name[20];
    char sex;
    int age;
};
worker.num=10101;
strcopy(worker.name,"ZhangFun");
worker.sex='M';
worker.age=20;
这是错误的,只能对变量赋值而不能对类型赋值。上面只定义了struct  worker类型而未定义变量。应该改为:

struct worker
{
    long int num; 
    char name[20];
    char sex;
    int age;
};
struct worker  worker_1;
worker_1num=10101;
strcopy(worker_1.name,"ZhangFun");
worker_1.sex='M';
worker_1.age=20;
定义一个结构体变量,并对其中的各个成员赋值。

30、使用文件时忘记打开或者打开方式与使用情况不匹配。例如,对文件的读写,用只读的方式打开,却企图向该文件输出数据,例如:

if((fp=fopen("test.txt","r")==NULL)
{
    printf("cannot open this file!\n");
     exit(0);
}
ch=fgetc(fp);
while(ch!='#')
{
  ch=ch+4;
  fputc(ch,fp);
  ch=fget(fp);
}
对以“r”(只读方式)方式打开的文件,进行既读又写的操作,显示是不行的。

此外,有的程序常忘记关闭文件,虽然系统会自动关闭所有文件,但是可能会丢失数据。因此必须在用完文件后关闭它。


以上只是列举了一些初学者常出现的错误,对这些错误大多是对于C语法不熟悉所造成的。对C 语言使用多了,比较熟练了,犯这些错误自然就会减少。在深入使用C语言后,还会出现一些更深入、更隐蔽的错误。

总结之:程序出错有3中情况:

(1)语法错误。违背了C语法的规定,对这类错误,编译系统一般能给出“出错信息”,并且告诉在哪一行出错。只要细心,就会很快发现并排除的。

(2)逻辑出错。程序并无违背C语法规定,但程序执行的结果与原意不符。这是由于程序设计人员设计的算法有错误或者变成的程序有错,通知给系统的指令与解题的原意不符,即出现了逻辑上的错误。

(3)运行错误。程序既无语法错误,也无逻辑错误,但在运行时出现错误甚至停止运行,例如:

int a,b c;
scanf("%d%d",&a,&b);
c=b/a;
printf("c=%d\n",c);
输入a 和 b的值,输出b/a的值,程序没有错。但是如果输入a 的值为0,就会出现错误。


写完一个程序只能说完成任务的一半(甚至不到一半)。调试程序往往比写程序更难,更需要精力、时间和经验。

















C语言常见错误小结 2008-06-12 10:55:08 C语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法检查并不象其它高级语言那么严格,这就给编程人员留下 “灵活的余地”,但还是由于这个灵活给程序调试带来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误。看着有错的程 序,不知该如何改起,本人通过对C的学习,积累了一些C编程时常犯的错误,写给各位学员以供参考。 1.书写标识符时,忽略了大小写字母的区别。 main() { int a=5; printf("%d",A); } 编译程序把a和A认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。 2.忽略了变量的类型,进行了不合法的运算。 main() { float a,b; printf("%d",a%b); } %是求余运算,得到a/b的整余数。整型变量a和b可以进行求余运算,而实型变量则不允许进行“求余”运算。 3.将字符常量与字符串常量混淆。 char c; c="a"; 在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“\”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a'和‘\',而把它赋给一个字符变量是不行的。 4.忽略了“=”与“==”的区别。 在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中可以写 if (a=3) then … 但C语言中,“=”是赋值运算符,“==”是关系运算符。如: if (a==3) a=b; 前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a。由于习惯问题,初学者往往会犯这样的错误。 5.忘记加分号。 分号是C语句中不可缺少的一部分,语句末尾必须有分号。 a=1 b=2 编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。 { z=x+y; t=z/100; printf("%f",t); } 对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)。 6.多加分号。 对于一个复合语句,如: { z=x+y; t=z/100; printf("%f",t); }; 复合语句的花括号后不应再加分号,否则将会画蛇添足。 又如: if (a%3==0); I++; 本是如果3整除a,则I加1。但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将执行I++语句,不论3是否整除a,I都将自动加1。 再如: for (I=0;I<5;I++); {scanf("%d",&x); printf("%d",x);} 本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循环体变为空语句,此时只能输入一个数并输出它。 7.输入变量时忘记加地址运算符“&”。 int a,b; scanf("%d%d",a,b); 这是不合法的。Scanf函数的作用是:按照a、b在内存的地址将a、b的值存进去。“&a”指a在内存中的地址。 8.输入数据的方式与要求不符。 ①scanf("%d%d",&a,&b); 输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法: 3,4 输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab。 ②scanf("%d,%d",&a,&b); C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。下面输入是合法的: 3,4 此时不用逗号而用空格或其它字符是不对的。 3 4 3:4 又如: scanf("a=%d,b=%d",&a,&b); 输入应如以下形式: a=3,b=4 9.输入字符的格式与要求不一致。 在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。 scanf("%c%c%c",&c1,&c2,&c3); 如输入a b c 字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后面不需要用空格作为两个字符的间隔。 10.输入输出的数据类型与所用格式说明符不一致。 如,a已定义为整型,b定义为实型 a=3;b=4.5; printf("%f%d\n",a,b); 编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。 11.输入数据时,企图规定精度。 scanf("%7.2f",&a); 这样做是不合法的,输入数据时不能规定精度。 12.switch语句中漏写break语句。 如:根据考试成绩的等级打印出百分制数段。 switch(grade) { case 'A':printf("85~100\n"); case 'B':printf("70~84\n"); case 'C':printf("60~69\n"); case 'D':printf("<60\n"); default:printf("error\n"); 由于漏写了break语句,case只起标号的作用,而不起判断作用。因此,当grade值为A时,printf函数在执行完第一个语句后接着执行第二、三、四、五个printf函数语句。正确写法应在每个分支后再加上“break;”。如 case 'A':printf("85~100\n");break; 13.忽视了while和do-while语句在细节上的区别。 (1)main() {int a=0,I; scanf("%d",&I); while(I<=10) {a=a+I; I++; } printf("%d",a); } (2)main() {int a=0,I; scanf("%d",&I); do {a=a+I; I++; }while(I10时,二者结果就不同了。因为while循环是先判断后执行,而do- while循环是先执行后判断。对于大于10的数while循环一次也不执行循环体,而do-while语句则要执行一次循环体。 14.定义数组时误用变量。 int n; scanf("%d",&n); int a[n]; 数组名后用方括号括起来的是常量表达式,可以包括常量和符号常量。即C不允许对数组的大小作动态定义。 15.在定义数组时,将定义的“元素个数”误认为是可使的最大下标值。 main() {static int a[10]={1,2,3,4,5,6,7,8,9,10}; printf("%d",a[10]); } C语言规定:定义时用a[10],表示a数组有10个元素。其下标值由0开始,所以数组元素a[10]是不存在的。 16.初始化数组时,未使用静态存储。 int a[3]={0,1,2}; 这样初始化数组是不对的。C语言规定只有静态存储(static)数组和外部存储(exterm)数组才能初始化。应改为: static int a[3]={0,1,2}; 17.在不应加地址运算符&的位置加了地址运算符。 scanf("%s",&str); C语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入项是字符数组名,不必要再加地址符&。应改为: scanf("%s",str); 18.同时定义了形参和函数中的局部变量。 int max(x,y) int x,y,z; {z=x>y?x:y; return(z); } 形参应该在函数体外定义,而局部变量应该在函数体内定义。应改为: int max(x,y) int x,y; {int z; z=x>y?x:y; return(z); }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值