分數的加減法——C語言初學者代碼中的常見錯誤與瑕疵(11)

前文連接:分數的加減法——C語言初學者代碼中的常見錯誤與瑕疵(10)

原代碼2

#include <stdio.h>
#include <string.h> int abs(int a) { return a>0?a:-a; } int gcd(int a,int b) { if(a<b) a^=b^=a^=b; return b?gcd(b,a%b):a; } int main() { int a,b,c,d,temp,temp0,temp1; char op; while(scanf("%d/%d%c%d/%d",&a,&b,&op,&c,&d)!=EOF) { temp1=b*d/gcd(b,d); if(op=='-') temp0=a*temp1/b-c*temp1/d; else temp0=a*temp1/b+c*temp1/d; temp=gcd(abs(temp0),temp1); temp0/=temp; temp1/=temp; if(temp0&&temp1==1) printf("%d\n",temp0); else if(temp0) printf("%d/%d\n",temp0,temp1); else printf("0\n"); } return 1; }

評析

  這是另外一位初學者給出的代碼。
  這段代碼貌似更簡練一些,但也有不少毛病。html

整體


  整體結構上偷工減料,貪小便宜(省寫函數類型聲明),且次序不當(把main()放在後面)。 程序員

#include <string.h>

   這是有魚沒魚先撒一網。實際上多餘寫這條,由於根本就沒用。程序員應該知道什麼叫Occam's razor:Plurality should not be posited without necessity.(拉丁文表述是:Pluralitas non est ponenda sine neccesitate.)。函數

main()


 int a,b,c,d,temp,temp0,temp1;

  a,b,c,d這幾個標識符過於庸俗,不過考慮到直接來自於題目,彷佛也不便深責。
  temp,temp0,temp1實在是太差了,無厘頭。並且徹底不該該定義在這個位置,應該放在while的循環體內定義。 post

 char op;

   這個沒什麼問題,op顯然是operation的縮寫。url

while(scanf("%d/%d%c%d/%d",&a,&b,&op,&c,&d)!=EOF)

  這個scanf()函數調用的轉換格式——"%d/%d%c%d/%d",或者叫「匹配格式」設計得很差。spa

  若是要嚴格知足題目中「a, b, c, d是一個0-9的整數」的要求,這個格式應該寫爲"%1d/%1d%c%1d/%1d"。更投機取巧的寫法能夠把那個正負號視爲c的一部分:"%1d/%1d%2d/%1d",但這僅僅在加減法時才適用,並不具有通常性。設計

  若是但願程序更「寬容」一些,好比能接受輸入流中合情合理的空格,例如 1 / 8 + 3 / 8,這個轉換格式應該設計爲"%d / %d %c %d / %d"。code

  對於"%d/%d%c%d/%d"來講, 1 / 8 + 3 / 8 這樣的輸入最多隻能轉換一個"%d",即正確輸入那個1。而對於"%d / %d %c %d / %d"來講, 1 / 8 + 3 / 8這樣的輸入能徹底地正確轉換(甚至多加幾個空白字符也不要緊)。htm

  這是由於對於scanf()來講,轉換格式中的空白字符(white-space character)的意義是讀至第一個非white-space character。(A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read. The directive nev er fails.)blog

  通常狀況下,在scanf()的轉換控制字符串中應該不寫任何white-space character或非white-space character(怎樣利用scanf()函數自虐 ),可是凡事都有例外。

  temp1=b*d/gcd(b,d);
  1. 急躁。急吼吼地就開始處理公共細節。
  2. 粗心。顯然沒考慮b或d爲0的狀況。
  if(op=='-')
   temp0=a*temp1/b-c*temp1/d;
  else
   temp0=a*temp1/b+c*temp1/d;

  顯得有點笨重。要是我也許會這樣寫

 if(op=='-')
      c = - c ;
 temp0=a*temp1/b+c*temp1/d;
  temp=gcd(abs(temp0),temp1);

  求絕對值這種小事情仍是在gcd()函數定義內部完成爲好。寫在這裏過於突出細節,影響主要思想的表達。 

  if(temp0&&temp1==1)
   printf("%d\n",temp0);
  else if(temp0)
   printf("%d/%d\n",temp0,temp1);
  else
   printf("0\n");

  這段代碼顯然應該抽象爲一個函數,寫在這裏顯得拖泥帶水。 

 return 1;

   這個奇葩。應該

 return 0;

 abs() 


int abs(int a)
{
 return a>0?a:-a;
}

  這函數的名字顯然有問題,不該該取這個名字,由於有一個庫函數就是這個名字(聲明於stdlib.h中)。固然這樣用也不是不能夠,但其中涉及到一系列複雜的語言規則。瞭解這個規則的人雖然知道本身的函數能夠與庫函數重名,但卻歷來不會這樣用。我相信這位初學者是不知道這個規則的,他(她)這樣寫是瞎貓碰到死耗子而已。 

int gcd(int a,int b)
{
 if(a<b)
  a^=b^=a^=b;
 return b?gcd(b,a%b):a;
}

   這個函數是錯的。

 if(a<b)
  a^=b^=a^=b;

  這裏有兩個問題。第一,a^=b^=a^=b這個表達式是未定義行爲(參見,爲「a+=a-=a*a」預擬的悼詞「牙里長嘴」和「a+=a-=a*a」 )。第二,從後面的代碼來看,這裏根本就不必給a、b排序。(參見拙著《品悟C》第五章 多此一舉 問題12「不完全的思考」,p152)

續文:分數的加減法——C語言初學者代碼中的常見錯誤與瑕疵(12)

相關文章
相關標籤/搜索