程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 分數的加減法——C語言初學者代碼中的常見錯誤與瑕疵(11)

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

編輯:關於C語言

#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)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved