分數的加減法——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) 

相關文章
相關標籤/搜索