<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 第?1?章?程序的基本概念 **目錄** + [1\. 程序和編程語言](intro.program.html) + [2\. 自然語言和形式語言](intro.naturalformal.html) + [3\. 程序的調試](ch01s03.html) + [4\. 第一個程序](intro.helloworld.html) ## 1.?程序和編程語言 程序(Program)告訴計算機應如何完成一個計算任務,這里的計算可以是數學運算,比如解方程,也可以是符號運算,比如查找和替換文檔中的某個單詞。從根本上說,計算機是由數字電路組成的運算機器,只能對數字做運算,程序之所以能做符號運算,是因為符號在計算機內部也是用數字表示的。此外,程序還可以處理聲音和圖像,聲音和圖像在計算機內部必然也是用數字表示的,這些數字經過專門的硬件設備轉換成人可以聽到、看到的聲音和圖像。 程序由一系列指令(Instruction)組成,指令是指示計算機做某種運算的命令,通常包括以下幾類: 輸入(Input) 從鍵盤、文件或者其它設備獲取數據。 輸出(Output) 把數據顯示到屏幕,或者存入一個文件,或者發送到其它設備。 基本運算 執行最基本的數學運算(加減乘除)和數據存取。 測試和分支 測試某個條件,然后根據不同的測試結果執行不同的后續指令。 循環 重復執行一系列操作。 對于程序來說,有上面這幾類指令就足夠了。你曾用過的任何一個程序,不管它有多么復雜,都是由這幾類指令組成的。程序是那么的復雜,而編寫程序可以用的指令卻只有這么簡單的幾種,這中間巨大的落差就要由程序員去填了,所以編寫程序理應是一件相當復雜的工作。_編寫程序可以說就是這樣一個過程:把復雜的任務分解成子任務,把子任務再分解成更簡單的任務,層層分解,直到最后簡單得可以用以上指令來完成。_ 編程語言(Programming Language)分為低級語言(Low-level Language)和高級語言(High-level Language)。機器語言(Machine Language)和匯編語言(Assembly Language)屬于低級語言,直接用計算機指令編寫程序。而C、C++、Java、Python等屬于高級語言,用語句(Statement)編寫程序,語句是計算機指令的抽象表示。舉個例子,同樣一個語句用C語言、匯編語言和機器語言分別表示如下: **表?1.1.?一個語句的三種表示** | 編程語言 | 表示形式 | | --- | --- | | C語言 | a=b+1; | | 匯編語言 | `mov????0x804a01c,%eax` | | | `add????$0x1,%eax` | | | `mov????%eax,0x804a018` | | 機器語言 | `a1?1c?a0?04?08` | | | `83?c0?01` | | | `a3?18?a0?04?08` | 計算機只能對數字做運算,符號、聲音、圖像在計算機內部都要用數字表示,指令也不例外,上表中的機器語言完全由十六進制數字組成。最早的程序員都是直接用機器語言編程,但是很麻煩,需要查大量的表格來確定每個數字表示什么意思,編寫出來的程序很不直觀,而且容易出錯,于是有了匯編語言,把機器語言中一組一組的數字用助記符(Mnemonic)表示,直接用這些助記符寫出匯編程序,然后讓匯編器(Assembler)去查表把助記符替換成數字,也就把匯編語言翻譯成了機器語言。從上面的例子可以看出,匯編語言和機器語言的指令是一一對應的,匯編語言有三條指令,機器語言也有三條指令,匯編器就是做一個簡單的替換工作,例如在第一條指令中,把`movl ?,%eax`這種格式的指令替換成機器碼`a1 ?`,?表示一個地址,在匯編指令中是`0x804a01c`,轉換成機器碼之后是`1c a0 04 08`(這是指令中的十六進制數的小端表示,小端表示將在[第?5.1?節 “目標文件”](ch18s05.html#asm.relocatable)介紹)。 從上面的例子還可以看出,C語言的語句和低級語言的指令之間不是簡單的一一對應關系,一條`a=b+1;`語句要翻譯成三條匯編或機器指令,這個過程稱為編譯(Compile),由編譯器(Compiler)來完成,顯然編譯器的功能比匯編器要復雜得多。用C語言編寫的程序必須經過編譯轉成機器指令才能被計算機執行,編譯需要花一些時間,這是用高級語言編程的一個缺點,然而更多的是優點。首先,用C語言編程更容易,寫出來的代碼更緊湊,可讀性更強,出了錯也更容易改正。其次,C語言是可移植的(Portable)或者稱為平臺無關的(Platform Independent)。 平臺這個詞有很多種解釋,可以指計算機體系結構(Architecture),也可以指操作系統(Operating System),也可以指開發平臺(編譯器、鏈接器等)。不同的計算機體系結構有不同的指令集(Instruction Set),可以識別的機器指令格式是不同的,直接用某種體系結構的匯編或機器指令寫出來的程序只能在這種體系結構的計算機上運行,然而各種體系結構的計算機都有各自的C編譯器,可以把C程序編譯成各種不同體系結構的機器指令,這意味著用C語言寫的程序只需稍加修改甚至不用修改就可以在各種不同的計算機上編譯運行。各種高級語言都具有C語言的這些優點,所以絕大部分程序是用高級語言編寫的,只有和硬件關系密切的少數程序(例如驅動程序)才會用到低級語言。還要注意一點,即使在相同的體系結構和操作系統下,用不同的C編譯器(或者同一個C編譯器的不同版本)編譯同一個程序得到的結果也有可能不同,C語言有些語法特性在C標準中并沒有明確規定,各編譯器有不同的實現,編譯出來的指令的行為特性也會不同,應該盡量避免使用不可移植的語法特性。 總結一下編譯執行的過程,首先你用文本編輯器寫一個C程序,然后保存成一個文件,例如`program.c`(通常C程序的文件名后綴是`.c`),這稱為源代碼(Source Code)或源文件,然后運行編譯器對它進行編譯,編譯的過程并不執行程序,而是把源代碼全部翻譯成機器指令,再加上一些描述信息,生成一個新的文件,例如`a.out`,這稱為可執行文件,可執行文件可以被操作系統加載運行,計算機執行該文件中由編譯器生成的指令,如下圖所示: **圖?1.1.?編譯執行的過程** ![編譯執行的過程](https://box.kancloud.cn/2016-04-02_56ff80c8a8145.png) 有些高級語言以解釋(Interpret)的方式執行,解釋執行過程和C語言的編譯執行過程很不一樣。例如編寫一個Shell腳本`script.sh`,內容如下: ``` #! /bin/sh VAR=1 VAR=$(($VAR+1)) echo $VAR ``` 定義Shell變量`VAR`的初始值是1,然后自增1,然后打印`VAR`的值。用Shell程序`/bin/sh`解釋執行這個腳本,結果如下: ``` $ /bin/sh script.sh 2 ``` 這里的`/bin/sh`稱為解釋器(Interpreter),它把腳本中的每一行當作一條命令解釋執行,而不需要先生成包含機器指令的可執行文件再執行。如果把腳本中的這三行當作三條命令直接敲到Shell提示符下,也能得到同樣的結果: ``` $ VAR=1 $ VAR=$(($VAR+1)) $ echo $VAR 2 ``` **圖?1.2.?解釋執行的過程** ![解釋執行的過程](https://box.kancloud.cn/2016-04-02_56ff80d0434dc.png) 編程語言仍在發展演化。以上介紹的機器語言稱為第一代語言(1GL,1st Generation Programming Language),匯編語言稱為第二代語言(2GL,2nd Generation Programming Language),C、C++、Java、Python等可以稱為第三代語言(3GL,3rd Generation Programming Language)。目前已經有了4GL(4th Generation Programming Language)和5GL(5th Generation Programming Language)的概念。3GL的編程語言雖然是用語句編程而不直接用指令編程,但語句也分為輸入、輸出、基本運算、測試分支和循環等幾種,和指令有直接的對應關系。而4GL以后的編程語言更多是描述要做什么(Declarative)而不描述具體一步一步怎么做(Imperative),具體一步一步怎么做完全由編譯器或解釋器決定,例如SQL語言(SQL,Structured Query Language,結構化查詢語言)就是這樣的例子。 ### 習題 1、解釋執行的語言相比編譯執行的語言有什么優缺點? 這是我們的第一個思考題。本書的思考題通常要求讀者系統地總結當前小節的知識,結合以前的知識,并經過一定的推理,然后作答。本書強調的是基本概念,讀者應該抓住概念的定義和概念之間的關系來總結,比如本節介紹了很多概念:_程序_由_語句_或_指令_組成,計算機只能執行_低級語言_中的_指令_(匯編語言的指令要先轉成機器碼才能執行),_高級語言_要執行就必須先翻譯成低級語言,翻譯的方法有兩種--_編譯_和_解釋_,雖然有這樣的不便,但高級語言有一個好處是_平臺無關性_。什么是_平臺_?一種平臺,就是一種_體系結構_,就是一種_指令集_,就是一種_機器語言_,這些都可看作是一一對應的,上文并沒有用“一一對應”這個詞,但讀者應該能推理出這個結論,而高級語言和它們不是一一對應的,因此高級語言是_平臺無關_的,概念之間像這樣的數量對應關系尤其重要。那么編譯和解釋的過程有哪些不同?主要的不同在于什么時候翻譯和什么時候執行。 現在回答這個思考題,根據編譯和解釋的不同原理,你能否在執行效率和平臺無關性等方面做一下比較? 希望讀者掌握_以概念為中心的閱讀思考習慣_,每讀一節就總結一套概念之間的關系圖畫在書上空白處。如果讀到后面某一節看到一個講過的概念,但是記不清在哪一節講過了,沒關系,書后的索引可以幫你找到它是在哪一節定義的。 ## 2.?自然語言和形式語言 自然語言(Natural Language)就是人類講的語言,比如漢語、英語和法語。這類語言不是人為設計(雖然有人試圖強加一些規則)而是自然進化的。形式語言(Formal Language)是為了特定應用而人為設計的語言。例如數學家用的數字和運算符號、化學家用的分子式等。編程語言也是一種形式語言,是專門設計用來表達計算過程的形式語言。 形式語言有嚴格的語法(Syntax)規則,例如,3+3=6是一個語法正確的數學等式,而3=+6$則不是,H<sub>2</sub>O是一個正確的分子式,而<sub>2</sub>Zz則不是。語法規則是由符號(Token)和結構(Structure)的規則所組成的。Token的概念相當于自然語言中的單詞和標點、數學式中的數和運算符、化學分子式中的元素名和數字,例如3=+6$的問題之一在于$不是一個合法的數也不是一個事先定義好的運算符,而<sub>2</sub>Zz的問題之一在于沒有一種元素的縮寫是Zz。結構是指Token的排列方式,3=+6$還有一個結構上的錯誤,雖然加號和等號都是合法的運算符,但是不能在等號之后緊跟加號,而<sub>2</sub>Zz的另一個問題在于分子式中必須把下標寫在化學元素名稱之后而不是前面。關于Token的規則稱為詞法(Lexical)規則,而關于結構的規則稱為語法(Grammar)規則<sup>[[1](#ftn.id2704713)]</sup>。 當閱讀一個自然語言的句子或者一種形式語言的語句時,你不僅要搞清楚每個詞(Token)是什么意思,而且必須搞清楚整個句子的結構是什么樣的(在自然語言中你只是沒有意識到,但確實這樣做了,尤其是在讀外語時你肯定也意識到了)。這個分析句子結構的過程稱為解析(Parse)。例如,當你聽到“The other shoe fell.”這個句子時,你理解the other shoe是主語而fell是謂語動詞,一旦解析完成,你就搞懂了句子的意思,如果知道shoe是什么東西,fall意味著什么,這句話是在什么上下文(Context)中說的,你還能理解這個句子主要暗示的內容,這些都屬于語義(Semantic)的范疇。 雖然形式語言和自然語言有很多共同之處,包括Token、結構和語義,但是也有很多不一樣的地方。 歧義性(Ambiguity) 自然語言充滿歧義,人們通過上下文的線索和自己的常識來解決這個問題。形式語言的設計要求是清晰的、毫無歧義的,這意味著每個語句都必須有確切的含義而不管上下文如何。 冗余性(Redundancy) 為了消除歧義減少誤解,自然語言引入了相當多的冗余。結果是自然語言經常說得啰里啰嗦,而形式語言則更加緊湊,極少有冗余。 與字面意思的一致性 自然語言充斥著成語和隱喻(Metaphor),我在某種場合下說“The other shoe fell”,可能并不是說誰的鞋掉了。而形式語言中字面(Literal)意思基本上就是真實意思,也會有一些例外,例如下一章要講的C語言轉義序列,但即使有例外也會明確規定哪些字面意思不是真實意思,它們所表示的真實意思又是什么。 說自然語言長大的人(實際上沒有人例外),往往有一個適應形式語言的困難過程。某種意義上,形式語言和自然語言之間的不同正像詩歌和說明文的區別,當然,前者之間的區別比后者更明顯: 詩歌 詞語的發音和意思一樣重要,全詩作為一個整體創造出一種效果或者表達一種感情。歧義和非字面意思不僅是常見的而且是刻意使用的。 說明文 詞語的字面意思顯得更重要,并且結構能傳達更多的信息。詩歌只能看一個整體,而說明文更適合逐字句分析,但仍然充滿歧義。 程序 計算機程序是毫無歧義的,字面和本意高度一致,能夠完全通過對Token和結構的分析加以理解。 現在給出一些關于閱讀程序(包括其它形式語言)的建議。首先請記住形式語言遠比自然語言緊湊,所以要多花點時間來讀。其次,結構很重要,從上到下從左到右讀往往不是一個好辦法,而應該學會在大腦里解析:識別Token,分解結構。最后,請記住細節的影響,諸如拼寫錯誤和標點錯誤這些在自然語言中可以忽略的小毛病會把形式語言搞得面目全非。 * * * <sup>[[1](#id2704713)]</sup> 很不幸,Syntax和Grammar通常都翻譯成“語法”,這讓初學者非常混亂,Syntax的含義其實包含了Lexical和Grammar的規則,還包含一部分語義的規則,例如在C程序中變量應先聲明后使用。即使在英文的文獻中Syntax和Grammar也常混用,在有些文獻中Syntax的含義不包括Lexical規則,只要注意上下文就不會誤解。另外,本書在翻譯容易引起混淆的時候通常直接用英文名稱,例如Token沒有十分好的翻譯,直接用英文名稱。 ## 3.?程序的調試 編程是一件復雜的工作,因為是人做的事情,所以難免經常出錯。據說有這樣一個典故:早期的計算機體積都很大,有一次一臺計算機不能正常工作,工程師們找了半天原因最后發現是一只臭蟲鉆進計算機中造成的。從此以后,程序中的錯誤被叫做臭蟲(Bug),而找到這些Bug并加以糾正的過程就叫做調試(Debug)。有時候調試是一件非常復雜的工作,要求程序員概念明確、邏輯清晰、性格沉穩,還需要一點運氣。調試的技能我們在后續的學習中慢慢培養,但首先我們要區分清楚程序中的Bug分為哪幾類。 編譯時錯誤 編譯器只能翻譯語法正確的程序,否則將導致編譯失敗,無法生成可執行文件。對于自然語言來說,一點語法錯誤不是很嚴重的問題,因為我們仍然可以讀懂句子。而編譯器就沒那么寬容了,只要有哪怕一個很小的語法錯誤,編譯器就會輸出一條錯誤提示信息然后罷工,你就得不到你想要的結果。雖然大部分情況下編譯器給出的錯誤提示信息就是你出錯的代碼行,但也有個別時候編譯器給出的錯誤提示信息幫助不大,甚至會誤導你。在開始學習編程的前幾個星期,你可能會花大量的時間來糾正語法錯誤。等到有了一些經驗之后,還是會犯這樣的錯誤,不過會少得多,而且你能更快地發現錯誤原因。等到經驗更豐富之后你就會覺得,語法錯誤是最簡單最低級的錯誤,編譯器的錯誤提示也就那么幾種,即使錯誤提示是有誤導的也能夠立刻找出真正的錯誤原因是什么。相比下面兩種錯誤,語法錯誤解決起來要容易得多。 運行時錯誤 編譯器檢查不出這類錯誤,仍然可以生成可執行文件,但在運行時會出錯而導致程序崩潰。對于我們接下來的幾章將編寫的簡單程序來說,運行時錯誤很少見,到了后面的章節你會遇到越來越多的運行時錯誤。讀者在以后的學習中要時刻_注意區分編譯時和運行時(Run-time)這兩個概念_,不僅在調試時需要區分這兩個概念,在學習C語言的很多語法時都需要區分這兩個概念,有些事情在編譯時做,有些事情則在運行時做。 邏輯錯誤和語義錯誤 第三類錯誤是邏輯錯誤和語義錯誤。如果程序里有邏輯錯誤,編譯和運行都會很順利,看上去也不產生任何錯誤信息,但是程序沒有干它該干的事情,而是干了別的事情。當然不管怎么樣,計算機只會按你寫的程序去做,問題在于你寫的程序不是你真正想要的,這意味著程序的意思(即語義)是錯的。找到邏輯錯誤在哪需要十分清醒的頭腦,要通過觀察程序的輸出回過頭來判斷它到底在做什么。 通過本書你將掌握的最重要的技巧之一就是調試。調試的過程可能會讓你感到一些沮喪,但調試也是編程中最需要動腦的、最有挑戰和樂趣的部分。從某種角度看調試就像偵探工作,根據掌握的線索來推斷是什么原因和過程導致了你所看到的結果。調試也像是一門實驗科學,每次想到哪里可能有錯,就修改程序然后再試一次。如果假設是對的,就能得到預期的正確結果,就可以接著調試下一個Bug,一步一步逼近正確的程序;如果假設錯誤,只好另外再找思路再做假設。“當你把不可能的全部剔除,剩下的——即使看起來再怎么不可能——就一定是事實。”(即使你沒看過福爾摩斯也該看過柯南吧)。 也有一種觀點認為,編程和調試是一回事,編程的過程就是逐步調試直到獲得期望的結果為止。你應該總是從一個能正確運行的小規模程序開始,每做一步小的改動就立刻進行調試,這樣的好處是總有一個正確的程序做參考:如果正確就繼續編程,如果不正確,那么一定是剛才的小改動出了問題。例如,Linux操作系統包含了成千上萬行代碼,但它也不是一開始就規劃好了內存管理、設備管理、文件系統、網絡等等大的模塊,一開始它僅僅是Linus Torvalds用來琢磨Intel 80386芯片而寫的小程序。據Larry Greenfield 說,“Linus的早期工程之一是編寫一個交替打印AAAA和BBBB的程序,這玩意兒后來進化成了Linux。”(引自The Linux User's Guide Beta1版)在后面的章節中會給出更多關于調試和編程實踐的建議。 ## 4.?第一個程序 通常一本教編程的書中第一個例子都是打印“Hello, World.”,這個傳統源自[[K&R]](bi01.html#bibli.kr "The C Programming Language"),用C語言寫這個程序可以這樣寫: **例?1.1.?Hello World** ``` #include <stdio.h> /* main: generate some simple output */ int main(void) { printf("Hello, world.\n"); return 0; } ``` 將這個程序保存成`main.c`,然后編譯執行: ``` $ gcc main.c $ ./a.out Hello, world. ``` `gcc`是Linux平臺的C編譯器,編譯后在當前目錄下生成可執行文件`a.out`,直接在命令行輸入這個可執行文件的路徑就可以執行它。如果不想把文件名叫`a.out`,可以用`gcc`的`-o`參數自己指定文件名: ``` $ gcc main.c -o main $ ./main Hello, world. ``` 雖然這只是一個很小的程序,但我們目前暫時還不具備相關的知識來完全理解這個程序,比如程序的第一行,還有程序主體的`int main(void){...return 0;}`結構,這些部分我們暫時不詳細解釋,讀者現在只需要把它們看成是每個程序按慣例必須要寫的部分(Boilerplate)。但要注意`main`是一個特殊的名字,C程序總是從`main`里面的第一條語句開始執行的,在這個程序中是指`printf`這條語句。 第3行的`/* ... */`結構是一個注釋(Comment),其中可以寫一些描述性的話,解釋這段程序在做什么。注釋只是寫給程序員看的,編譯器會忽略從`/*`到`*/`的所有字符,所以寫注釋沒有語法規則,愛怎么寫就怎么寫,并且不管寫多少都不會被編譯進可執行文件中。 `printf`語句的作用是把消息打印到屏幕。注意語句的末尾以;號(Semicolon)結束,下一條語句`return 0;`也是如此。 C語言用{}括號(Brace或Curly Brace)把語法結構分成組,在上面的程序中`printf`和`return`語句套在`main`的{}括號中,表示它們屬于`main`的定義之中。我們看到這兩句相比`main`那一行都縮進(Indent)了一些,在代碼中可以用若干個空格(Blank)和Tab字符來縮進,縮進不是必須的,但這樣使我們更容易看出這兩行是屬于`main`的定義之中的,要寫出漂亮的程序必須有整齊的縮進,[第?1?節 “縮進和空白”](ch09s01.html#codingstyle.indent)將介紹推薦的縮進寫法。 正如前面所說,編譯器對于語法錯誤是毫不留情的,如果你的程序有一點拼寫錯誤,例如第一行寫成了`stdoi.h`,在編譯時會得到錯誤提示: ``` $ gcc main.c main.c:1:19: error: stdoi.h: No such file or directory ... ``` 這個錯誤提示非常緊湊,初學者往往不容易看明白出了什么錯誤,即使知道這個錯誤提示說的是第1行有錯誤,很多初學者對照著書看好幾遍也看不出自己這一行哪里有錯誤,因為他們對符號和拼寫不敏感(尤其是英文較差的初學者),他們還不知道這些符號是什么意思又如何能記住正確的拼寫?對于初學者來說,最想看到的錯誤提示其實是這樣的:“在`main.c`程序第1行的第19列,您試圖包含一個叫做`stdoi.h`的文件,可惜我沒有找到這個文件,但我卻找到了一個叫做`stdio.h`的文件,我猜這個才是您想要的,對嗎?”可惜沒有任何編譯器會友善到這個程度,大多數時候你所得到的錯誤提示并不能直接指出誰是犯人,而只是一個線索,你需要根據這個線索做一些偵探和推理。 有些時候編譯器的提示信息不是`error`而是`warning`,例如把上例中的`printf("Hello, world.\n");`改成`printf(1);`然后編譯運行: ``` $ gcc main.c main.c: In function ‘main’: main.c:7: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast $ ./a.out Segmentation fault ``` 這個警告信息是說類型不匹配,但勉強還能配得上。警告信息不是致命錯誤,編譯仍然可以繼續,如果整個編譯過程只有警告信息而沒有錯誤信息,仍然可以生成可執行文件。但是,警告信息也是不容忽視的。出警告信息說明你的程序寫得不夠規范,可能有Bug,雖然能編譯生成可執行文件,但程序的運行結果往往是不正確的,例如上面的程序運行時出了一個段錯誤,這屬于運行時錯誤。各種警告信息的嚴重程度不同,像上面這種警告幾乎一定表明程序中有Bug,而另外一些警告只表明程序寫得不夠規范,一般還是能正確運行的,有些不重要的警告信息`gcc`默認是不提示的,但這些警告信息也有可能表明程序中有Bug。_一個好的習慣是打開`gcc`的`-Wall`選項,也就是讓`gcc`提示所有的警告信息,不管是嚴重的還是不嚴重的,然后把這些問題從代碼中全部消滅_。比如把上例中的`printf("Hello, world.\n");`改成`printf(0);`然后編譯運行: ``` $ gcc main.c $ ./a.out ``` 編譯既不報錯也不報警告,一切正常,但是運行程序什么也不打印。如果打開`-Wall`選項編譯就會報警告了: ``` $ gcc -Wall main.c main.c: In function ‘main’: main.c:7: warning: null argument where non-null required (argument 1) ``` 如果`printf`中的0是你不小心寫上去的(例如錯誤地使用了編輯器的查找替換功能),這個警告就能幫助你發現錯誤。雖然本書的命令行為了突出重點通常省略`-Wall`選項,但是強烈建議你寫每一個編譯命令時都加上`-Wall`選項。 ### 習題 1、盡管編譯器的錯誤提示不夠友好,但仍然是學習過程中一個很有用的工具。你可以像上面那樣,從一個正確的程序開始每次改動一小點,然后編譯看是什么結果,如果出錯了,就盡量記住編譯器給出的錯誤提示并把改動還原。因為錯誤是你改出來的,你已經知道錯誤原因是什么了,所以能很容易地把錯誤原因和錯誤提示信息對應起來記住,這樣下次你在毫無防備的情況下撞到這個錯誤提示時就會很容易想到錯誤原因是什么了。這樣反復練習,有了一定的經驗積累之后面對編譯器的錯誤提示就會從容得多了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看