分数的加减法——C语言初学者代码中的常见错误与瑕疵(10)

题目

分数的加减法 html

编写一个C程序,实现两个分数的加减法
输入:输入包含多行数据
每行数据是一个字符串,格式是"a/boc/d"。
其中a, b, c, d是一个0-9的整数。o是运算符"+"或者"-"。
输出:对于输入数据的每一行输出两个分数的运算结果。
注意结果应符合书写习惯,没有多余的符号、分子、分母,而且化简至最简分数 程序员

样例输入:
1/8+3/8
1/4-1/2
1/3-1/3
输出:
1/2
-1/4
0数组

评析 

  彻底看不懂题目!数据结构

  原本是一个很好的问题,惋惜被出题者给败坏了。函数

  最大的毛病出在“每行数据是一个字符串,格式是"a/boc/d"”这行文字,驴唇不对马嘴,使人没法理解。post

  字符串的定义是:A string is a contiguous sequence of characters terminated by and including the first null character.在文本输入流中并不存在null character——\0,所以严格地说文本流根本不可能存在字符串。url

  更搞笑的是“格式是"a/boc/d"”。把"a/boc/d"这种东西叫字符串(String Literal)的前提是在C代码层面而言。若"……"这种形式的东西出如今莎士比亚戏剧中,那就绝对不是字符串。把输入文本流中的"……"说成是字符串,是“关公战秦琼”式的说法。看了后面的输入样例,我才弄明白这里的""是根本没有的。spa

  另外一个毛病是“其中a, b, c, d是一个0-9的整数”,这是多此一举式的简化,并无使问题获得任何真正的简化,只起到了迷惑解题者、束缚解题者的做用。设计

  综上所述,这是一个极好的问题,但倒是一个败家的提法。code

  对这样的问题,常常出现的情形是,初学者已经开始写代码了,高手还在呆呆的思考题目和题目的要求究竟是什么。这是初学者和成熟的程序员之间一个很是显著的区别。

  初学者解决问题的时间分配一般是倒三角形

   

  而成熟的程序员则偏偏相反

  

  固然,我并非反对新手在写代码方面进行大量练习,可是新手特别容易忽视理解问题要求和不多在数据结构方面深思熟虑的弱点应该予以充分的重视。

原代码1:

#include <stdio.h>
#include <math.h>

int comdiv(int x,int y);

int main()
{
 char string[8];
 int a[4],i,j,comd,b[2];
 while(gets(string)!=NULL)
 {
  for(i=0,j=0;i<4;i++,j+=2)
   a[i]=(int)string[j]-48;

  if(a[1]==0 || a[3]==0)
   printf("matherror\n");

  else if(string[3]=='-')
  {
   b[0]=a[0]*a[3]-a[1]*a[2];
   b[1]=a[1]*a[3];
   if(b[0]==0)
    printf("0\n");

   else if(b[0]>0)
     comd=comdiv(b[0],b[1]);
     else 
     { 
      comd=comdiv(-b[0],b[1]);
      printf("%d/%d\n",b[0]/comd,b[1]/comd);
     }
  }

  else 
  {
   b[0]=a[0]*a[3]+a[1]*a[2];
   b[1]=a[1]*a[3];
   comd=comdiv(b[0],b[1]);
   printf("%d/%d\n",b[0]/comd,b[1]/comd);
  }
 }

  return 0;
}

int comdiv(int x,int y)
{
 int i,j;

 if(x==0||y==0)
  return 1;
 else if(x==1||y==1)
  return 1;
 else if(x==y)
  return x;
 else if(x>y && !(x%y))
  return y;
 else if(!(y%x))
  return x;

 for(i=2,j=1;i<=(x>y?x:y);i++)
 {
  if(x%i==0&&y%i==0)
   j=i;
 }

 return j;
}

评析:

#include <math.h>

   一旦涉及计算,新手就喜欢写这个,实际上根本不须要。在math.h中列出函数原型的函数都是近似计算函数,整数精确计算一般都用不着math.h中的函数。

char string[8];

  这里的毛病是数组定义得过小,很容易出毛病。受到谭浩强流的坏影响,很多初学者在为存储字符串定义字符数组时,常常有斤斤计较的毛病。

 int a[4],i,j,comd,b[2];

  这位小朋友把a , b ,c ,d视为一个int [4],把运算结果视为一个int [2]。应该说有初步的数据结构设计意识,但还处于很幼稚的阶段。 

while(gets(string)!=NULL)

  这个彻底是被题目给骗蒙了,真的把输入流中的 1/8+3/8 看成字符串存储了,这很傻。实际上,对于输入流中的  1/8+3/8 能够有不少视角。从scanf()的角度来看,也能够把 1/8+3/8 视为"%d/%d%c%d/%d"。这样就不难明白为何前面说原题目中的“其中a, b, c, d是一个0-9的整数”是“多此一举式的简化,并无使问题获得任何真正的简化,只起到了迷惑解题者、束缚解题者的做用”了。

  还有须要说明的是,gets()这个函数事实上已经被C语言开除了,在C语言正式开除它以前,不少职业程序员在更早的时候就已经非正式地把这个函数从C语言中开除了。若是要完成gets()函数的功能能够考虑使用fgets()函数,或本身写函数实现。 

  for(i=0,j=0;i<4;i++,j+=2)
   a[i]=(int)string[j]-48;
  1. 不管如何都应经过一个函数实现这段代码的功能。
  2. 那个“48”属于谭浩强流,应该写'0'。
  3. (int)string[j],似是而非。string[j]做为右值原本就是int类型。全部以右值参与运算的char类型数据事实上都是int类型。
  4. 这种写法的容错性弱爆了。哪怕输入为 1/8 + 3/8 【注: + 两侧有空格】 都会带来灭顶之灾。
  if(a[1]==0 || a[3]==0)
   printf("matherror\n");

  else if(string[3]=='-')
  {
   b[0]=a[0]*a[3]-a[1]*a[2];
   b[1]=a[1]*a[3];
   if(b[0]==0)
    printf("0\n");

   else if(b[0]>0)
     comd=comdiv(b[0],b[1]);
     else 
     { 
      comd=comdiv(-b[0],b[1]);
      printf("%d/%d\n",b[0]/comd,b[1]/comd);
     }
  }

  else 
  {
   b[0]=a[0]*a[3]+a[1]*a[2];
   b[1]=a[1]*a[3];
   comd=comdiv(b[0],b[1]);
   printf("%d/%d\n",b[0]/comd,b[1]/comd);
  }

  结构太差。应该

  if(a[1]==0 || a[3]==0)
  {
      printf("matherror\n");
      continue ;
  }
   
  if(string[3]=='-')
  {
                   /* …… */
  }
  else 
  {
                   /* …… */

}

 

  if(string[3]=='-')
  {
   b[0]=a[0]*a[3]-a[1]*a[2];
   b[1]=a[1]*a[3];
   if(b[0]==0)
    printf("0\n");

   else if(b[0]>0)
     comd=comdiv(b[0],b[1]);
     else 
     { 
      comd=comdiv(-b[0],b[1]);
      printf("%d/%d\n",b[0]/comd,b[1]/comd);
     }
  }

  else 
  {
   b[0]=a[0]*a[3]+a[1]*a[2];
   b[1]=a[1]*a[3];
   comd=comdiv(b[0],b[1]);
   printf("%d/%d\n",b[0]/comd,b[1]/comd);
  }

  这段代码若使用switch语句会更天然些。从实现功能的角度来讲,做者没有考虑到1/2+1/2这样的的情形,没有考虑到3/4+1/2这样的情形。因此这段代码是错误的。其余的大小毛病至关多,这里不一一指出了。主要的问题在于,原做者没有对数据进行很好的抽象,所以也就没法很好地组织代码;结构化程序设计思想没有获得完全的贯彻,迷失于在main()中纠结细节。 

续文:分数的加减法——C语言初学者代码中的常见错误与瑕疵(11) 

相关文章
相关标签/搜索