#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; } 復制代碼 評析 這是另一位初學者給出的代碼。 這段代碼貌似更簡練一些,但也有很多毛病。 總體 總體結構上偷工減料,貪小便宜(省寫函數類型聲明),且次序不當(把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的循環體內定義。 char op; 這個沒什麼問題,op顯然是operantion的縮寫。 while(scanf("%d/%d%c%d/%d",&a,&b,&op,&c,&d)!=EOF) 這個scanf()函數調用的轉換格式——"%d/%d%c%d/%d",或者叫“匹配格式”設計得不好。 如果要嚴格滿足題目中“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"。 對於"%d/%d%c%d/%d"來說, 1 / 8 + 3 / 8 這樣的輸入最多只能轉換一個"%d",即正確輸入那個1。而對於"%d / %d %c %d / %d"來說, 1 / 8 + 3 / 8這樣的輸入能完全地正確轉換(甚至多加幾個空白字符也沒關系)。 這是因為對於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.) 一般情況下,在scanf()的轉換控制字符串中應該不寫任何white-space character或非white-space character(怎樣利用scanf()函數自虐 ),但是凡事都有例外。 temp1=b*d/gcd(b,d); 急躁。急吼吼地就開始處理公共細節。 粗心。顯然沒考慮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)