高質(zhì)量C-C++編程指南_第1頁
高質(zhì)量C-C++編程指南_第2頁
高質(zhì)量C-C++編程指南_第3頁
高質(zhì)量C-C++編程指南_第4頁
高質(zhì)量C-C++編程指南_第5頁
已閱讀5頁,還剩88頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領

文檔簡介

1、精選優(yōu)質(zhì)文檔-傾情為你奉上精選優(yōu)質(zhì)文檔-傾情為你奉上專心-專注-專業(yè)專心-專注-專業(yè)精選優(yōu)質(zhì)文檔-傾情為你奉上專心-專注-專業(yè)高質(zhì)量C+/C編程指南文件狀態(tài)草稿文件正式文件更改正式文件文件標識:當前版本:1.0作者:林銳博士完成日期:2001年7月24日版本歷史版本/狀態(tài)作者參與者起止日期備注V 0.9草稿文件林銳2001-7-1至2001-7-18林銳起草V 1.0正式文件林銳2001-7-18至2001-7-24朱洪海審查V 0.9,林銳修正草稿中的錯誤目錄前言軟件質(zhì)量是被大多數(shù)程序員掛在嘴上而不是放在心上的東西!除了完全外行和真正的編程高手外,初讀本書,你最先的感受將是驚慌:“哇!我以前

2、捏造的C+/C程序怎么會有那么多的毛?。俊眲e難過,作者只不過比你早幾年、多幾次驚慌而已。請花一兩個小時認真閱讀這本百頁經(jīng)書,你將會獲益匪淺,這是前面N-1個讀者的建議。一、編程老手與高手的誤區(qū)自從計算機問世以來,程序設計就成了令人羨慕的職業(yè),程序員在受人寵愛之后容易發(fā)展成為毛病特多卻常能自我臭美的群體。如今在Internet上流傳的“真正”的程序員據(jù)說是這樣的:(1)真正的程序員沒有進度表,只有討好領導的馬屁精才有進度表,真正的程序員會讓領導提心吊膽。(2)真正的程序員不寫使用說明書,用戶應當自己去猜想程序的功能。(3)真正的程序員幾乎不寫代碼的注釋,如果注釋很難寫,它理所當然也很難讀。(4)

3、真正的程序員不畫流程圖,原始人和文盲才會干這事。(5)真正的程序員不看參考手冊,新手和膽小鬼才會看。(6)真正的程序員不寫文檔也不需要文檔,只有看不懂程序的笨蛋才用文檔。(7)真正的程序員認為自己比用戶更明白用戶需要什么。(8)真正的程序員不接受團隊開發(fā)的理念,除非他自己是頭頭。(9)真正的程序員的程序不會在第一次就正確運行,但是他們愿意守著機器進行若干個30小時的調(diào)試改錯。(10)真正的程序員不會在上午9:00到下午5:00之間工作,如果你看到他在上午9:00工作,這表明他從昨晚一直干到現(xiàn)在。具備上述特征越多,越顯得水平高,資格老。所以別奇怪,程序員的很多缺點竟然可以被當作優(yōu)點來欣賞。就象在

4、武俠小說中,那些獨來獨往、不受約束且?guī)c邪氣的高手最令人崇拜。我曾經(jīng)也這樣信奉,并且希望自己成為那樣的“真正”的程序員,結(jié)果沒有得到好下場。我從讀大學到博士畢業(yè)十年來一直勤奮好學,累計編寫了數(shù)十萬行C+/C代碼。有這樣的苦勞和疲勞,我應該稱得上是編程老手了吧?我開發(fā)的軟件都與科研相關(集成電路CAD和3D圖形學領域),動輒數(shù)萬行程序,技術復雜,難度頗高。這些軟件頻頻獲獎,有一個軟件獲得首屆中國大學生電腦大賽軟件展示一等獎。在1995年開發(fā)的一套圖形軟件庫到2000年還有人買。羅列出這些“業(yè)績”,可以說明我算得上是編程高手了吧?可惜這種個人感覺不等于事實。讀博期間我曾用一年時間開發(fā)了一個近10萬

5、行C+代碼的3D圖形軟件產(chǎn)品,我內(nèi)心得意表面謙虛地向一位真正的軟件高手請教。他雖然從未涉足過3D圖形領域,卻在幾十分鐘內(nèi)指出該軟件多處重大設計錯誤。讓人感覺那套軟件是用紙糊的華麗衣服,扯一下掉一塊,戳一下破個洞。我目瞪口呆地意識到這套軟件毫無實用價值,一年的心血白化了,并且害死了自己的軟件公司。人的頓悟通常發(fā)生在最心痛的時刻,在沮喪和心痛之后,我作了深刻反省,“面壁”半年,重新溫習軟件設計的基礎知識。補修“內(nèi)功”之后,又覺得腰板硬了起來。博士畢業(yè)前半年,我曾到微軟中國研究院找工作,接受微軟公司一位資深軟件工程師的面試。他讓我寫函數(shù)strcpy的代碼。太容易了吧?錯!這么一個小不點的函數(shù),他從三

6、個方面考查:(1)編程風格;(2)出錯處理;(3)算法復雜度分析(用于提高性能)。在大學里從來沒有人如此嚴格地考查過我的程序。我化了半個小時,修改了數(shù)次,他還不盡滿意,讓我回家好好琢磨。我精神抖擻地進“考場”,大汗淋漓地出“考場”。這“高手”當?shù)靡蔡C囊了。我又好好地反省了一次。我把反省后的心得體會寫成文章放在網(wǎng)上傳閱,引起了不少軟件開發(fā)人員的共鳴。我因此有幸和國產(chǎn)大型IT企業(yè)如華為、上海貝爾、中興等公司的同志們廣泛交流。大家認為提高質(zhì)量與生產(chǎn)率是軟件工程要解決的核心問題。高質(zhì)量程序設計是非常重要的環(huán)節(jié),畢竟軟件是靠編程來實現(xiàn)的。我們心目中的老手們和高手們能否編寫出高質(zhì)量的程序來?不見得都能!

7、就我的經(jīng)歷與閱歷來看,國內(nèi)大學的計算機教育壓根就沒有灌輸高質(zhì)量程序設計的觀念,教師們和學生們也很少自覺關心軟件的質(zhì)量。勤奮好學的程序員長期在低質(zhì)量的程序堆中滾爬,吃盡苦頭之后才有一些心得體會,長進極慢,我就是一例。現(xiàn)在國內(nèi)IT企業(yè)擁有學士、碩士、博士文憑的軟件開發(fā)人員比比皆是,但他們在接受大學教育時就“先天不足”,豈能一到企業(yè)就突然實現(xiàn)質(zhì)的飛躍。試問有多少軟件開發(fā)人員對正確性、健壯性、可靠性、效率、易用性、可讀性(可理解性)、可擴展性、可復用性、兼容性、可移植性等質(zhì)量屬性了如指掌?并且能在實踐中運用自如?。“高質(zhì)量”可不是干活小心點就能實現(xiàn)的!我們有充分的理由疑慮:(1)編程老手可能會長期用隱

8、含錯誤的方式編程(習慣成自然),發(fā)現(xiàn)毛病后都不愿相信那是真的!(2)編程高手可以在某一領域?qū)懗鰳O有水平的代碼,但未必能從全局把握軟件質(zhì)量的方方面面。事實證明如此。我到上海貝爾工作一年來,陸續(xù)面試或測試過近百名“新”“老”程序員的編程技能,質(zhì)量合格率大約是10。很少有人能夠?qū)懗鐾耆腺|(zhì)量要求的if語句,很多程序員對指針、內(nèi)存管理一知半解,。領導們不敢相信這是真的。我做過現(xiàn)場試驗:有一次部門新進14名碩士生,在開歡迎會之前對他們進行“C+/C編程技能”摸底考試。我問大家試題難不難?所有的人都回答不難。結(jié)果沒有一個人及格,有半數(shù)人得零分。競爭對手公司的朋友們也做過試驗,同樣一敗涂地。真的不是我“心

9、狠手辣”或者要求過高,而是很多軟件開發(fā)人員對自己的要求不夠高。要知道華為、上海貝爾、中興等公司的員工素質(zhì)在國內(nèi)IT企業(yè)中是比較前列的,倘若他們的編程質(zhì)量都如此差的話,我們怎么敢期望中小公司拿出高質(zhì)量的軟件呢?連程序都編不好,還談什么振興民族軟件產(chǎn)業(yè),豈不胡扯。我打算定義編程老手和編程高手,請您別見笑。定義1:能長期穩(wěn)定地編寫出高質(zhì)量程序的程序員稱為編程老手。定義2:能長期穩(wěn)定地編寫出高難度、高質(zhì)量程序的程序員稱為編程高手。根據(jù)上述定義,馬上得到第一推論:我既不是高手也算不上是老手。在寫此書前,我閱讀了不少程序設計方面的英文著作,越看越羞慚。因為發(fā)現(xiàn)自己連編程基本技能都未能全面掌握,頂多算是二流

10、水平,還好意思談什么老手和高手。希望和我一樣在國內(nèi)土生土長的程序員朋友們能夠做到:(1)知錯就改;(2)經(jīng)常溫故而知新;(3)堅持學習,天天向上。二、本書導讀首先請做附錄B的C+/C試題(不要看答案),考查自己的編程質(zhì)量究竟如何。然后參照答案嚴格打分。(1)如果你只得了幾十分,請不要聲張,也不要太難過。編程質(zhì)量差往往是由于不良習慣造成的,與人的智力、能力沒有多大關系,還是有藥可救的。成績越差,可以進步的空間就越大,中國不就是在落后中趕超發(fā)達資本主義國家嗎?只要你能下決心改掉不良的編程習慣,第二次考試就能及格了。(2)如果你考及格了,表明你的技術基礎不錯,希望你能虛心學習、不斷進步。如果你還沒有

11、找到合適的工作單位,不妨到上海貝爾試一試。(3)如果你考出85分以上的好成績,你有義務和資格為你所在的團隊作“C+/C編程”培訓。希望你能和我們多多交流、相互促進。半年前我曾經(jīng)發(fā)現(xiàn)一顆好苗子,就把他挖到我們小組來。(4)如果你在沒有任何提示的情況下考了滿分,希望你能收我做你的徒弟。編程考試結(jié)束后,請閱讀本書的正文。本書第一章至第六章主要論述C+/C編程風格。難度不高,但是細節(jié)比較多。別小看了,提高質(zhì)量就是要從這些點點滴滴做起。世上不存在最好的編程風格,一切因需求而定。團隊開發(fā)講究風格一致,如果制定了大家認可的編程風格,那么所有組員都要遵守。如果讀者覺得本書的編程風格比較合你的工作,那么就采用它

12、,不要只看不做。人在小時候說話發(fā)音不準,寫字潦草,如果不改正,總有后悔的時候。編程也是同樣道理。第七章至第十一章是專題論述,技術難度比較高,看書時要積極思考。特別是第七章“內(nèi)存管理”,讀了并不表示懂了,懂了并不表示就能正確使用。有一位同事看了第七章后覺得“野指針”寫得不錯,與我切磋了一把??墒沁^了兩周,他告訴我,他忙了兩天追查出一個Bug,想不到又是“野指針”出問題,只好重讀第七章。光看本書對提高編程質(zhì)量是有限的,建議大家閱讀本書的參考文獻,那些都是經(jīng)典名著。如果你的編程質(zhì)量已經(jīng)過關了,不要就此滿足。如果你想成為優(yōu)秀的軟件開發(fā)人員,建議你閱讀并按照CMMI規(guī)范做事,讓自己的綜合水平上升一個臺階

13、。上海貝爾的員工可以向網(wǎng)絡應用事業(yè)部軟件工程研究小組索取CMMI有關資料,最好能參加培訓。三、版權(quán)聲明本書的大部分內(nèi)容取材于作者一年前的書籍手稿(尚未出版),現(xiàn)整理匯編成為上海貝爾網(wǎng)絡應用事業(yè)部的一個規(guī)范化文件,同時作為培訓教材。由于C+/C編程是眾所周知的技術,沒有秘密可言。編程的好經(jīng)驗應該大家共享,我們自己也是這么學來的。作者愿意公開本書的電子文檔。版權(quán)聲明如下:(1)讀者可以任意拷貝、修改本書的內(nèi)容,但不可以篡改作者及所屬單位。(2)未經(jīng)作者許可,不得出版或大量印發(fā)本書。(3)如果競爭對手公司的員工得到本書,請勿公開使用,以免發(fā)生糾紛。預計到2002年7月,我們將建立切合中國國情的CMM

14、I 3級解決方案。屆時,包括本書在內(nèi)的約1000頁規(guī)范將嚴格受控。歡迎讀者對本書提出批評建議。林銳,2001年7月第1章文件結(jié)構(gòu)每個C+/C程序通常分為兩個文件。一個文件用于保存程序的聲明(declaration),稱為頭文件。另一個文件用于保存程序的實現(xiàn)(implementation),稱為定義(definition)文件。C+/C程序的頭文件以“.h”為后綴,C程序的定義文件以“.c”為后綴,C+程序的定義文件通常以“.cpp”為后綴(也有一些系統(tǒng)以“.cc”或“.cxx”為后綴)。1.1版權(quán)和版本的聲明版權(quán)和版本的聲明位于頭文件和定義文件的開頭(參見示例1-1),主要內(nèi)容有:(1)版權(quán)信

15、息。(2)文件名稱,標識符,摘要。(3)當前版本號,作者/修改者,完成日期。(4)版本歷史信息。/* Copyright (c) 2001,上海貝爾有限公司網(wǎng)絡應用事業(yè)部* All rights reserved.* 文件名稱:filename.h* 文件標識:見配置管理計劃書* 摘要:簡要描述本文件的內(nèi)容* 當前版本:1.1* 作者:輸入作者(或修改者)名字* 完成日期:2001年7月20日* 取代版本:1.0* 原作者:輸入原作者(或修改者)名字* 完成日期:2001年5月10日*/示例1-1 版權(quán)和版本的聲明1.2頭文件的結(jié)構(gòu)頭文件由三部分內(nèi)容組成:(1)頭文件開頭處的版權(quán)和版本聲明(參

16、見示例1-1)。(2)預處理塊。(3)函數(shù)和類結(jié)構(gòu)聲明等。假設頭文件名稱為graphics.h,頭文件的結(jié)構(gòu)參見示例1-2。【規(guī)則1-2-1】為了防止頭文件被重復引用,應當用ifndef/define/endif結(jié)構(gòu)產(chǎn)生預處理塊?!疽?guī)則1-2-2】用#include 格式來引用標準庫的頭文件(編譯器將從標準庫目錄開始搜索)?!疽?guī)則1-2-3】用#include“filename.h”格式來引用非標準庫的頭文件(編譯器將從用戶的工作目錄開始搜索)?!窘ㄗh1-2-1】頭文件中只存放“聲明”而不存放“定義”在C+語法中,類的成員函數(shù)可以在聲明的同時被定義,并且自動成為內(nèi)聯(lián)函數(shù)。這雖然會帶來書寫上的方

17、便,但卻造成了風格不一致,弊大于利。建議將成員函數(shù)的定義與聲明分開,不論該函數(shù)體有多么小?!窘ㄗh1-2-2】不提倡使用全局變量,盡量不要在頭文件中出現(xiàn)象extern int value這類聲明。/版權(quán)和版本聲明見示例1-1,此處省略。#ifndefGRAPHICS_H/防止graphics.h被重復引用#defineGRAPHICS_H#include /引用標準庫的頭文件#include“myheader.h”/引用非標準庫的頭文件void Function1();/ 全局函數(shù)聲明class Box/ 類結(jié)構(gòu)聲明;#endif示例1-2 C+/C頭文件的結(jié)構(gòu)1.3定義文件的結(jié)構(gòu)定義文件有三部

18、分內(nèi)容:(1)定義文件開頭處的版權(quán)和版本聲明(參見示例1-1)。(2)對一些頭文件的引用。(3)程序的實現(xiàn)體(包括數(shù)據(jù)和代碼)。假設定義文件的名稱為graphics.cpp,定義文件的結(jié)構(gòu)參見示例1-3。/版權(quán)和版本聲明見示例1-1,此處省略。#include“graphics.h”/引用頭文件/ 全局函數(shù)的實現(xiàn)體void Function1()/ 類成員函數(shù)的實現(xiàn)體void Box:Draw()示例1-3 C+/C定義文件的結(jié)構(gòu)1.4頭文件的作用早期的編程語言如Basic、Fortran沒有頭文件的概念,C+/C語言的初學者雖然會用使用頭文件,但常常不明其理。這里對頭文件的作用略作解釋:(1

19、)通過頭文件來調(diào)用庫功能。在很多場合,源代碼不便(或不準)向用戶公布,只要向用戶提供頭文件和二進制的庫即可。用戶只需要按照頭文件中的接口聲明來調(diào)用庫功能,而不必關心接口怎么實現(xiàn)的。編譯器會從庫中提取相應的代碼。(2)頭文件能加強類型安全檢查。如果某個接口被實現(xiàn)或被使用時,其方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這一簡單的規(guī)則能大大減輕程序員調(diào)試、改錯的負擔。1.5目錄結(jié)構(gòu)如果一個軟件的頭文件數(shù)目比較多(如超過十個),通常應將頭文件和定義文件分別保存于不同的目錄,以便于維護。例如可將頭文件保存于include目錄,將定義文件保存于source目錄(可以是多級目錄)。如果某些頭文件是私有

20、的,它不會被用戶的程序直接引用,則沒有必要公開其“聲明”。為了加強信息隱藏,這些私有的頭文件可以和定義文件存放于同一個目錄。第2章程序的版式版式雖然不會影響程序的功能,但會影響可讀性。程序的版式追求清晰、美觀,是程序風格的重要構(gòu)成因素??梢园殉绦虻陌媸奖扔鳛椤皶ā?。好的“書法”可讓人對程序一目了然,看得興致勃勃。差的程序“書法”如螃蟹爬行,讓人看得索然無味,更令維護者煩惱有加。請程序員們學習程序的“書法”,彌補大學計算機教育的漏洞,實在很有必要。2.1空行空行起著分隔程序段落的作用??招械皿w(不過多也不過少)將使程序的布局更加清晰??招胁粫速M內(nèi)存,雖然打印含有空行的程序是會多消耗一些紙張,

21、但是值得。所以不要舍不得用空行?!疽?guī)則2-1-1】在每個類聲明之后、每個函數(shù)定義結(jié)束之后都要加空行。參見示例2-1(a)【規(guī)則2-1-2】在一個函數(shù)體內(nèi),邏揖上密切相關的語句之間不加空行,其它地方應加空行分隔。參見示例2-1(b)/ 空行void Function1()/ 空行void Function2()/ 空行void Function3()/空行while (condition)statement1;/空行if (condition)statement2;elsestatement3;/空行statement4;示例2-1(a) 函數(shù)之間的空行示例2-1(b) 函數(shù)內(nèi)部的空行2.2代碼

22、行【規(guī)則2-2-1】一行代碼只做一件事情,如只定義一個變量,或只寫一條語句。這樣的代碼容易閱讀,并且方便于寫注釋?!疽?guī)則2-2-2】if、for、while、do等語句自占一行,執(zhí)行語句不得緊跟其后。不論執(zhí)行語句有多少都要加。這樣可以防止書寫失誤。示例2-2(a)為風格良好的代碼行,示例2-2(b)為風格不良的代碼行。int width;/ 寬度int height;/ 高度int depth;/ 深度int width, height, depth; / 寬度高度深度x = a + b;y = c + d;z = e + f;X a + b;y = c + d;z = e + f;if (w

23、idth height)dosomething();if (width =”、“=”、“+”、“*”、“%”、“&”、“|”、“”這類操作符前后不加空格?!窘ㄗh2-3-1】對于表達式比較長的for語句和if語句,為了緊湊起見可以適當?shù)厝サ粢恍┛崭?,如for (i=0; i10; i+)和if (a=b) & (c= 2000)/ 良好的風格if(year=2000)/ 不良的風格if (a=b) & (c=b&c=d)/ 不良的風格for (i=0; i10; i+)/ 良好的風格for(i=0;i10;i+)/ 不良的風格for (i = 0; I 10; i +)/ 過多的空格x = a

24、b ? a : b;/ 良好的風格x=aFunction();/ 不要寫成 b - Function();示例2-3 代碼行內(nèi)的空格2.4對齊【規(guī)則2-4-1】程序的分界符和應獨占一行并且位于同一列,同時與引用它們的語句左對齊?!疽?guī)則2-4-2】 之內(nèi)的代碼塊在右邊數(shù)格處左對齊。示例2-4(a)為風格良好的對齊,示例2-4(b)為風格不良的對齊。void Function(int x) /programcodevoid Function(int x) /programcodeif (condition) /programcodeelse /programcodeif (condition) /

25、programcodeelse /programcodefor (initialization; condition; update) /programcodefor (initialization; condition; update) /programcodeWhile (condition) /programcodewhile (condition) /programcode如果出現(xiàn)嵌套的,則使用縮進對齊,如:示例2-4(a)風格良好的對齊示例2-4(b)風格不良的對齊2.5長行拆分【規(guī)則2-5-1】代碼行最大長度宜控制在70至80個字符以內(nèi)。代碼行不要過長,否則眼睛看不過來,也不便于打

26、印。【規(guī)則2-5-2】長表達式要在低優(yōu)先級操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要進行適當?shù)目s進,使排版整齊,語句可讀。if (very_longer_variable1 = very_longer_variable12)& (very_longer_variable3 = very_longer_variable14)& (very_longer_variable5 Draw();/類的成員函數(shù)【規(guī)則3-1-8】用正確的反義詞組命名具有互斥意義的變量或相反動作的函數(shù)等。例如:intminValue;intmaxValue;intSetValue();intGe

27、tValue();【建議3-1-1】盡量避免名字中出現(xiàn)數(shù)字編號,如Value1,Value2等,除非邏輯上的確需要編號。這是為了防止程序員偷懶,不肯為命名動腦筋而導致產(chǎn)生無意義的名字(因為用數(shù)字編號最省事)。3.2簡單的Windows應用程序命名規(guī)則作者對“匈牙利”命名規(guī)則做了合理的簡化,下述的命名規(guī)則簡單易用,比較適合于Windows應用軟件的開發(fā)?!疽?guī)則3-2-1】類名和函數(shù)名用大寫字母開頭的單詞組合而成。例如:class Node;/ 類名class LeafNode;/ 類名voidDraw(void);/ 函數(shù)名voidSetValue(int value);/ 函數(shù)名【規(guī)則3-2-

28、2】變量和參數(shù)用小寫字母開頭的單詞組合而成。例如:BOOL flag;intdrawMode;【規(guī)則3-2-3】常量全用大寫的字母,用下劃線分割單詞。例如:const int MAX = 100;const int MAX_LENGTH = 100;【規(guī)則3-2-4】靜態(tài)變量加前綴s_(表示static)。例如:void Init()static int s_initValue;/靜態(tài)變量【規(guī)則3-2-5】如果不得已需要全局變量,則使全局變量加前綴g_(表示global)。例如:int g_howManyPeople;/全局變量int g_howMuchMoney;/全局變量【規(guī)則3-2-6】

29、類的數(shù)據(jù)成員加前綴m_(表示member),這樣可以避免數(shù)據(jù)成員與成員函數(shù)的參數(shù)同名。例如:void Object:SetValue(int width, int height)m_width = width;m_height = height;【規(guī)則3-2-7】為了防止某一軟件庫中的一些標識符和其它軟件庫中的沖突,可以為各種標識符加上能反映軟件性質(zhì)的前綴。例如三維圖形標準OpenGL的所有庫函數(shù)均以gl開頭,所有常量(或宏定義)均以GL開頭。3.3簡單的Unix應用程序命名規(guī)則第4章表達式和基本語句讀者可能懷疑:連if、for、while、goto、switch這樣簡單的東西也要探討編程風格

30、,是不是小題大做?我真的發(fā)覺很多程序員用隱含錯誤的方式寫表達式和基本語句,我自己也犯過類似的錯誤。表達式和語句都屬于C+/C的短語結(jié)構(gòu)語法。它們看似簡單,但使用時隱患比較多。本章歸納了正確使用表達式和語句的一些規(guī)則與建議。4.1運算符的優(yōu)先級C+/C語言的運算符有數(shù)十個,運算符的優(yōu)先級與結(jié)合律如表4-1所示。注意一元運算符+-*的優(yōu)先級高于對應的二元運算符。優(yōu)先級運算符結(jié)合律從高到低排列( ) -.從左至右!+-(類型)sizeof+-*&從右至左*/%從左至右+-從左至右從左至右=從左至右=!=從左至右&從左至右從左至右|從左至右&從左至右|從右至左?:從右至左=+=-=*=/=%=&=|=

31、從左至右表4-1運算符的優(yōu)先級與結(jié)合律【規(guī)則4-1-1】如果代碼行中的運算符比較多,用括號確定表達式的操作順序,避免使用默認的優(yōu)先級。由于將表4-1熟記是比較困難的,為了防止產(chǎn)生歧義并提高可讀性,應當用括號確定表達式的操作順序。例如:word = (high = b & c d & c + f = g + h ;/復合表達式過于復雜【規(guī)則4-2-2】不要有多用途的復合表達式。例如:d = (a = b + c) + r ;該表達式既求a值又求d值。應該拆分為兩個獨立的語句:a = b + c;d = a + r;【規(guī)則4-2-3】不要把程序中的復合表達式與“真正的數(shù)學表達式”混淆。例如:if

32、(a b c)/ a b c是數(shù)學表達式而不是程序表達式并不表示if (ab) & (bc)而是成了令人費解的if ( (ab)=”或“=-EPSINON) & (x=EPSINON)其中EPSINON是允許的誤差(即精度)。4.3.4 指針變量與零值比較【規(guī)則4-3-4】應當將指針變量用“=”或“!=”與NULL比較。指針變量的零值是“空”(記為NULL)。盡管NULL的值與0相同,但是兩者意義不同。假設指針變量的名字為p,它與零值比較的標準if語句如下:if (p = NULL)/ p與NULL顯式比較,強調(diào)p是指針變量if (p != NULL)不要寫成if (p = 0)/ 容易讓人誤

33、解p是整型變量if (p != 0)或者if (p)/ 容易讓人誤解p是布爾變量if (!p)4.3.5對if語句的補充說明有時候我們可能會看到if (NULL = p) 這樣古怪的格式。不是程序?qū)戝e了,是程序員為了防止將 if (p = NULL) 誤寫成 if (p = NULL),而有意把p和NULL顛倒。編譯器認為 if (p = NULL) 是合法的,但是會指出 if (NULL = p)是錯誤的,因為NULL不能被賦值。程序中有時會遇到if/else/return的組合,應該將如下不良風格的程序if (condition)return x;return y;改寫為if (condi

34、tion)return x;elsereturn y;或者改寫成更加簡練的return (condition ? x : y);4.4循環(huán)語句的效率C+/C循環(huán)語句中,for語句使用頻率最高,while語句其次,do語句很少用。本節(jié)重點論述循環(huán)體的效率。提高循環(huán)體效率的基本辦法是降低循環(huán)體的復雜性?!窘ㄗh4-4-1】在多重循環(huán)中,如果有可能,應當將最長的循環(huán)放在最內(nèi)層,最短的循環(huán)放在最外層,以減少CPU跨切循環(huán)層的次數(shù)。例如示例4-4(b)的效率比示例4-4(a)的高。for (row=0; row100; row+)for ( col=0; col5; col+ )sum = sum + a

35、rowcol;for (col=0; col5; col+ )for (row=0; row100; row+)sum = sum + arowcol;示例4-4(a) 低效率:長循環(huán)在最外層示例4-4(b) 高效率:長循環(huán)在最內(nèi)層【建議4-4-2】如果循環(huán)體內(nèi)存在邏輯判斷,并且循環(huán)次數(shù)很大,宜將邏輯判斷移到循環(huán)體的外面。示例4-4(c)的程序比示例4-4(d)多執(zhí)行了N-1次邏輯判斷。并且由于前者老要進行邏輯判斷,打斷了循環(huán)“流水線”作業(yè),使得編譯器不能對循環(huán)進行優(yōu)化處理,降低了效率。如果N非常大,最好采用示例4-4(d)的寫法,可以提高效率。如果N非常小,兩者效率差別并不明顯,采用示例4-

36、4(c)的寫法比較好,因為程序更加簡潔。for (i=0; iN; i+)if (condition)DoSomething();elseDoOtherthing();if (condition)for (i=0; iN; i+)DoSomething();elsefor (i=0; iN; i+)DoOtherthing();表4-4(c) 效率低但程序簡潔表4-4(d) 效率高但程序不簡潔4.5 for語句的循環(huán)控制變量【規(guī)則4-5-1】不可在for循環(huán)體內(nèi)修改循環(huán)變量,防止for循環(huán)失去控制?!窘ㄗh4-5-1】建議for語句的循環(huán)控制變量的取值采用“半開半閉區(qū)間”寫法。示例4-5(a)中

37、的x值屬于半開半閉區(qū)間“0 = x N”,起點到終點的間隔為N,循環(huán)次數(shù)為N。示例4-5(b)中的x值屬于閉區(qū)間“0 = x = N-1”,起點到終點的間隔為N-1,循環(huán)次數(shù)為N。相比之下,示例4-5(a)的寫法更加直觀,盡管兩者的功能是相同的。for (int x=0; xN; x+)for (int x=0; x 0 )*pbTo + = *pbFrom + ;return pvTo;示例6-5復制不重疊的內(nèi)存塊assert不是一個倉促拼湊起來的宏。為了不在程序的Debug版本和Release版本引起差別,assert不應該產(chǎn)生任何副作用。所以assert不是函數(shù),而是宏。程序員可以把as

38、sert看成一個在任何系統(tǒng)狀態(tài)下都可以安全使用的無害測試手段。如果程序在assert處終止了,并不是說含有該assert的函數(shù)有錯誤,而是調(diào)用者出了差錯,assert可以幫助我們找到發(fā)生錯誤的原因。很少有比跟蹤到程序的斷言,卻不知道該斷言的作用更讓人沮喪的事了。你化了很多時間,不是為了排除錯誤,而只是為了弄清楚這個錯誤到底是什么。有的時候,程序員偶爾還會設計出有錯誤的斷言。所以如果搞不清楚斷言檢查的是什么,就很難判斷錯誤是出現(xiàn)在程序中,還是出現(xiàn)在斷言中。幸運的是這個問題很好解決,只要加上清晰的注釋即可。這本是顯而易見的事情,可是很少有程序員這樣做。這好比一個人在森林里,看到樹上釘著一塊“危險”

39、的大牌子。但危險到底是什么?樹要倒?有廢井?有野獸?除非告訴人們“危險”是什么,否則這個警告牌難以起到積極有效的作用。難以理解的斷言常常被程序員忽略,甚至被刪除。Maguire, p8-p30【規(guī)則6-5-1】使用斷言捕捉不應該發(fā)生的非法情況。不要混淆非法情況與錯誤情況之間的區(qū)別,后者是必然存在的并且是一定要作出處理的?!疽?guī)則6-5-2】在函數(shù)的入口處,使用斷言檢查參數(shù)的有效性(合法性)?!窘ㄗh6-5-1】在編寫函數(shù)時,要進行反復的考查,并且自問:“我打算做哪些假定?”一旦確定了的假定,就要使用斷言對假定進行檢查?!窘ㄗh6-5-2】一般教科書都鼓勵程序員們進行防錯設計,但要記住這種編程風格可能

40、會隱瞞錯誤。當進行防錯設計時,如果“不可能發(fā)生”的事情的確發(fā)生了,則要使用斷言進行報警。6.6引用與指針的比較引用是C+中的概念,初學者容易把引用和指針混淆一起。一下程序中,n是m的一個引用(reference),m是被引用物(referent)。int m;int &n = m;n相當于m的別名(綽號),對n的任何操作就是對m的操作。例如有人名叫王小毛,他的綽號是“三毛”。說“三毛”怎么怎么的,其實就是對王小毛說三道四。所以n既不是m的拷貝,也不是指向m的指針,其實n就是m它自己。引用的一些規(guī)則如下:(1)引用被創(chuàng)建的同時必須被初始化(指針則可以在任何時候被初始化)。(2)不能有NULL引用

41、,引用必須與合法的存儲單元關聯(lián)(指針則可以是NULL)。(3)一旦引用被初始化,就不能改變引用的關系(指針則可以隨時改變所指的對象)。以下示例程序中,k被初始化為i的引用。語句k = j并不能將k修改成為j的引用,只是把k的值改變成為6。由于k是i的引用,所以i的值也變成了6。int i = 5;int j = 6;int &k = i;k = j;/ k和i的值都變成了6;上面的程序看起來象在玩文字游戲,沒有體現(xiàn)出引用的價值。引用的主要功能是傳遞函數(shù)的參數(shù)和返回值。C+語言中,函數(shù)的參數(shù)和返回值的傳遞方式有三種:值傳遞、指針傳遞和引用傳遞。以下是“值傳遞”的示例程序。由于Func1函數(shù)體內(nèi)的

42、x是外部變量n的一份拷貝,改變x的值不會影響n, 所以n的值仍然是0。void Func1(int x)x = x + 10;int n = 0;Func1(n);cout “n = ” n endl;/ n = 0以下是“指針傳遞”的示例程序。由于Func2函數(shù)體內(nèi)的x是指向外部變量n的指針,改變該指針的內(nèi)容將導致n的值改變,所以n的值成為10。void Func2(int *x)(* x) = (* x) + 10;int n = 0;Func2(&n);cout “n = ” n endl;/ n = 10以下是“引用傳遞”的示例程序。由于Func3函數(shù)體內(nèi)的x是外部變量n的引用,x和n

43、是同一個東西,改變x等于改變n,所以n的值成為10。void Func3(int &x)x = x + 10;int n = 0;Func3(n);cout “n = ” n endl;/ n = 10對比上述三個示例程序,會發(fā)現(xiàn)“引用傳遞”的性質(zhì)象“指針傳遞”,而書寫方式象“值傳遞”。實際上“引用”可以做的任何事情“指針”也都能夠做,為什么還要“引用”這東西?答案是“用適當?shù)墓ぞ咦銮∪缙浞值墓ぷ鳌薄V羔樐軌蚝翢o約束地操作內(nèi)存中的如何東西,盡管指針功能強大,但是非常危險。就象一把刀,它可以用來砍樹、裁紙、修指甲、理發(fā)等等,誰敢這樣用?如果的確只需要借用一下某個對象的“別名”,那么就用“引用”,

44、而不要用“指針”,以免發(fā)生意外。比如說,某人需要一份證明,本來在文件上蓋上公章的印子就行了,如果把取公章的鑰匙交給他,那么他就獲得了不該有的權(quán)利。第7章內(nèi)存管理歡迎進入內(nèi)存這片雷區(qū)。偉大的Bill Gates 曾經(jīng)失言:640K ought to be enough for everybodyBill Gates 1981程序員們經(jīng)常編寫內(nèi)存管理程序,往往提心吊膽。如果不想觸雷,唯一的解決辦法就是發(fā)現(xiàn)所有潛伏的地雷并且排除它們,躲是躲不了的。本章的內(nèi)容比一般教科書的要深入得多,讀者需細心閱讀,做到真正地通曉內(nèi)存管理。7.1內(nèi)存分配方式內(nèi)存分配方式有三種:(1)從靜態(tài)存儲區(qū)域分配。內(nèi)存在程序編譯

45、的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。例如全局變量,static變量。(2)在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。(3)從堆上分配,亦稱動態(tài)內(nèi)存分配。程序在運行的時候用malloc或new申請任意多少的內(nèi)存,程序員自己負責在何時用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期由我們決定,使用非常靈活,但問題也最多。7.2常見的內(nèi)存錯誤及其對策發(fā)生內(nèi)存錯誤是件非常麻煩的事情。編譯器不能自動發(fā)現(xiàn)這些錯誤,通常是在程序運行時才能捕捉到。而這些

46、錯誤大多沒有明顯的癥狀,時隱時現(xiàn),增加了改錯的難度。有時用戶怒氣沖沖地把你找來,程序卻沒有發(fā)生任何問題,你一走,錯誤又發(fā)作了。常見的內(nèi)存錯誤及其對策如下:內(nèi)存分配未成功,卻使用了它。編程新手常犯這種錯誤,因為他們沒有意識到內(nèi)存分配會不成功。常用解決辦法是,在使用內(nèi)存之前檢查指針是否為NULL。如果指針p是函數(shù)的參數(shù),那么在函數(shù)的入口處用assert(p!=NULL)進行檢查。如果是用malloc或new來申請內(nèi)存,應該用if(p=NULL) 或if(p!=NULL)進行防錯處理。內(nèi)存分配雖然成功,但是尚未初始化就引用它。犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為內(nèi)存的缺省初值全

47、為零,導致引用初值錯誤(例如數(shù)組)。內(nèi)存的缺省初值究竟是什么并沒有統(tǒng)一的標準,盡管有些時候為零值,我們寧可信其無不可信其有。所以無論用何種方式創(chuàng)建數(shù)組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。內(nèi)存分配成功并且已經(jīng)初始化,但操作越過了內(nèi)存的邊界。例如在使用數(shù)組時經(jīng)常發(fā)生下標“多1”或者“少1”的操作。特別是在for循環(huán)語句中,循環(huán)次數(shù)很容易搞錯,導致數(shù)組操作越界。忘記了釋放內(nèi)存,造成內(nèi)存泄露。含有這種錯誤的函數(shù)每被調(diào)用一次就丟失一塊內(nèi)存。剛開始時系統(tǒng)的內(nèi)存充足,你看不到錯誤。終有一次程序突然死掉,系統(tǒng)出現(xiàn)提示:內(nèi)存耗盡。動態(tài)內(nèi)存的申請與釋放必須配對,程序中malloc與free的使用

48、次數(shù)一定要相同,否則肯定有錯誤(new/delete同理)。釋放了內(nèi)存卻繼續(xù)使用它。有三種情況:(1)程序中的對象調(diào)用關系過于復雜,實在難以搞清楚某個對象究竟是否已經(jīng)釋放了內(nèi)存,此時應該重新設計數(shù)據(jù)結(jié)構(gòu),從根本上解決對象管理的混亂局面。(2)函數(shù)的return語句寫錯了,注意不要返回指向“棧內(nèi)存”的“指針”或者“引用”,因為該內(nèi)存在函數(shù)體結(jié)束時被自動銷毀。(3)使用free或delete釋放了內(nèi)存后,沒有將指針設置為NULL。導致產(chǎn)生“野指針”?!疽?guī)則7-2-1】用malloc或new申請內(nèi)存之后,應該立即檢查指針值是否為NULL。防止使用指針值為NULL的內(nèi)存?!疽?guī)則7-2-2】不要忘記為數(shù)

49、組和動態(tài)內(nèi)存賦初值。防止將未被初始化的內(nèi)存作為右值使用。【規(guī)則7-2-3】避免數(shù)組或指針的下標越界,特別要當心發(fā)生“多1”或者“少1”操作。【規(guī)則7-2-4】動態(tài)內(nèi)存的申請與釋放必須配對,防止內(nèi)存泄漏?!疽?guī)則7-2-5】用free或delete釋放了內(nèi)存之后,立即將指針設置為NULL,防止產(chǎn)生“野指針”。7.3指針與數(shù)組的對比C+/C程序中,指針和數(shù)組在不少地方可以相互替換著用,讓人產(chǎn)生一種錯覺,以為兩者是等價的。數(shù)組要么在靜態(tài)存儲區(qū)被創(chuàng)建(如全局數(shù)組),要么在棧上被創(chuàng)建。數(shù)組名對應著(而不是指向)一塊內(nèi)存,其地址與容量在生命期內(nèi)保持不變,只有數(shù)組的內(nèi)容可以改變。指針可以隨時指向任意類型的內(nèi)存

50、塊,它的特征是“可變”,所以我們常用指針來操作動態(tài)內(nèi)存。指針遠比數(shù)組靈活,但也更危險。下面以字符串為例比較指針與數(shù)組的特性。7.3.1修改內(nèi)容示例7-3-1中,字符數(shù)組a的容量是6個字符,其內(nèi)容為hello0。a的內(nèi)容可以改變,如a0= X。指針p指向常量字符串“world”(位于靜態(tài)存儲區(qū),內(nèi)容為world0),常量字符串的內(nèi)容是不可以被修改的。從語法上看,編譯器并不覺得語句p0= X有什么不妥,但是該語句企圖修改常量字符串的內(nèi)容而導致運行錯誤。char a =“hello”;a0 =X;cout a endl;char *p =“world”;/ 注意p指向常量字符串p0 =X;/ 編譯器

51、不能發(fā)現(xiàn)該錯誤cout p endl;示例7-3-1修改數(shù)組和指針的內(nèi)容7.3.2內(nèi)容復制與比較不能對數(shù)組名進行直接復制與比較。示例7-3-2中,若想把數(shù)組a的內(nèi)容復制給數(shù)組b,不能用語句 b = a ,否則將產(chǎn)生編譯錯誤。應該用標準庫函數(shù)strcpy進行復制。同理,比較b和a的內(nèi)容是否相同,不能用if(b=a) 來判斷,應該用標準庫函數(shù)strcmp進行比較。語句p = a 并不能把a的內(nèi)容復制指針p,而是把a的地址賦給了p。要想復制a的內(nèi)容,可以先用庫函數(shù)malloc為p申請一塊容量為strlen(a)+1個字符的內(nèi)存,再用strcpy進行字符串復制。同理,語句if(p=a) 比較的不是內(nèi)容

52、而是地址,應該用庫函數(shù)strcmp來比較。/ 數(shù)組char a = hello;char b10;strcpy(b, a);/ 不能用b = a;if(strcmp(b, a) = 0)/ 不能用if (b = a)/ 指針int len = strlen(a);char *p = (char *)malloc(sizeof(char)*(len+1);strcpy(p,a);/ 不要用 p = a;if(strcmp(p, a) = 0)/ 不要用 if (p = a)示例7-3-2數(shù)組和指針的內(nèi)容復制與比較7.3.3計算內(nèi)存容量用運算符sizeof可以計算出數(shù)組的容量(字節(jié)數(shù))。示例7-3

53、-3(a)中,sizeof(a)的值是12(注意別忘了0)。指針p指向a,但是sizeof(p)的值卻是4。這是因為sizeof(p)得到的是一個指針變量的字節(jié)數(shù),相當于sizeof(char*),而不是p所指的內(nèi)存容量。C+/C語言沒有辦法知道指針所指的內(nèi)存容量,除非在申請內(nèi)存時記住它。注意當數(shù)組作為函數(shù)的參數(shù)進行傳遞時,該數(shù)組自動退化為同類型的指針。示例7-3-3(b)中,不論數(shù)組a的容量是多少,sizeof(a)始終等于sizeof(char *)。char a = hello world;char *p= a;cout sizeof(a) endl;/ 12字節(jié)cout sizeof(

54、p) endl;/ 4字節(jié)示例7-3-3(a)計算數(shù)組和指針的內(nèi)存容量void Func(char a100)cout sizeof(a) endl;/ 4字節(jié)而不是100字節(jié)示例7-3-3(b)數(shù)組退化為指針7.4指針參數(shù)是如何傳遞內(nèi)存的?如果函數(shù)的參數(shù)是一個指針,不要指望用該指針去申請動態(tài)內(nèi)存。示例7-4-1中,Test函數(shù)的語句GetMemory(str, 200)并沒有使str獲得期望的內(nèi)存,str依舊是NULL,為什么?void GetMemory(char *p, int num)p = (char *)malloc(sizeof(char) * num);void Test(vo

55、id)char *str = NULL;GetMemory(str, 100);/ str 仍然為 NULLstrcpy(str, hello);/ 運行錯誤示例7-4-1試圖用指針參數(shù)申請動態(tài)內(nèi)存毛病出在函數(shù)GetMemory中。編譯器總是要為函數(shù)的每個參數(shù)制作臨時副本,指針參數(shù)p的副本是 _p,編譯器使 _p = p。如果函數(shù)體內(nèi)的程序修改了_p的內(nèi)容,就導致參數(shù)p的內(nèi)容作相應的修改。這就是指針可以用作輸出參數(shù)的原因。在本例中,_p申請了新的內(nèi)存,只是把_p所指的內(nèi)存地址改變了,但是p絲毫未變。所以函數(shù)GetMemory并不能輸出任何東西。事實上,每執(zhí)行一次GetMemory就會泄露一塊內(nèi)

56、存,因為沒有用free釋放內(nèi)存。如果非得要用指針參數(shù)去申請內(nèi)存,那么應該改用“指向指針的指針”,見示例7-4-2。void GetMemory2(char *p, int num)*p = (char *)malloc(sizeof(char) * num);void Test2(void)char *str = NULL;GetMemory2(&str, 100);/ 注意參數(shù)是 &str,而不是strstrcpy(str, hello);cout str endl;free(str);示例7-4-2用指向指針的指針申請動態(tài)內(nèi)存由于“指向指針的指針”這個概念不容易理解,我們可以用函數(shù)返回值來

57、傳遞動態(tài)內(nèi)存。這種方法更加簡單,見示例7-4-3。char *GetMemory3(int num)char *p = (char *)malloc(sizeof(char) * num);return p;void Test3(void)char *str = NULL;str = GetMemory3(100);strcpy(str, hello);cout str endl;free(str);示例7-4-3用函數(shù)返回值來傳遞動態(tài)內(nèi)存用函數(shù)返回值來傳遞動態(tài)內(nèi)存這種方法雖然好用,但是常常有人把return語句用錯了。這里強調(diào)不要用return語句返回指向“棧內(nèi)存”的指針,因為該內(nèi)存在函數(shù)結(jié)

58、束時自動消亡,見示例7-4-4。char *GetString(void)char p = hello world;return p;/ 編譯器將提出警告void Test4(void)char *str = NULL;str = GetString();/ str 的內(nèi)容是垃圾cout str endl;示例7-4-4 return語句返回指向“棧內(nèi)存”的指針用調(diào)試器逐步跟蹤Test4,發(fā)現(xiàn)執(zhí)行str = GetString語句后str不再是NULL指針,但是str的內(nèi)容不是“hello world”而是垃圾。如果把示例7-4-4改寫成示例7-4-5,會怎么樣?char *GetString

59、2(void)char *p = hello world;return p;void Test5(void)char *str = NULL;str = GetString2();cout str endl;示例7-4-5 return語句返回常量字符串函數(shù)Test5運行雖然不會出錯,但是函數(shù)GetString2的設計概念卻是錯誤的。因為GetString2內(nèi)的“hello world”是常量字符串,位于靜態(tài)存儲區(qū),它在程序生命期內(nèi)恒定不變。無論什么時候調(diào)用GetString2,它返回的始終是同一個“只讀”的內(nèi)存塊。7.5 free和delete把指針怎么啦?別看free和delete的名字惡

60、狠狠的(尤其是delete),它們只是把指針所指的內(nèi)存給釋放掉,但并沒有把指針本身干掉。用調(diào)試器跟蹤示例7-5,發(fā)現(xiàn)指針p被free以后其地址仍然不變(非NULL),只是該地址對應的內(nèi)存是垃圾,p成了“野指針”。如果此時不把p設置為NULL,會讓人誤以為p是個合法的指針。如果程序比較長,我們有時記不住p所指的內(nèi)存是否已經(jīng)被釋放,在繼續(xù)使用p之前,通常會用語句if (p != NULL)進行防錯處理。很遺憾,此時if語句起不到防錯作用,因為即便p不是NULL指針,它也不指向合法的內(nèi)存塊。char *p = (char *) malloc(100);strcpy(p,“hello”);free(p

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論