品悟C——拋棄C程式設計中的謬誤與惡習

《品悟C——拋棄C程式設計中的謬誤與惡習》是2012年8月31日出版的圖書,作者是薛非。本書集中討論C程了序設計中常見錯誤,其主旨在與更好的幫助讀者從錯誤中更準確地理解C語言,並在實踐中更好地運用C語言。

圖書簡介

品悟C——拋棄C程式設計中的謬誤與惡習 品悟C——拋棄C程式設計中的謬誤與惡習

全書分為上下兩部:上部“形而下學”討論C代碼中常見的錯誤和編程惡習;下部“形上學”討論的是對C語言本身認識方面的常見誤區。這些問題特別具有普遍性,很多錯誤不僅僅存在於初學者之中,即使是不專業的程式設計師多半也會在本書中找到自己的錯誤。因此,對於任何想糾正自己對C語言的錯誤認識以及想不斷提高C語言水平的讀者來說,本書都提供了一種獨到且重要的視角,是一本具有一定參考價值的技術資料。 本書並非那種長篇大論環環相扣的書籍,它的各個主題獨立成篇,使人閱讀起來輕鬆愉快。讀這本書並不需要正襟危坐在書房裡,它可以隨時隨地地拿過來翻閱。 本書可作為各高校C語言或相關課程的教學參考書,也適合那些具有一定經驗的C語言程式設計師閱讀使用。

圖書前言

編者序——敢於向權威挑戰
經常聽到我的作者朋友說類似這樣的話:“看我的代碼,多帥氣!”何謂帥氣、漂亮的代碼?
在我看來,先不談代碼醜俊,首先代碼必須沒有殘疾。在健康的代碼基礎上,才能談網路間流傳的各種稱號:漂亮代碼、優質代碼、帥氣代碼、性感代碼……
這就是本書要講的內容——摒棄一些錯誤的觀念,重新認識C語言的嚴謹與華麗。
計算機語言是人類操控計算機硬體的主要手段,是語言創造者智慧的濃縮。計算機語言在中國的普及,是伴隨著中國經濟騰飛的這幾十年。無數中國的程式設計師伴隨著很多經典教材順利入門並成長了起來,隨著網際網路的發展,更多的朋友在網上發表自己的看法,所以網際網路也成為了大家獲取編程技術的重要渠道。
但是,金無足赤,隨著C語言標準的不斷完善,C開發理念的不斷進步,市面上的一些教材和網上一些說法確實存在一些值得商榷的問題,或者謬誤,因為這些資料傳播甚廣,有些反而成為了“標準”,這種現象有時候可能會誤導初學者,讓初學者無法從一開始就建立相對準確的開發觀念。
本書就是這樣一本敢於向“權威”挑戰的書,通過作者多年的積累,以及眾多熱心網友的鼎力支持,對一些C語言中經常出現的問題進行討論。
在中國的教育體制下,不可否認,很多創造力被限制甚至扼殺,很多路被強制標註為正確途徑,很多人的學習興趣在一開始就被磨滅……。但是很高興能出現這么一本書,在閱讀過程中,可以體會一眾先鋒程式設計師對開發觀念的重視,對細節的精益求精,對邏輯的反覆推敲,甚至上升到——對真理的求索。
我通常拿到一本書,會先瀏覽一下,把我認為重要的文字用特殊形式標出來,比如“提示”、“注意”、“TIPS”之類的樣式以及文字加粗、代碼加黑之類的重點標註,但是本書我在做出努力之後,發現無法進行這樣的處理,因為似乎每句話都是重點,每段代碼都是精心挑選的,無論是正確代碼還是錯誤代碼。
最後一點不得不提一下,作者的文筆非常犀利,對一些問題的批判毫不留情,可能是作者本人以及相關參與編寫的網友受到過太多問題資料的影響,以至於走了不少彎路,所以面對問題資料,個別時候會流露出來一種憤青一般的憤怒。這可以理解,而且我在編輯加工過程中也儘量保持了文風,但是還是希望作者和所有讀者做一名淡定的程式設計師。
如果本書冒犯了某些朋友,請不要大動肝火,可以發郵件與作者討論,畢竟從一個角度看問題,存在片面的可能性。大家討論技術,以代碼會友。

權威之所以是權威,必然有其可取之處,對於權威,我們除了要有挑戰的勇氣,更多的是要理性的尊重,畢竟“權威”帶領太多人進入C語言門檻。沒有BUG的代碼是不存在,同樣沒有問題的資料也是不存在的,希望大家在學習過程中,多思考,多交流,早日樹立端正的開發思路。
在此感謝包括作者在內的熱心人,有監督才會有活力,有挑戰才會有發展!
前言

這是一本什麼樣的書
這是一本關於C 程式設計常見錯誤的書。書中詳盡地剖析、討論了學習或使用C 語言過程中常見的一些錯誤觀念和錯誤實踐。目的是幫助讀者更準確地理解並運用C 語言。
為什麼要寫這樣一本書
因為錯誤①與程式設計一向如影隨形,密不可分。只要編寫程式,就無法避免錯誤。這是一條鐵律。無論是初出茅廬的新手還是經驗豐富的老手,無一例外。從某種意義上來說,軟體業的主要工作有三項:製造錯誤、改正錯誤和掩飾錯誤。因此不深入了解和認識錯誤就不可能真正懂得編程。
孔子說:“未知生,焉知死?”程式設計也是這個道理,不懂得錯誤就不可能真正理解程式設計語言的要點及編程的真諦。實踐表明,人們從錯誤中學習到的東西往往比從正確中學到的多得多。這就是人們常說的“吃一塹,長一智”。
事實上每個程式設計師的成長曆程都是一部不斷認識錯誤並予以改正的糾錯史。每一個
優秀的程式設計師無一不是從無數的錯誤中脫穎而出的,並且始終要與錯誤進行不懈地斗
爭。閃光的思想一向少不得經歷錯誤的磨礪。
從這個角度來看,對程式設計中的錯誤進行討論比那些一本正經地正確敘述更有助於程式設計人員水平的提高,本書的目的就在於此。
然而儘管市場上C語言的書籍多得可以車載斗量,但遺憾的是幾乎鮮有專門論述C程式設計中錯誤的書籍,除了Koenig所著的《C陷阱與缺陷》。不過我覺得作者Koenig先生“闡之未盡,我有我的看法”。
實際上Koenig先生並不了解中國國情,他不知道程式設計中的錯誤其實分為兩種,一種是錯誤,另一種是中國式錯誤。一句話,他根本無法想像我們是用質量何等奇特的教科書、用何等落後的方式學習C編程。因此他的書只提到了普通的一般性錯誤,而對形形色色的具有中國特色的錯誤卻隻字未提。這是一個巨大的、需要填補的空白(我不知道這算不算學術空白,從某種意義上來說這更像是清除技術污染)。這項工作目前來看還沒有人做。既然古代的聖賢曾經教誨過我們應該“當仁不讓”,所以現在只好由本書來勉為其難了。
錯誤是怎樣“煉”成的
眾所周知,所謂編程在本質上無非是用特定的形式語言向計算機描述一個問題的解
①錯誤有很多文雅含蓄或直截了當的稱呼,比如:缺點(defect),偏差(variance),故障(fault),失敗(failure),問題(problem),矛盾(inconsistency),錯誤(error),特殊(feature),事件(incident),缺陷(bug),異常(anomaly),不良反應(idiosyncrast),崩潰(breakdown)……,不一而足。本書後面通稱為錯誤或BUG。

決方法。在此之前,對這種語言的學習也是一個必不可少的環節。在整個過程中的一系列環節的任何一個環節上都可能會出現錯誤。
首先,語言的學習過程中會產生許多錯誤。這些錯誤不僅是因為學習者自身的原因,同時也是由於教科書方面的原因。
許多虔誠的學習者內心的潛意識裡會以為教科書不會有什麼錯誤,他們一向跪在教科書上學習,遇到錯誤時只是不斷地檢討自己。在這個連嬰兒奶粉都可能含有三聚氰胺的社會,懷有這種念頭實在是too.naive了。沒有任何理由指望你的教科書比嬰兒奶粉更純淨。
教科書同樣可能有錯,這種“水源”的污染才是最嚴重的污染。在這種情況下,只查找自身出錯的原因是不可能解決問題的。這種先入為主的錯誤非常難以糾正,而且其影響往往也最為深遠。例如,很多人在成為職業程式設計師之後也還不清楚
a+=a‐=a*a.
這個表達式是不正確的。
對語言不夠熟悉是初學者犯錯誤的另一個主要原因。通常表現為代碼里不是缺點兒什麼就是多點兒什麼。這種錯誤是一種最初級的錯誤,連編譯器能夠指出,因而比較容易得到糾正,所以多半不足為慮。
學習一種語言,本質上就是為了學習一種新的思維方式。將平時不嚴謹的自然語言思維習慣帶入程式經常會導致對程式設計語言的誤解,例如把“x不等於1或2”錯誤地表達為
x!=1||x!=2
甚至我們所熟悉的嚴謹的數學語言,由於語境發生了變化,也會在程式中帶來錯誤。例如,很多人都犯過把“==”(“等於”)寫成數學中的“=”(“賦值”,在數學領域表示“等於”)這種錯誤。這種錯誤一般需要通過長期的自覺訓練來糾正。
往往被初學者所忽視的是不良編程習慣的問題,他們一心只想著寫正確的代碼,而從不考慮如何正確地寫出正確的代碼。這往往會導致許多低級錯誤一犯再犯,比如一再出現括弧不成對這樣的問題。
缺乏良好的編程習慣是寫不好程式的,任何一個工人都懂得:良好的產品質量需要由合理的工藝來保證。不懂得正確地編寫程式就幾乎不可能寫出正確的程式。
初學者忽視不良習慣的另一個主要原因是,這些編程陋習有時並不能在短期內顯示出其惡果,比如“int.a,b,c;”這種爛得不能再爛的變數名。實際上良好編程習慣的意義不僅在於避免錯誤,還在於它意味著少犯錯誤,或者在出現錯誤時很容易檢查到並修正,這些長遠的利益在短時間內一般是體會不到的。
良好的習慣要靠自覺培養,但是如果教科書沒告訴你這些,那是教科書的問題。因為初學者不可能在短時間內完全靠自己的悟性領會前人幾十年積累下來的編程規範方面的經驗。順便說一句,有些教科書的代碼風格本身就極濫無比,對初學者誤導很大。
僅僅熟悉語言元素對於編程來說是遠遠不夠的,因為每一個程式的目標都是為了解決問題。而從問題到程式要經歷問題的提出、問題的分析到問題的解決,最後落實為代碼,在這一過程中每一個步驟都可能會出現錯誤。
正確地提出問題是解決問題的先決條件,錯誤的問題是不可能解決的。例如,要求“輸出1~n*n的自然數構成的魔方陣”就是一個錯誤的問題,因為2階的魔方陣根本不存在。求解這樣的問題,可以說是未解先錯。
問題提出模糊不清的要求也是一種錯誤,例如“有三個數a、b、c,要求按大小順序把它們輸出”,這裡的三個數究竟是什麼數,是模糊不清的,在不同的情況下代碼截然不同。要求應該明確。要求不清是提出者的錯誤,但擅自猜測問題的要求則是程式設計師的錯誤。
編寫求解錯誤問題或不嚴謹問題的程式有害無益,而且貽害無窮,這會使程式設計師在不知不覺中喪失必要的職業嚴謹,對錯誤的需求漸漸變得麻木不仁。要知道,在真實的開發中,因為軟體需求錯誤而導致的軟體錯誤占到錯誤總數的一半以上,而且那些錯誤的需求往往不那么清楚明顯、容易察覺。不具備審視問題或要求是否合理的習慣和能力,在真正的軟體開發中就如同盲人騎瞎馬,不可能不掉到溝里。
初學者中很少有人意識到問題本身可能就是錯誤的。很多初學者常常不管三七二十一地解決錯誤的問題。這對他們的邏輯思維能力是一種巨大的慢性戕害。
即使正確地提出了問題,也還可能被程式設計者所誤解。錯誤地理解問題的要求,同樣是錯誤產生的一個重要原因。
分析問題的過程中最容易產生邏輯錯誤,或者對問題分析得不全面導致的顧此失彼。這種不全面的邏輯漏洞往往比完全的邏輯錯誤更難發現,因為這時程式的運行有可能會“顯得”是正確的。
即使沒有前面提到的那些錯誤,距離寫出優秀的代碼還有很長的路。寫代碼和寫文章一樣,需要精心地進行布局謀篇,用術語來說就是進行設計。
設計不僅包括算法設計還包括對數據結構的設計。在“算法——程式的靈魂”這種片面觀點的誤導下,很多人輕視數據結構的設計,冥思苦想所謂的算法。實際上算法和數據結構是密不可分的,那些艱深晦澀的糟糕算法,大多是因為垃圾的數據結構設計。
有些人寫程式毫無設計意識,上來就開始匆忙寫代碼,東一榔頭西一棒槌,寫到哪算哪,寫出的代碼笨拙無比、僵硬造作、邏輯混亂、囉唆重複或者效率低下。這樣的代碼即使能輸出結果,也是不合格的醜陋代碼。
難道程式長得醜陋也是一種錯誤嗎?是的,作為一種藝術,程式有自己的美學標準。從微觀上看,這種美感表現為充分體現語言的優美和風格的簡潔;從巨觀來看則表現為一種層次清晰和條理分明。這種標準並不出於純粹的審美,也有其功用價值——不容易出錯,發生錯誤容易改正。而要達到這一目標,程式設計實踐過程離不開正確的哲學指導,這種哲學的一個核心理念就是結構化程式設計思想。
至此,儘管還有很多種類錯誤沒有談到,但是已經不難得出結論,程式設計錯誤很多。需要始終對錯誤保持警惕。
應該如何對待程式設計中的錯誤
首先,不要懼怕錯誤。錯誤儘管繁多,但只要了解認識錯誤就可以改正、避免錯誤。最可怕的是對錯誤的無知與鴕鳥般的無視。認識錯誤是改正錯誤的前提。

其次,了解錯誤越早越好,了解得越多越好。了解得越早進步得越快,了解得越多進步越大。
第三,錯誤也是一種財富。既然從錯誤中積累經驗是提高程式設計水平的必經之路,那么從他人的錯誤中吸取教訓則是一種提高程式設計水平的捷徑,因為所謂捷徑無非就是少走彎路或不走彎路而已。
本書的主要內容及特點
本書匯集了大量C語言的錯誤和編程陋習,逐個予以詳細討論,這些錯誤主要取自國內發行量和影響力較大的C語言教科書及其他一些非教科書C語言書籍以及網路上比較流行的觀點,主要包括□□□□□(為節約篇幅,此處刪去286個字),也有一些則是C語言本身的一些不易理解的特點以及學習者不具備良好的編程習慣所導致的錯誤。應該說本書所舉出的反面例子非常具有代表性。
本書分為上下兩部,上部“形而下學”討論代碼中的常見錯誤和不良風格,下部“形上學”討論的是很多人對C語言持有的錯誤觀點和認識。
書中各個小節基本上是互相獨立的,因此讀這本書並不需要循序漸進,可以翻到哪頁讀哪頁。
什麼人應該及什麼人不應該看這本書
對於使用國內教科書學習C 語言的人來說,無論是初學者還是職業程式設計師,這本書應該是很有價值的參考讀物。因為本書的內容恰恰是教科書不講的內容或恰恰是教科書講錯了的內容。
如果是為了應付什麼等級考試之類的話,就不需要浪費錢財了。看了此書你可能會發現那些考試本身也有很多錯誤。
閱讀指南
如下形式,標註為“樣本”的虛線框中的內容,是採樣自教材或者網際網路上值得商榷的說法或者代碼。

如下形式,虛線框中的內容,是存在問題,或者說是不那么完美的說法或者代碼。

如下形式,無虛線框中的內容,是沒有問題的優質代碼或相對準確的說法。
#include <stdio.h>
int main( void )
{

char str1[5],str2[5],str3[5];
str1[4]="X";
str2[4]="X";

……
雖然為寫這本書花了近三年的時間,但最後完稿時,我發現這段時間依然顯得非常短促。因此儘管這是一本討論錯誤的書,但是毋庸諱言,它本身可能也免不了存在錯誤。您若在閱讀時發現任何錯誤或不妥之處,歡迎與作者聯繫並通知作者:這裡先謝了!

致謝
本書的寫作過程中得到了很多人的幫助和支持,在此作者表示深深的感謝。特別感謝starwing83網友的一貫支持和幫助,他對程式設計語言的深刻理解和豐富的實踐經驗不但讓筆者受益匪淺,而且也使本書在許多方面得到了提高和充實。本書的第11章問題28、第18章問題10和第19章問題1為starwing83網友所撰寫。特別感謝OwnWaterloo網友的一貫支持和幫助,他對標準的準確理解和對程式設計深刻的洞悉解開了筆者的很多疑惑,使作者在很多問題上都茅塞頓開。第5章“問

題19”為OwnWaterloo網友所撰寫。
特別感謝幻の上帝網友,在多年的共同探討中,他熱情而無私地給予作者很多支持、幫助和啟迪。本書的很多觀點受惠於幻の上帝網友的啟迪,他還為本書收集了很多資料,為本書做了許多翔實的考證工作,並糾正了作者的很多錯誤。
特別感謝諸多網友在長期的討論中所給予的道義支持、精神鼓勵和技術幫助,他們是:變異老鼠、shan_ghost、蔡萬釗、狗氣球、狗蛋、三月二十七、tempname2、x5miao、madoldman、walleeee、ChiyuT、davelv、huangzhenfan、花瓣雪、一介村夫、wait_rabbit、MMMX、rover12421、良化綱領_、assiss、gccer、8pm、supermegaboy、noword2k、hahajerry007、ztz0223、Ray001、fera、unixlinuxsys、cokeboL、koolcoy、sh19871122、群雄逐鹿中原、fender0107401、jerryz920、hellioncu、blueheavenljn、x2、lylesong、supersuper8、gaara99、Kabie、pkkj、rossini23、wolfkin、milujite、weixuejun、cnhbdu、yesBSD、kouu、chinesedragon、water_wf、gz80、toniz、eminem112、sunjiakuang、airjordanforce、yumaofsj、yug1129、campuspuzzle、www1862、zxrjkl、peijue、jhzhu_snps、x75yan、zhaohongjian000、tinysniper、xxwpk007、geel、字母二十六、奶茶dsk、abc976031617、rain_fish、amarant、財版、沒本、grand508、star1983653、bill15、makeit、liexusong、pandaiam、txg531、kingwolf520、梅川內依酷、bukkake、wuxb45、hobbs136、cjaizss、greensnow、jhui66、luojiannx、耐心學習、xiaobenniao514、L_kernel、geruihai、erlangs、 CUXXXCU、ip200、Hongqiyaodao、dglwx、jhinux、xwxfirst、timesu、srdgame、septem776、liupingforarm、kellenforever、192redwolf、cobras、small_bee、gyarenas、jack4010、startn、rainysky、jimmyixy、litanhe、rubyish、Demon—Hunter、wuliming_sc、bigxu、xfoucs、時尚農民、yangxue1206、hukb_cu、jnbxzl0200157、tianshx、sukora、ux400、craneflyfly、autoasm、時間看來、Jokday、asuka2001、namiii、jeung、btdm123、lilery、wangzhen11aaa、nketc、KanonInD、ybh37、dahan16、digdeep126、keytounix、光明‐使者、gtv、SoforthHe、djsxut、oilgeo、呆呆的等候救贖、hbmhalley、china_ssl、solu、dajiangyou77、wl85125771、魔獸_LOVER、0xC1988、inzahgi、武林萌豬、lilinly225、wgm001、royalzhang、千年老狼、musezh2、gooderfeng、HAL9000、Jack_Jack、大石頭、萬倉一黍、Ivony、愚溪、nscboy、minvt、underuwings、Artech、ini_always、小彬、xujif、LeonSharp、劉博平、linyilong、浩然正氣89、backag、happycat1988、ygcao、rhs、Zlinux、小_金_魚、泡泡騰、SongSharp、szwe、eeeyes、+v、linxr、andreas、冠吸柏汁霆瘋、九原山人、火禾、luotong、ProgrammerK、hachihe、rockyoung、czcz1024、Edgar.Wang、magicDict、hoodlum1980、空明流轉、隨風浪跡天涯、不死鳥之魂、木耳、木野狐(Neil.Chen)、散客游、sunriseyuen、chasefornone、飛浪、glshader、Credo、wcut、houqidian、Parry、xiekun605746、哥哥.Net、Virus‐BeautyCode、莊金峰、我想我是風、MienNg、soap、greenhand2008、Homer_Simpson、zsea、鎏、ctou45、shamo0303、gussing、eflay、王小兵、l23cy、winter‐cn、Lee.Kevin、陽光明媚Ryan、地獄鬥神、richardzeng、pulihe、寒靄、cncolder、陳玉國、是否存在、key.yao、codebumb、☆凱子、darklx、草珊瑚、鶴沖天、Jake.Lin、Lithium、乾拔三分、leizisdu、zzuxiaolei、BitSky、Jeffrey.Zhao、桀驁的靈魂、海南.
胡勇、sm11e、我寫的不是代碼是寂寞、lzyzizi、肖璟、嗷嗷、木+頭、zy498420、№完成、五星、Simon‐Zhu、BeckFun、Tony.Zhou、lxlylm、陳梓瀚(vczh)、zdd、xchat、frank_hust、漂浮的雨、六芒星、MagicHu、天方、深藍流沙、viperchaos、swfc_qinmm、瀟湘雨歇、riccc、Jeff.Wong、wincss、reavics、weiwelcome0、Muse、lx458004975、鏡美如、volcanol、路過秋天、徐少俠、南京.王清培、諾貝爾、藺燕梅……這個名單可能有所遺漏,在此向被遺漏的網友表示歉意。
特別感謝ChinaUnix論壇,在這個論壇的《以其昏昏使人昭昭》帖子中我完成了這本書的主要素材的積累。這個論壇良好的技術氛圍使得本書素材的積累得以順利進行。
特別感謝部落格園,作者在這個論壇發表的《C解毒》系列博文是本書的部分內容或草稿。在這個論壇得到了很多網友有益的反饋。
本書在寫作過程中參閱了大量網上技術資料,從中得到了很多啟發,鄭重向這些作者表示謝意。
一位朋友根據《以其昏昏使人昭昭》帖子整理了一份文檔,給本書的寫作帶來了極大的便利和幫助,在此深深表示感謝。
薛非

圖書目錄

上部 形而下學——代碼錯誤及陋習
第1章 低級錯誤 3
問題1 C啊,多少C++假汝之名而行——C、C++不分 4
問題2 環節缺失——關於C程式開發過程 7
問題3 錯誤的“標點”——混用中英文字元 9
問題4 錯誤的“單詞” 11
問題5 “合併單詞”產生的問題 12
問題6 形形色色的“分號”誤用 13
問題7 “一仆二主”——兩個main()函式 15
問題8 忘記“&” 17
問題9 who’s who 18
問題10 同形異質——為符號常量賦值 19
問題11 詞不達意 20
問題12 使用switch語句的常見錯誤 21
第2章 初級錯誤 23
問題1 隨手寫出的大數 24
問題2 關於數據類型的潛規則 25
問題3 使用垃圾值 26
問題4 對數組的誤用與誤解 28
問題5 好心辦壞事——修改代碼引發的錯誤 30
問題6 “右移運算等價於除法運算” 32
問題7 “中國人民很行”——C語言中的錯別字 33
問題8 學會數數——循環次數錯誤 34
問題9 忽視函式原型36
問題10 張冠李戴——錯誤的格式轉換 37
問題11 只進不出——缺少輸出 39
問題12 鳩占鵲巢——數組越界 41
第3章邏輯錯誤45
問題1 喧賓奪主——換行問題 46
問題2 結構體鍊表的一個例題——輸出錯誤 47
問題3 一些幼稚的寫法 50
問題4 c=getchar()!=EOF——優先權錯誤 50
問題5 判斷三個整數相等——“==”運算符的誤用 51
問題6 懸掛else問題——怎樣寫出正確的if語句52
問題7 浮點循環變數 53
問題8 不顧常識 55
問題9 自然語言的陷阱 57
問題10 c==" " || '\n' || '\t' 58
問題11 亂做習題你傷不起啊 59
問題12 誤改循環變數 62
第4章 似是而非 65
問題1 void main() 66
問題2 生硬的嫁接,滑稽的實參66
問題3 輸出之誤——輸出數組應注意的問題 70
問題4 多此一舉 73
問題5 笨拙的for語句 74
問題6 只用printf()函式輸出 76
問題7 容易招致編譯器誤解的代碼 76
問題8 使用“//”注釋注意事項 77
問題9 使用野指針78
問題10 用驢子拖寶馬——濫用結構體 79
問題11 似是而非的k=sqrt(n) 84
問題12 scanf("%s",&str) 88
問題13 已知兩邊長求三角形面積——不完全函式參數 89
問題14 對“c”轉換說明符的誤解 92
問題15 多餘的計算 93
問題16 到處忙活與一勞永逸——函式類型聲明位置問題 96
問題17 半身不遂和粗中有細 99
問題18 忽視前提 103
問題19 char text[3][80]不是一篇文章,也並非3行文字 104
問題20 不安全的gets()函式 108
問題21 檔案名稱殘缺 109
問題22 exit(0) 110
問題23 捨近求遠 112
問題24 重複無效的代碼 114
問題25 拙劣的外部變數114
第5章 畫蛇添足 129
問題1 KISS 130
問題2 為賦新詞強說愁 131
問題3 囉唆重複 132
問題4 表達式是什麼 133
問題5 不識指針 134
問題6 脫褲子放屁 136
問題7 怎樣利用scanf()函式自虐 138
問題8 自尋煩惱的scanf()函式調用方式 139
問題9 輸入指定範圍的整數——Basic風格寫法和C風格寫法 140
問題10 “豆漿買兩碗,喝一碗,倒一碗”——多餘的數組元素 143
問題11篩法“四不像” 146
問題12 不徹底的思考 152
問題13 代碼寫得要“拽”(DRY) 156
問題14 作繭自縛的“%3d” 158
問題15 大腹便便——臃腫與重複 160
問題16 舍簡就繁 161
問題17 程式的劣化與最佳化 163
問題18 濫用變數綜合症 172
問題19 free空指針 174
第6章 疑難雜症 177
問題1 混亂是怎樣煉成的 178
問題2 無知亂吃藥——strcpy()函式的誤用 183
問題3邊界測試——讓BUG現形 186
問題4 內褲外穿——錯位及不倫不類 192
問題5 flag標誌:代碼餿了(一) 196
問題6 flag標誌:代碼餿了(二) 202
問題7 flag標誌:代碼餿了(三) 210
問題8 雞窩裡飛出偽鳳凰 216
問題9 不顧常識 221
問題10 不易察覺的BUG 222
問題11 含糊之過、多做之過及亂做之過 224
問題12 怎樣調戲程式 230
問題13 糟蹋好題——魔方陣問題 236
第7章 風格問題 249
問題1 如何進行數組初始化 250
問題2 括弧不配對錯誤及如何免疫 251
問題3 不良代碼編輯風格引起的錯誤——怎樣寫控制語句252
問題4 將main()函式進行到底 253
問題5 赤裸的scanf() 257
問題6 c + 32的問題 257
問題7 怎樣整理房間 259
問題8 貪小便宜——省略函式類型聲明等問題 262
問題9 常數Magic Number 265
問題10 搏二兔 269
下 部 形上學——概念錯誤及誤區
第8章 概念的缺失 277
問題1 C語言的歷史及C標準 278
問題2 只知變數不知對象 278
問題3 C語言中Byte的含義 280
問題4 “不知所云”的無定義行為(undefined behavior) 281
問題5 “可以清心也”應如何解讀——不確定行為
(Unspecified behavior) 283
問題6 實現(Implemantation) 285
問題7 “編譯器自己做主”——實現定義的行為
(implementation-defined behavior) 285
問題8 整數提升(Integer promotion) 285
問題9 副效應(side effects) 286
問題10 “左值就是可以出現在賦值運算符的左側” 287
第9章 基本詞法 289
問題1 字元集問題 290
問題2 “α,β,δ,ε,Ⅰ,Ⅱ,Ⅲ,Ⅳ等不是C語言的合法字元” 291
問題3 “各種字元集的基本集都包括了127個字元” 291
問題4 C源程式的組成——“單詞”視角 292
問題5 “a”、‘a’與a辨析 293
問題6 “C語言關鍵字都是小寫的” 294
問題7 “C語言允許一行內寫多個語句” 294
問題8 “標識符由字母、下劃線和數字組成” 295
問題9 標識符的長度限制 295
問題10 “括弧、賦值、逗號都是運算符” 296
第10章 數據類型及類型轉換297
問題1 “類型就是對數據存儲單元的安排” 298
問題2 1.%3之惑 298
問題3 “int為2個位元組或4個位元組” 299
問題4 int類型的範圍問題 299
問題5 混亂不堪的“整型” 300
問題6 “?345是整型常量” 300
問題7 “字元數據以ASCII碼存儲” 301
問題8 “char類型就是signed char類型” 301
問題9 通過程式測定char類型的性質 302
問題10 “C用1個位元組(8位)存儲一個字元” 302
問題11 “i="A"是字元型數據賦給整型變數” 303
問題12 “字元常量只能是一個字元” 303
問題13 “從常量的表示形式可以判斷其類型” 303
問題14 1、'1'與"1" 304
問題15 “變數值在存儲單元中都是以補碼形式存儲的” 304
問題16 “不應向無符號整型變數賦予一個負值” 305
問題17 3.0乘以4.2怎么就變成了12.59999 306
問題18 “實型變數”剖析 307
問題19 使用float招來的警告及“因噎廢食”的對策 307
問題20 123f——怎樣寫float類型的常量 309
問題21 “實型常量有兩種表示形式” 309
問題22 “浮點數類型包括float、double和long double” 310
問題23 “實數就是浮點數” 310
問題24 “編譯系統為每一個float型變數分配4個位元組” 310
問題25 “浮點型”、“雙精度浮點型”和“實型” 311
問題26 “Turbo C對long double 型分配16個位元組” 311
問題27 “必然以規範化的指數形式輸出” 311
問題28 “浮點型數據是用來表示具有小數點的實數的” 312
問題29 對類型轉換運算的誤解 312
問題30 “整型量與字元型數據以及邏輯型數據可以通用” 312
問題31 “C語言把所有的實數都作為雙精度數處理” 314
問題32 “所有float型數據都先轉換為double型” 314
問題33 “int型與float或double型數據進行運算,結果是double型” 315
問題34 關於char類型數據的運算規則 316
問題35 對表達式“10+'a'+i*f-d/3”的分析 316
問題36 “字元數據既可以字元形式輸出,也可以整數形式輸出” 318
問題37 3 ? 1∶2. 319
問題38 “運算符兩側的數據類型不同,先自動進行型轉換” 319
第11章 運算和表達式(Expression) 321
問題1 關於“整型常量” 322
問題2 “變數名是以一個名字代表一個存儲地址” 323
問題3 “預設情況下所有變數都是auto 的” 324
問題4 “不能對一個類型賦值、存取或運算” 324
問題5 “變數必須先定義、後使用” 324
問題6 “常量是沒有名字的不變數” 325
問題7 “標識符用來標識一個對象” 325
問題8 啥叫“中間變數” 326
問題9 “先乘除後加減”——“優先權高先運算” 326
問題10 “賦值截斷” 326
問題11 對(a=b)>0 的誤讀 327
問題12 “‘.’運算符優先權高於後綴‘++’運算符” 327
問題13 “‘結合性’是C語言的特點之一” 327
問題14 “‘左結合性’,即運算對象先與左面的運算符相結合” 328
問題15 “算術表達式”和“算術運算符”:兩個臆造的偽概念 328
問題16 關於“算術表達式”、“優先權”和“結合性”的胡扯 330
問題17 “C語言共有34種運算符” 333
問題18 “兩個實數相除的結果是雙精度實數” 334
問題19 整數除法的捨入方向問題 335
問題20 “++運算的作用是使變數的值加1” 335
問題21 為a+=a-=a*a預擬的悼詞 335
問題22 &&的運算規則 339
問題23 對“5>3&&8<4-!0”的分析 340
問題24 邏輯運算符和邏輯表達式341
問題25 “二元運算符都可以與賦值符一起組合成複合賦值符” 342
問題26 “賦值運算符的作用是將一個數據賦給一個變數” 343
問題27 “執行表達式a=(b=5),就是執行b=5和a=b兩個賦值表達式” 343
問題28類型系統?表達式?副效應?——C的“易筋經” 343
問題29 GB/T15272-94的一個錯誤 346
問題30 sizeof“函式” 347
問題31逗號運算符是幾目運算符 348
問題32 怎樣濫用逗號運算符 348
問題33 “逗號運算符最常用於循環語句” 349
問題34 同名不同義的運算符:“++”與“++” 350
問題35 ++、--的運算對象問題 351
問題36 自增運算符(++)不能用於表達式 351
問題37 cast運算的優先權問題 352
第12章 聲明和語句 355
問題1 “全局變數” 356
問題2 聲明外部變數時省略類型名稱 356
問題3 聲明點(Declaration point)的問題 358
問題4 指向靜態整型變數的指針 359
問題5 存儲類別 359
問題6 “輸出語句”及“賦值語句” 360
問題7 “函式調用語句” 360
問題8 “C語言規定語句末尾必須有分號” 361
問題9 switch語句的一般形式 362
問題10 switch語句中的表達式類型問題 363
問題11 “while語句的範圍只到while後面第一個分號處” 364
問題12 “for語句和while語句無條件等價” 364
問題13 “語句的作用是向計算機系統發出操作指令,要求執行相應的
操作” 365
第13章 函式 367
問題1 “實際參數可以是常量、變數或表達式” 368
問題2 “函式調用的形式” 368
問題3 “虛擬參數”和“虛實結合” 369
問題4 “值傳遞”和“址傳遞” 369
問題5 函式的指針是起始地址 371
問題6 用指針調用函式 371
問題7 “void意為函式無類型” 372
問題8 “函式必須‘先定義後使用’” 372
問題9 “使用函式原型作聲明是C的一個重要特點” 373
問題10 “函式體包括聲明部分和執行部分” 373
問題11 “庫函式是編譯系統事先定義好的” 374
問題12 “定義無參函式的一般形式” 374
問題13 “不帶回值的函式的函式體中不得出現return語句” 374
問題14 過時的函式定義 375
問題15 “不能調用main函式” 376
問題16 “C程式在main()函式中結束” 376
問題17 五花八門的main()函式 377
問題18 “函式名代表函式的入口地址” 384
問題19 “函式聲明”、“函式原型”與“函式定義”辨析 385
第14章 指針 389
問題1 指針的種類問題 390
問題2 “指針就是一個變數” 390
問題3 “指針就是地址” 391
問題4 “一元‘&’是取地址運算符” 393
問題5 錯誤觀念下的代碼 393
問題6 返回指向局部變數的指針 396
問題7野指針問題 396
問題8 “指針是32位的無符號的整數” 397
問題9 “指針的大小都一樣” 397
問題10 “無類型指針” 397
問題11 指針加減法運算的“定義域” 398
問題12 const 悖論 400
問題13 不經意的錯誤:&a[10] 與a+10的差異 403
問題14 新編《守株待兔》——C語言版——兼聊為什麼不應該用%d
格式轉換輸出指針 403
第15章 數組和字元串 407
問題1 定義一維數組的一般形式 408
問題2 何謂“可變長數組” 408
問題3 “字元串是存放在字元型數組中的” 409
問題4 “數組中未被賦值的元素的值自動置'\0'” 410
問題5 “數組名不是左值” 411
問題6 數組名“只代表數組首元素的地址” 412
問題7 辨析“形參和實參可以分別用字元數組名或字元指針變數” 414
問題8 關於strncpy()函式的功能 415
問題9 “數組名作函式參數”問題 416
問題10 怎樣稱呼字元串 418
問題11 小氣的數組和危險的輸入——慎用scanf()函式和gets()函式 420
問題12 修改字元串文字量 421
問題13 對strcpy()功能斷章取義的描述 422
第16章 結構體、共用體、枚舉及位運算425
問題1 成員運算問題 426
問題2 “->”稱為指向運算符 426
問題3 “只能對最低級的成員進行賦值或存取以及運算” 426
問題4 63、64還是68?——談談alignment 428
問題5 結構體的尺寸問題 431
問題6 空結構體的尺寸問題 431
問題7 “結構體變數的地址主要用作函式參數” 432
問題8 “共用體變數所占記憶體長度等於最長成員的長度” 432
問題9 “不能引用共用體變數” 433
問題10 “不能對共用體變數名賦值” 435
問題11 “枚舉變數的值限定在定義時規定的幾個枚舉元素範圍內” 435
問題12 “‘位運算’是C語言區別於其他高級語言的一個重要特點” 436
問題13 “參加位運算的對象只能是整型或字元型的數據” 437
問題14 “‘<<’用來將一個數的各二進制位全部左移若干位” 438
問題15 不同長度的數據進行位運算 439
問題16 用memcmp()函式比較兩個結構體 439
第17章 標準庫 441
問題1 “%c轉換說明符用於輸入單個字元”引發的潛在BUG 442
問題2 “對無符號整型數據用%u輸出” 442
問題3 “double類型應該用‘%lf’輸出” 443
問題4 關於%g格式的錯誤認識 443
問題5 關於%o格式的錯誤認識 444
問題6 “gets()函式是fgets()函式用於stdin的特例” 445
問題7 “用puts函式輸出的字元串中可以包含轉義字元” 447
問題8 “檔案結束標誌EOF(即-1)” 447
問題9 “fseek(fp、100L、0)” 448
問題10 打開檔案時的兩種錯誤 448
問題11 “用"a"方式打開檔案應保證檔案事先存在” 449
問題12 “在程式中可以直接用FILE類型名定義變數” 449
問題13 “stdin是指向標準輸入流的指針變數” 449
問題14 關於feof()函式的錯誤 450
問題15 “度日如年”的故事及“feof()函式”的故事 450
問題16 關於fflush()函式的錯誤 455
問題17 盲目前行——忽視malloc()函式返回值457
問題18 管殺不管埋——缺少free()函式 458
問題19 怎樣建立鍊表並同時造成記憶體泄漏459
問題20 qsort()函式是快速排序 462
問題21 關於calloc()函式 462
問題22 關於free()函式 463
問題23 關於realloc()函式 464
問題24 為什麼不可以static char*p=malloc(10); 464
問題25 abs()是數學函式 465
第18章 預處理 467
問題1 “預處理功能是C語言特有的” 468
問題2 主動引進BUG 468
問題3 “#define是用一個指定的標識符來代表字元串” 469
問題4 “預處理器把程式中的注釋全部刪除” 470
問題5 “所有的PI都代表3.1416” 470
問題6 #define預處理命令的位置問題 471
問題7 能否用痰盂盛飯——談在頭檔案中定義外部變數472
問題8 預處理次序錯誤 475
問題9 缺少#include 476
問題10 包含源檔案——是奇技淫巧還是飲鴆止渴 476
問題11 使用函式式宏的禁忌 480
問題12 不合格的宏 481
問題13 “宏實參從左到右進行置換” 481
問題14條件編譯與if語句482
第19章 關於C99 485
問題1 亡羊補牢還是越錯越遠——“C99允許在函式中的複合語句
中定義變數” 486
問題2 C99增加了哪些數據類型 488
問題3 “C99標準不支持一行內寫多個語句” 489
問題4 “C99把字元型數據作為整數類型的一種” 489
問題5 “C99要求main函式一律指定為int型” 489
問題6 “C99允許使用常變數” 490
問題7 “C99允許使用多位元組字元” 490
問題8 “C99允許使用基類型為void的指針類型” 490
問題9 “C99增加了C++中的功能” 490
問題10 “C99允許同類型的共用體變數互相賦值” 491
問題11 能否用VisualC++6.0學習C99 491
第20章 雜七雜八 493
問題1 “系統描述語言”與“系統程式語言” 494
問題2 關於計算機指令長度 494
問題3 同一律 494
問題4 為什麼不能用彙編解釋C 495
問題5 “檔案中的物理順序” 495
問題6 “C語言源程式的擴展名為.c” 496
問題7 缺乏測試意識 496
問題8 為Wirth正名 499
問題9 “bohra”是誰 500
問題10 “83 ANSI C”和“87 ANSI C” 500
問題11 關於ISO 501
問題12 關於勒讓德多項式 501
參考文獻 502

相關詞條

熱門詞條

聯絡我們