《编写高质量代码:改善c程序代码的125个建议》——第1章 数据,程序设计之根本建议1:认识ANSI C...
本節(jié)書摘來自華章計(jì)算機(jī)《編寫高質(zhì)量代碼:改善c程序代碼的125個(gè)建議》一書中的第1章,建議1,作者:馬 偉 更多章節(jié)內(nèi)容可以訪問云棲社區(qū)“華章計(jì)算機(jī)”公眾號(hào)查看。
第1章 數(shù)據(jù),程序設(shè)計(jì)之根本
數(shù)據(jù)是程序設(shè)計(jì)最基礎(chǔ)的概念,程序?qū)?shù)據(jù)進(jìn)行操作。換句話說,任何一個(gè)完整的程序都可以看成是一組數(shù)據(jù)和作用于這組數(shù)據(jù)上的操作的說明。同時(shí),程序中的每個(gè)數(shù)據(jù)項(xiàng)也都有一個(gè)與之相關(guān)的類型,稱為“數(shù)據(jù)類型”。
這樣,在程序中就可以使用數(shù)據(jù)類型來區(qū)分不同的數(shù)據(jù),進(jìn)而根據(jù)實(shí)際需要為這些數(shù)據(jù)分配不同的存儲(chǔ)空間。這就像成年人必須睡成人床,而給嬰兒配備嬰兒床就足夠了,如果你給嬰兒分配一張成人床就會(huì)造成資源浪費(fèi),相反給成年人分配一張嬰兒床則有可能會(huì)發(fā)生“溢出”。數(shù)據(jù)類型也一樣,由于不同的數(shù)據(jù)所需要的存儲(chǔ)容量各不相同,因此需要分配的內(nèi)存空間大小也會(huì)不一樣,這樣才能夠保證內(nèi)存資源的合理配置,使程序性能達(dá)到最優(yōu)化。因此,如何合理、安全地使用這些數(shù)據(jù)類型是每個(gè)程序員必須掌握的。本章將圍繞這一話題進(jìn)行討論。
建議1:認(rèn)識(shí)ANSI C
談到C語言的發(fā)展歷程,就不得不從最早的二進(jìn)制語言說起。大家都知道,二進(jìn)制語言可以說是世界上最早的計(jì)算機(jī)語言,它只允許程序設(shè)計(jì)人員使用計(jì)算機(jī)能夠直接識(shí)別和執(zhí)行的二進(jìn)制代碼(即0和1,其中,0代表低電壓,1代表高電壓)來編寫程序。可想而知,這樣的編碼方式對(duì)程序設(shè)計(jì)人員來說是多么困難與枯燥。因此,為了提高程序設(shè)計(jì)效率并減輕程序設(shè)計(jì)人員的負(fù)擔(dān),所以后來很快便出現(xiàn)了匯編語言(Assembly Language)。
與二進(jìn)制語言一樣,匯編語言也是面向機(jī)器的程序設(shè)計(jì)語言,不同類型的計(jì)算機(jī)上需要提供不同的匯編語言。但與二進(jìn)制語言不同的是,匯編語言使用助記符(Memoni)來代替操作碼,并用地址符號(hào)(Symbol)或標(biāo)號(hào)(Label)來代替地址碼。由于匯編語言采用符號(hào)代替二進(jìn)制代碼,因此,它也被稱為符號(hào)語言。使用匯編語言編寫的程序機(jī)器并不能直接識(shí)別,而需要通過一種程序?qū)R編語言翻譯成機(jī)器能夠識(shí)別的二進(jìn)制語言,這種起翻譯作用的程序就是匯編程序。
相對(duì)于二進(jìn)制語言,匯編語言不僅使開發(fā)效率得到了很大提升,而且它還具有許多優(yōu)點(diǎn),比如它能夠直接同計(jì)算機(jī)的底層軟件或硬件進(jìn)行交互,直接訪問與硬件相關(guān)的存儲(chǔ)器或 I/O端口;能夠不受編譯器的限制,對(duì)生成的二進(jìn)制代碼進(jìn)行完全控制;能夠?qū)﹃P(guān)鍵的代碼進(jìn)行更準(zhǔn)確地控制,避免因線程共同訪問或硬件設(shè)備共享而引起的死鎖;能夠根據(jù)特定的應(yīng)用對(duì)代碼進(jìn)行最佳優(yōu)化,提高運(yùn)行速度等。
盡管如此,匯編語言依舊是一種層次非常低的語言,它僅僅高于直接手工編寫二進(jìn)制的機(jī)器指令碼。在實(shí)際應(yīng)用中,它仍然暴露了一些不可避免的缺陷:如編寫的代碼非常難以閱讀,不好維護(hù);很容易產(chǎn)生bug,難于調(diào)試;一般只能針對(duì)特定的體系結(jié)構(gòu)和處理器進(jìn)行優(yōu)化;開發(fā)效率很低,時(shí)間長(zhǎng)且單調(diào)等。因此,我們更加需要一種設(shè)計(jì)描述簡(jiǎn)單,能脫離對(duì)機(jī)型的要求,并且能在任何計(jì)算機(jī)上運(yùn)行的計(jì)算機(jī)語言,我們稱這種語言為高級(jí)語言。這樣,程序設(shè)計(jì)人員就可以將問題及解決問題的算法過程描述出來,利用這種高級(jí)語言直接寫出各種表達(dá)式來描述簡(jiǎn)單的計(jì)算過程,而無須針對(duì)不同的機(jī)型編寫不同的代碼。
高級(jí)語言編寫的程序稱為源程序,源程序不能在計(jì)算機(jī)上直接運(yùn)行,必須將其翻譯成二進(jìn)制代碼后才能執(zhí)行。一般有兩種翻譯方式:一種是“解釋程序”方式,即將源程序作為輸入,翻譯一句后就提交計(jì)算機(jī)執(zhí)行一句,這種方式并不形成目標(biāo)程序;另一種是“編譯程序”方式,即將源程序作為輸入,全部翻譯成二進(jìn)制代碼后再執(zhí)行,編譯后的二進(jìn)制程序稱為目標(biāo)程序。
世界上出現(xiàn)的第一種高級(jí)語言是Algol語言,它也可以算作C語言的前身。它和普通語言表達(dá)式非常接近,適用于數(shù)值計(jì)算,所以Algol多用于科學(xué)計(jì)算機(jī)。1960年Algol 60版本推出后,很受程序設(shè)計(jì)人員歡迎。Algol 60推出了許多新的概念,如局部性概念、動(dòng)態(tài)、遞歸、巴科斯-諾爾范式(Backus-Naur Form,BNF)等。從某種意義上講,Algol 60應(yīng)該是程序設(shè)計(jì)語言發(fā)展史上的一個(gè)里程碑,它標(biāo)志著程序設(shè)計(jì)語言已成為一門獨(dú)立的科學(xué)學(xué)科,并為后來的軟件自動(dòng)化及軟件可靠性的發(fā)展奠定了基礎(chǔ)。
雖然使用Algol 60來描述算法很方便,但是它離計(jì)算機(jī)硬件系統(tǒng)卻很遠(yuǎn),不宜用來編寫系統(tǒng)程序。1963年英國(guó)劍橋大學(xué)在Algol語言的基礎(chǔ)上增添了處理硬件的能力,并命名為“CPL”(Combined Programming Language),即復(fù)合程序設(shè)計(jì)語言。但由于CPL的規(guī)模很大,學(xué)習(xí)和掌握都比較困難,因此沒有流行。1967年劍橋大學(xué)的Martin Richards對(duì)CPL語言進(jìn)行了簡(jiǎn)化,推出BCPL(Basic Combined Programming Language),即基本復(fù)合程序設(shè)計(jì)語言。它是典型的面向過程的高級(jí)語言,它的語法更加靠近機(jī)器本身,適合于開發(fā)精巧、高要求的應(yīng)用程序,而且它對(duì)編譯器的要求也不高。同時(shí),BCPL也是最早使用庫(kù)函數(shù)封裝基本輸入輸出的語言之一,這使得它的跨平臺(tái)可移植性很好。
1969年,美國(guó)通用電氣公司、麻省理工學(xué)院與貝爾實(shí)驗(yàn)室聯(lián)合創(chuàng)建了一個(gè)龐大的項(xiàng)目,命名為Muktics工程。該項(xiàng)目的目的是創(chuàng)建一個(gè)操作系統(tǒng),不過由于該項(xiàng)目過于復(fù)雜和龐大,最終失敗了。這也致使項(xiàng)目的參與者之一通用電氣公司退出軟件領(lǐng)域,同時(shí),貝爾實(shí)驗(yàn)室的專家們也撤出了Muktics工程,轉(zhuǎn)而研究新的領(lǐng)域。之后,貝爾實(shí)驗(yàn)室的一位名為Ken Thompson研究員和他的同事Dennis Ritchie組成一個(gè)非正式的小組,開始進(jìn)行一些其他方面的研究。為了自?shī)首詷?#xff0c;Ken Thompson把他的“太空旅行”軟件移植到不太常用的PDP-7系統(tǒng)上。與此同時(shí),Ken Thompson還為PDP-7系統(tǒng)編寫了一個(gè)簡(jiǎn)單的操作系統(tǒng)。該操作系統(tǒng)比起Muktics工程簡(jiǎn)單了許多,采用匯編語言編寫,1970年Brian Kernighan為其取名為UNIX。
從這里可以看出,著名的操作系統(tǒng)UNIX是早于C語言出現(xiàn)的,后來才用C語言重寫此系統(tǒng),這一點(diǎn)一定要注意。
不過使用匯編語言編寫程序不僅吃力而且效率低下,所以Ken Thompson就考慮利用高級(jí)語言的特性來解決這一問題。1970年,Ken Thompson進(jìn)一步簡(jiǎn)化了BCPL,突出硬件的處理能力,并取“BCPL”的第一個(gè)字母“B”作為新語言的名稱,即B語言。同時(shí),他還使用B語言編寫了UNIX操作系統(tǒng)程序。不過B語言還是存在許多問題,最大問題就在于無法表達(dá)不同的數(shù)據(jù)類型,而且效率不高,這也迫使Ken Thompson后來不得不在PDP-11的基礎(chǔ)上重新使用匯編語言來實(shí)現(xiàn)UNIX。面對(duì)B語言存在的問題,1971年Dennis Ritchie通過增加類型擴(kuò)展了B語言,這次采用的是編譯模式而不是解釋模式,并且引入了類型系統(tǒng),每個(gè)變量在使用前必須聲明。這種擴(kuò)展的B語言稱為NB,即New B的縮寫。
1972年,Ken Thompson和Dennis Ritchie繼續(xù)對(duì)B語言進(jìn)行完善和擴(kuò)充,他們?cè)诒A鬊語言強(qiáng)大硬件處理能力的基礎(chǔ)上,擴(kuò)充了數(shù)據(jù)類型,恢復(fù)了通用性,并取“BCPL”的第二個(gè)字母“C”作為新語言的名稱,即C語言。其實(shí),C語言除了增加類型系統(tǒng)外,它還增加了許多方便編譯器設(shè)計(jì)者設(shè)計(jì)的新特性,主要表現(xiàn)在以下幾個(gè)方面:
數(shù)組下標(biāo)從0開始,而不是從1開始。例如,我們定義一個(gè)數(shù)組arr[50],因?yàn)镃語言的數(shù)組下標(biāo)是從0開始的,所以它的合法范圍是arr[0]~arr[49]。因此,你不能夠向arr[50]里存儲(chǔ)數(shù)據(jù)。
- 可以把數(shù)組看作指針,它簡(jiǎn)化了參數(shù)的傳遞方法,使大家不必忍受傳遞一個(gè)數(shù)組到函數(shù)時(shí)需要復(fù)制所有數(shù)組內(nèi)容的低效率。不過,值得注意的是,數(shù)組與指針并非在任何情況下都是等效的,這一點(diǎn)會(huì)在后面進(jìn)行詳細(xì)闡述。
float類型被自動(dòng)擴(kuò)展為double類型。雖然在ANSI C中情況不再如此,但最- 初浮點(diǎn)數(shù)常量的精度都是double類型的,所有表達(dá)式中float類型的變量總會(huì)被自動(dòng)轉(zhuǎn)化為double類型。 - 增加register關(guān)鍵字,用此關(guān)鍵字告訴編譯器設(shè)計(jì)者哪些變量被放到了寄存器中,從而簡(jiǎn)化編譯器,但卻也因此給程序員帶來了無窮無盡的麻煩,這一點(diǎn)會(huì)在后面的章節(jié)中詳細(xì)闡述。
此后,兩人又合作重寫了UNIX操作系統(tǒng),C語言也伴隨著UNIX操作系統(tǒng)成為一種廣受歡迎的計(jì)算機(jī)語言。圖1-1按時(shí)間順序闡述C語言的由來。
https://yqfile.alicdn.com/97fe12ceda341080e5a3a4f8839ee8685e3e0d3a.png" >
1978年,為了讓C語言脫離UNIX操作系統(tǒng),成為任何計(jì)算機(jī)上都能運(yùn)行的通用計(jì)算機(jī)語言,Brian Kernighan和Dennis Ritchie共同撰寫了《The C Programming Language》的第1版,該著作簡(jiǎn)稱為“K&R”。書中對(duì)C語言的語法進(jìn)行了規(guī)范化描述,書末的參考指南則給出了當(dāng)時(shí)C語言的完整定義,這也成為當(dāng)時(shí)C語言事實(shí)上的標(biāo)準(zhǔn),此標(biāo)準(zhǔn)稱為“K&R C”。從此以后,C語言被移植到各種機(jī)型上,并受到廣泛支持。
隨著C語言在多個(gè)領(lǐng)域的推廣和應(yīng)用,一些新的特性不斷被各種編譯器實(shí)現(xiàn)并添加進(jìn)來。于是建立一個(gè)新的“無歧義、與具體平臺(tái)無關(guān)的C語言定義”就成為越來越重要的事情。1983年,美國(guó)國(guó)家標(biāo)準(zhǔn)委員會(huì)ANSI(American National Standards Institute)屬下專門負(fù)責(zé)信息技術(shù)標(biāo)準(zhǔn)化的機(jī)構(gòu)ASC X3(現(xiàn)已更名為國(guó)際信息技術(shù)標(biāo)準(zhǔn)委員會(huì)(International Committee for Information Technology Standards,INCITS))成立了一個(gè)專門的技術(shù)委員會(huì) J11(J11 是委員會(huì)編號(hào),全稱是X3J11),用于起草關(guān)于C語言的標(biāo)準(zhǔn)草案。1989年,ANSI正式通過C語言標(biāo)準(zhǔn)草案,至此該標(biāo)準(zhǔn)成為美國(guó)國(guó)家標(biāo)準(zhǔn),此標(biāo)準(zhǔn)也稱為C89標(biāo)準(zhǔn)。
隨后,《The C Programming Language》第2版出版發(fā)行,書中內(nèi)容根據(jù) ANSI C(C89)進(jìn)行了更新。1990年,在ISO/IEC JTC1/SC22/WG14(即ISO/IEC聯(lián)合技術(shù)第I委員會(huì)第22分委員會(huì)第14工作組)的努力下,ISO批準(zhǔn)ANSI C成為國(guó)際標(biāo)準(zhǔn),于是ISO C(又稱為C90)誕生。與C89相比,C90除了標(biāo)準(zhǔn)文檔的印刷編排細(xì)節(jié)有些不同外(主要表現(xiàn)在刪除了“Rationale”一節(jié),并把文檔的格式與段落編碼作了改動(dòng)),它們?cè)诩夹g(shù)上是完全一樣的。到目前為止,C89是C語言運(yùn)用得最廣泛的標(biāo)準(zhǔn),基本上所有的C語言編譯器都完全支持該標(biāo)準(zhǔn)。相對(duì)于“K&R C”,C89主要做了以下幾方面的改進(jìn):
- 增加了新特性——原型。原型是函數(shù)聲明的擴(kuò)展,它使得編譯器很容易根據(jù)函數(shù)的定義檢查函數(shù)的用法。
- 增加了一些新的關(guān)鍵字,如enum、const、volatile、signed與void。C89的關(guān)鍵字見表1-1。
除此之外,C89還做了許多其他的改進(jìn),如增強(qiáng)了預(yù)處理指令,定義了相關(guān)的宏,允許將結(jié)構(gòu)本身作為參數(shù)傳遞給函數(shù),從“無符號(hào)保留”轉(zhuǎn)到“值保留”等。
自ISO C(C90)推出之后,ISO又于1994年與1996年分別出版了C90的技術(shù)勘誤文檔,更正了一些印刷錯(cuò)誤,同時(shí),在1995年還通過了一份C90的技術(shù)補(bǔ)充,這份補(bǔ)充對(duì)C90進(jìn)行了微小擴(kuò)充,擴(kuò)充后的ISO C被稱為C95。
1999年,ANSI和ISO又通過了最新版本的C語言標(biāo)準(zhǔn)和技術(shù)勘誤文檔,該標(biāo)準(zhǔn)被稱為C99。這里需要說明的是,與C89不同,并非市面上所有的編譯器都支持C99,并且有的編譯器只支持C99的部分新特性。相對(duì)于C89,C99主要做了以下幾方面的改進(jìn):
- 增加了restrict與inline關(guān)鍵字。
- 新增_Bool、_Complex與_Imaginary 3種數(shù)據(jù)類型,如C99中定義的復(fù)數(shù)類型為:float_Complex、float_Imaginary、double_Complex、double_Imaginary、long double_Complex與long double_Imaginary。
- 增強(qiáng)數(shù)組的功能,支持可變長(zhǎng)數(shù)組等。
- 支持復(fù)合賦值。
- 增強(qiáng)預(yù)處理程序,如引入_Pragma運(yùn)算符,并增加了一些內(nèi)部宏等。
- 支持柔性數(shù)組結(jié)構(gòu)成員,即允許結(jié)構(gòu)中的最后一個(gè)元素是未知大小的數(shù)組。
由于技術(shù)的發(fā)展日新月異,因此雖然C99還沒有得到完全支持,但在2007年,標(biāo)準(zhǔn)委員會(huì)就又開始起草新的C語言標(biāo)準(zhǔn)來取代現(xiàn)有的C99標(biāo)準(zhǔn),該標(biāo)準(zhǔn)命名為C1X,C1X是一個(gè)非正式名字。2011年12月,ANSI正式采納了ISO/IEC 9899:2011標(biāo)準(zhǔn),即C11標(biāo)準(zhǔn)。相對(duì)于C99,C11主要做了如下幾方面的改進(jìn):
- 采用新的對(duì)齊規(guī)范,包括_Alignas說明符、_Alignof運(yùn)算符、aligned_alloc函數(shù)與頭文件。
- 增加_Noreturn函數(shù)標(biāo)記。
- 增加_Generic關(guān)鍵詞。
- 增加靜態(tài)斷言_Static_assert()。
- 刪除gets()函數(shù),C99中已經(jīng)將此函數(shù)標(biāo)記為過時(shí),推薦新的替代函數(shù)gets_s()。
- 采用新的fopen()模式。
- 增加匿名結(jié)構(gòu)體/聯(lián)合體。
- 支持多線程技術(shù),包括_Thread_local與頭文件。
- 增加_Atomic類型修飾符和頭文件。
- 帶邊界檢查(bounds-checking)的函數(shù)接口,定義了新的安全的函數(shù),例如fopen_s()、strcat_s()等。
- 改進(jìn)Unicode支持與頭文件。
- 增加quick_exit()函數(shù)作為第三種終止程序的方式。
- 可以創(chuàng)建復(fù)數(shù)的宏。
- 增加更多處理浮點(diǎn)數(shù)的宏。
- struct timespec成為time.h的一部分,以及宏TIME_UTC和函數(shù)timespec_get()。
綜上所述,可以用圖1-2來直觀地闡述C語言標(biāo)準(zhǔn)的發(fā)展歷程。
https://yqfile.alicdn.com/d050768707ba2e90d060740fbeb80d17abeb878d.png" >
在GCC編譯器中,針對(duì)不同版本的C語言標(biāo)準(zhǔn),可以通過在命令行中使用“-std”選項(xiàng)來選擇所需要使用的C語言標(biāo)準(zhǔn)版本。
1)C89或者C90
2)C95
-std=iso9899:1994093)C99
-std=c99 -std=iso9899:19994)C11
-std=c11 -std=iso9899:20115)除此之外,如果需要在GCC中使用C擴(kuò)展,還可以通過如下參數(shù)形式實(shí)現(xiàn):
C89或者C90:-std=gnu90 C99:-std=gnu99 C11:-std=gnu11總結(jié)
以上是生活随笔為你收集整理的《编写高质量代码:改善c程序代码的125个建议》——第1章 数据,程序设计之根本建议1:认识ANSI C...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《Windows Server 2012
- 下一篇: Activiti实战. 导读