鸡窝里飞出伪凤凰
不少初學者在寫代碼時喜歡一main()到底——把所有的代碼都寫在main()函數中。代碼在作繭自縛的main()函數“迷宮”中像沒頭蒼蠅一樣左沖右突,寫到哪兒算哪兒。忙活了半天之后,終于成功“突圍”——可以運行程序了,然后就一臉輕松。但是卻把main()函數弄得凌亂不堪,一地雞毛,形同雞窩。
這種代碼是一種“爬行”式思維的產物,是人類思維的一種返祖現象。人類在學會直立行走之前就是按照這種方式思考的。俗話說,站得高才能看得遠。而匍匐在地上爬行,只能得到臟、亂、差的代碼。
這種代碼有一種變形,就是在一地雞毛的main()中可能會冷不丁地突然竄出一個令人眼前一亮的自定義函數調用,猶如一地雞毛的雞窩里仿佛要飛出一只金鳳凰似的。這樣的代碼多半出自那種半生不熟的新手,他們并不是自覺自愿地而往往是被強迫地使用一下自定義函數。這就使得代碼產生了一種滑稽的喜感,猶如一只小狗被要求站立起來作揖一樣,站立一秒鐘之后,很快就會重新伏到地上繼續爬行。下面的代碼就是一例:
#include <stdio.h>
int main( void )
{void inv(int x[],int n);
int i, a[10]={3,7,9,11,0,6,7,5,4,2};
printf("The original array:\n");
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
inv(a,10);
printf("The array has been vnverted:\n");
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
void inv(int *x,int n)
{int *p,temp,*i,*j,m=(n-1)/2;
i=x;j=x+n-1;p=x+m;
for(;i<=p;i++,j--)
{temp=*i;*i=*j;*j=temp;}
return ;
}
————譚浩強 ,《C程序設計》(第四版),清華大學出版社, 2010年6月,p241
首先來看main():
一進門就可以看到main()函數的頭上頂著一條條幅——不是“北京歡迎您”,而是函數類型聲明“void inv(int x[],int n);”。這樣做的效果是使得main()顯得更加凌亂,并且有讓main()函數獨霸inv()函數使用權之嫌。因為其他代碼中的其他函數若需要調用inv()函數還得重新進行函數類型聲明。這種不加思索地把函數類型說明隨便塞在某個地方的做法,絕不可能是出于事先周密思考權衡的結果,其原因估計多半只是為了對付編譯器的類型檢查而已。和下面的寫法對比一下,就不難發現函數類型聲明塞在函數內部的荒謬性:
void inv(int x[],int n);
int main( void )
{
/*……*/
}
顯然,后者不但能完全實現樣本代碼的功能,而且main()更清爽,此外代碼的其他部分若需要調用inv()也不需要無謂地再次寫函數類型聲明了。
再繼續往下看
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
一地雞毛!在main()這樣重要的地方忙活雞毛蒜皮是庸人最擅長的事情。其原因在于:思想一直是在代碼層面上爬行,而又缺乏代碼重構的意識。這是違背結構化程序設計原則的一個報應。結構化程序設計要求“自頂向下”地思考,要達到這個境界,前提是要“站直了,別趴下”。一直趴著絕對寫不出優雅,簡潔的代碼。優秀的代碼首先要站得高,其次很忌諱把事做“絕”。再往下看
View Code inv(a,10);前后都是一地雞毛,唯獨這個充分且簡潔的函數調用猶如金鳳一樣擺出了一副展翅欲飛的優美姿態。贊一個!再繼續看
View Code printf("The array has been vnverted:\n");for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
天哪!竟然寫出了和前面一模一樣的代碼!對于任何合格的程序員來說,這絕對是一種恥辱。這和車轱轆話來回說沒什么區別。這使得main()即使蒼蠅沒來下蛆就已經臟亂得腐敗不堪了。當然,有些人對此是不介意的。“不干不凈,吃了沒病”,你沒辦法對那些在垃圾桶里尋找食物的人說清楚什么叫做衛生。“不干不凈,吃了沒病”這種想法在程序設計界的翻版“只要程序能運行”,你同樣也無法和有些人說清楚代碼為什么應該簡潔優美,這是沒有辦法的事情。但是,如果把這種丑陋的惡習寫在教科書里,那就成了一種教唆,我個人認為應該判刑。因為這就和《衛生》課本里示范如何食用地溝油異曲同工。
再往后,就是那只從main()中飛出來的inv()函數的定義了。
{int *p,temp,*i,*j,m=(n-1)/2;
i=x;j=x+n-1;p=x+m;
for(;i<=p;i++,j--)
{temp=*i;*i=*j;*j=temp;}
return ;
}
首先,把“int *p,temp,*i,*j,m=(n-1)/2;”這幾個變量定義緊密地寫在“{”之后是一種令人作嘔的風格。此外,令人吃驚的是,實現如此簡單的功能,居然一口氣使用了5個“蒙頭蓋臉”的變量——你從名字上絕對不可能看出這些變量是做什么用的。這也是趴在地上思考的成果。由于事先缺乏充分且有高度的思考,就免不了“東一榔頭西一棒子”地濫用變量(反正變量不要錢)。同樣是由于缺乏縝密的思考,所以使用這些變量只是由于一時興起,事后代碼作者自己恐怕也弄不清楚究竟這些變量的真正含義。實際上根本不需要這么多的變量。
首先來看p,它的作用僅僅是用來表示x+m這個值,顯然毫無必要,代碼完全可以寫成
{
int temp,*i,*j,m=(n-1)/2;
i=x;j=x+n-1;
for(;i<=x+m;i++,j--)
{temp=*i;*i=*j;*j=temp;}
return ;
}
再看m,它只是記錄了(n-1)/2這個在代碼中從沒有改變過的值而已,因而也沒有必要。
View Code void inv(int *x,int n){
int temp,*i,*j;
i=x;j=x+n-1;
for(;i<=x+(n-1)/2;i++,j--)
{temp=*i;*i=*j;*j=temp;}
return ;
}
現在對代碼走查一下,假設n的值為3,那么i,j的變化情況為:
i??????????? ?j??????????
x????????? x+2????????
x+1?????x+1
不難發現,當i變化成x+1時,程序進行了一次毫無意義的交換。這說明“i<=x+(n-1)/2”這個表達式不但在寫法上過于啰嗦,在邏輯上也很非常蹩腳。實際上只要簡單地?????????
{
int temp,*i,*j;
for(i=x,j=x+n-1 ; i < j ;i++,j--)
{temp=*i;*i=*j;*j=temp;}
return ;
}
就可以了。
最后,
不但風格上奇丑無比,而且同樣犯了把事做“絕”的毛病——絕則錯。
所以,盡管從main()這個雞窩里飛了出來,但inv()只是一只“偽”鳳凰而已。
最后,對這段代碼重構如下:
?
?
總結
- 上一篇: C#在线获取歌词(转)
- 下一篇: SmartQuery WebPart 2