C程序設(shè)計(jì)語言(第二版)_第1頁
C程序設(shè)計(jì)語言(第二版)_第2頁
C程序設(shè)計(jì)語言(第二版)_第3頁
C程序設(shè)計(jì)語言(第二版)_第4頁
C程序設(shè)計(jì)語言(第二版)_第5頁
已閱讀5頁,還剩6頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

C程序設(shè)計(jì)語言(第二版)基本概念類型、運(yùn)算符與表達(dá)式一個(gè)對象的類型決定著該對象可取值的集合以及可以對該對象施行的運(yùn)算。2.2數(shù)據(jù)類型與大小1.在C語言中只有如下幾個(gè)基本數(shù)據(jù)類型:char單字節(jié),可以存放字符集中一個(gè)字符。int整數(shù),一般反映了宿主機(jī)上整數(shù)的自然大小。Float單精度浮點(diǎn)數(shù)。Double雙精度浮點(diǎn)數(shù)。此外,還有一些可用于限定這些基本類型的限定符。2.3常量1.諸如1234一類的整數(shù)常量是int常量。Long常量要以字母l或L結(jié)尾。無符號常量以字母u或U結(jié)尾,后綴ul或UL用于表示unsignedlong常量。(這里的常量其實(shí)就是指直接指定的一般數(shù)字或是字符字符串什么的)浮點(diǎn)常量必須包含一個(gè)小數(shù)點(diǎn)或指數(shù)(如1e-1)或兩者都包含,在沒有后綴時(shí)類型為double。后綴f與F用于指定float常量,而后綴l或L則用于指定字符常量是一個(gè)整數(shù),寫成用單引號括住單個(gè)字符的形式,如‘x’。字符常量的值是該字符在機(jī)器字符集中的數(shù)值。常量表達(dá)式時(shí)其中只涉及到常量的表達(dá)式。這種表達(dá)式可以在編譯時(shí)計(jì)算而不必推遲到運(yùn)算時(shí),因而可以用在常量可以出現(xiàn)的任何位置,例如由define定義的宏。字符串常量也叫字符串字面值,是用雙引號括住的由0個(gè)或多個(gè)字符組成的字符序列。從技術(shù)絕度看,字符串常量就是字符數(shù)組。在內(nèi)部表示字符串時(shí)要用一個(gè)空字符’\0’來結(jié)尾,故用于存儲字符串的物理存儲單元數(shù)比括在雙引號中的字符數(shù)多一個(gè)。這種表示發(fā)意味著,C語言對字符串的長度沒有限制,但是程序必須掃描完整個(gè)字符串才能決定這個(gè)字符串的長度。枚舉常量。枚舉是常量整數(shù)值的列表。不同的枚舉中的名字必須各不相同,同一枚舉中各個(gè)名字的值不要求不同。枚舉是使常量值與名字相關(guān)聯(lián)的又一種方便的方法,其相對于#define語句的優(yōu)勢是常量值可以由自己控制。2.4說明其實(shí)就是聲明。如果所涉及的變量不是自動(dòng)變量(就是局部變量),那么只初始化一次,而且從概念上講應(yīng)該在程序開始執(zhí)行之前進(jìn)行,此時(shí)要求初始化符必須為常量表達(dá)式。顯示初始化的自動(dòng)變量每當(dāng)進(jìn)入其所在的函數(shù)或分程序時(shí)就進(jìn)行一次初始化,其初始化符可以是任何表達(dá)式。外部變量與靜態(tài)變量的缺省值為0。未經(jīng)顯式初始化的自動(dòng)變量的值為未定義值(即垃圾)。2.5算術(shù)運(yùn)算符2.6關(guān)系運(yùn)算符與邏輯運(yùn)算符2.7類型轉(zhuǎn)換1.當(dāng)一個(gè)運(yùn)算符的幾個(gè)運(yùn)算分量的類型不同時(shí),要根據(jù)一些規(guī)則把它們轉(zhuǎn)換成某個(gè)共同的類型。一般而言,只能把“比較窄的”運(yùn)算分量自動(dòng)轉(zhuǎn)換成“比較寬的”運(yùn)算分量,這樣才能不丟失信息。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第1頁。2.char類型就是小整數(shù)類型,在算術(shù)表達(dá)式中可以自由地使用char類型的變量或常量。這就使得在某些字符轉(zhuǎn)換中有了很大的靈活性。但是在將字符轉(zhuǎn)換成整數(shù)時(shí)有一點(diǎn)微妙。C語言沒有指定char類型變量時(shí)無符號還是有符號量。當(dāng)把一個(gè)char類型的值轉(zhuǎn)換成int類型的值時(shí),其結(jié)果是不是負(fù)整數(shù)?結(jié)果視機(jī)器的不同而有所變化,反映了不同機(jī)器結(jié)構(gòu)之間的區(qū)別。在某些機(jī)器上,如果字符的最左一位為1,那么就被轉(zhuǎn)換成負(fù)整數(shù)。在另一些機(jī)器上,采取的是提升的方法,通過在最左邊加上0把字符提升為整數(shù),這樣的轉(zhuǎn)換結(jié)果總是正的。C語言的定義保證了機(jī)器的標(biāo)準(zhǔn)可打印字符集中的字符不會是負(fù)的,故在表達(dá)式中這些字符總是正的。但是,字符變量存儲的位模式在某些機(jī)器上可能是負(fù)的,而在另一些機(jī)器上卻是正的。為了保證程序的可移植性,如果要在char變量中存儲非字符數(shù)據(jù),那么最好指定signed或unsigned限定符。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第1頁。3.當(dāng)表達(dá)式中包含unsigned類型的運(yùn)算分量時(shí),轉(zhuǎn)換規(guī)則要復(fù)雜一些。主要問題是,在有符號與無符號值之間的比較運(yùn)算取決于機(jī)器,因?yàn)樗鼈內(nèi)Q于各個(gè)整數(shù)類型的大小。例如,假定int對象占16位,long對象占32位,那么,-1L<1U,這是因?yàn)閕nt類型的-1U被提升為signedlong類型;但是-1L>1UL,這是因?yàn)?1L被提升為unsingedlong類型,因此它是一個(gè)比較大的正數(shù)。4.在進(jìn)行賦值時(shí)要進(jìn)行類型轉(zhuǎn)換,=右邊的值要轉(zhuǎn)換成左邊變量的類型,后者即賦值表達(dá)式結(jié)果的類型。不管是否要進(jìn)行符號擴(kuò)展,字符值都要轉(zhuǎn)換成整數(shù)值。當(dāng)把較長的整型數(shù)轉(zhuǎn)換成較短的整型數(shù)或字符時(shí),要把超出的高位部分截掉。5.由于函數(shù)調(diào)用的變元是表達(dá)式,當(dāng)把變元傳遞給函數(shù)時(shí)也可能引起類型轉(zhuǎn)換。在沒有函數(shù)原型的情況下,char與short類型轉(zhuǎn)換為int類型,float轉(zhuǎn)換為double型,這就是即使在函數(shù)使用char與float類型的變元表達(dá)式調(diào)用時(shí)仍把參數(shù)說明成int與float的原因。2.8加一與減一運(yùn)算符2.9按位運(yùn)算符2.10賦值運(yùn)算符與賦值表達(dá)式2.11條件表達(dá)式2.12運(yùn)算符優(yōu)先級與表達(dá)式求值次序 同一行的各個(gè)運(yùn)算符具有相同的優(yōu)先級,縱向看越往下優(yōu)先級越低。 運(yùn)算符結(jié)合律()[]->從左至右!~++--+-*&(類型)sizeof從右至左*/%從左至右+-從左至右<<>>從左至右<<=>>=從左至右==!=從左至右&從左至右^從左至右|從左至右&&從左至右||從左至右?:從右至左=+=-=*=/=%=&=^=|=<<=>>=從右至左.從左至右控制流函數(shù)與程序結(jié)構(gòu)C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第2頁。4.1函數(shù)的基本知識C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第2頁。1.程序是變量定義和函數(shù)定義的結(jié)合。函數(shù)之間的通信可以通過變元、函數(shù)返回值以及外部變量進(jìn)行。函數(shù)可以以任意次序出現(xiàn)在原文件中。源程序可以分成多個(gè)文件,只要不把一個(gè)函數(shù)分在幾個(gè)文件中就行。4.3外部變量1.C程序由一組外部對象(外部變量或函數(shù))組成。外部變量在函數(shù)外面定義,故可以在很多函數(shù)中使用。由于C語言不允許在一個(gè)函數(shù)中定義其他函數(shù),因此函數(shù)本身是外部的。在缺省情況下,外部變量與函數(shù)具有如下性質(zhì):所有通過名字對外部變量與函數(shù)的引用都是引用的同一對象。4.4作用域規(guī)則4.5頭文件4.6靜態(tài)變量1.static說明適用于外部變量與函數(shù),用于把這些對象的作用域限定為被編譯源文件的剩余部分。2.外部static說明最常用于說明變量,當(dāng)然它也可以用于說明函數(shù)。通常情況下,函數(shù)名字是全局的,在整個(gè)程序的各個(gè)部分都可見。然而,如果把一個(gè)函數(shù)說明稱靜態(tài)的,那么該函數(shù)名字就不能用在除該函數(shù)說明所在的文件之外的其他文件中。Static說明也可用于說明內(nèi)部變量。內(nèi)部靜態(tài)變量就像自動(dòng)變量一樣局部于某一特定函數(shù),只能在該函數(shù)中使用,但與自動(dòng)變量不同的是,不管其所在函數(shù)是否被調(diào)用,它都一直是存在的,而不像自動(dòng)變量那樣,隨著所在函數(shù)的調(diào)用與退出而存在與消失。4.7寄存器變量Register說明用于提醒編譯程序所說明的變量在程序中使用頻率較高。其思想是,將寄存器變量放在機(jī)器的寄存器中,這樣可以是程序更小、執(zhí)行速度更快。但編譯器可以忽略此選項(xiàng)。寄存器說明只適用于自動(dòng)變量以及函數(shù)的形式參數(shù)。所有寄存器變量的地址都是不能訪問的。4.9初始化1.在沒有顯示初始化的情況下,外部變量與靜態(tài)變量都被初始化為0,而自動(dòng)變量與寄存器變量的初值則沒有定義(即,其初值是“垃圾”)。4.10遞歸4.11C預(yù)處理程序第五章指針與數(shù)組 5.1指針與地址 1.取地址運(yùn)算符&只能應(yīng)用于內(nèi)存中的對象(即變量與數(shù)組元素),它不能對表達(dá)式、常量或寄存器變量進(jìn)行操作。 5.3指針與數(shù)組1.數(shù)組下標(biāo)所能完成的任何運(yùn)算都可以用指針來實(shí)現(xiàn)。一般而言,指針運(yùn)算比數(shù)組下標(biāo)運(yùn)算的速度快。在對數(shù)組進(jìn)行下標(biāo)運(yùn)算,即求a[i]的值時(shí),C語言實(shí)際上是先將其轉(zhuǎn)化成*(a+i)的形式然后再進(jìn)行求值,因而在程序中這兩種形式等價(jià)。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第3頁。2.必須注意到,數(shù)組名字和指針之間仍然存在著一點(diǎn)區(qū)別。指針是變量,因而在C語言中,語句pa=a和pa++都是合法的。但是數(shù)組名字不是變量,因而諸如a=pa(這個(gè)是指對整個(gè)數(shù)組從一個(gè)到另一個(gè)的整體賦值)和a++(這是指對數(shù)組名執(zhí)行自加運(yùn)算,其實(shí)可以運(yùn)用a+i的形式,不知為啥a++不行)這樣的語句是非法的。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第3頁。3.當(dāng)把一個(gè)數(shù)組名字傳遞給一個(gè)函數(shù)時(shí),實(shí)際上傳遞的是該數(shù)組第一個(gè)元素的位置。也可以通過傳遞指向子數(shù)組的指針的方法把數(shù)組的一部分作為參數(shù)傳遞給函數(shù)。例如f(&a[2])。5.4地址算術(shù)運(yùn)算1.有效的指針運(yùn)算包括:相同類型指針之間的賦值運(yùn)算;指針值加或減一個(gè)整數(shù)值的運(yùn)算;指向相同數(shù)組中的元素的指針之間的減或比較運(yùn)算;將指針賦0或指針與0之間的比較運(yùn)算。所有其他形式的指針運(yùn)算均非法,諸如下列形式的運(yùn)算就是非法的指針運(yùn)算:指針間的加法、乘法、除法或屏蔽運(yùn)算;指針值加單雙精度浮點(diǎn)數(shù)的運(yùn)算;除兩者之一是void*類型指針外,不經(jīng)強(qiáng)制類型轉(zhuǎn)換就將指向一種類型對象的指針賦給指向另一種類型對象的指針的運(yùn)算。5.5字符指針與函數(shù)charamessage[]=“nowisthetime”;/*定義一個(gè)數(shù)組*/char*pmessage=“nowisthetime”;/*定義一個(gè)指針*/上述說明中,amessage是一個(gè)不可改變的常量,它總指向同一片存儲區(qū)。另一方面,pmessage是一個(gè)指針,其初值指向一個(gè)字符串常量,之后它可以被修改指向其他地址,但是如果試圖修改字符串的內(nèi)容,結(jié)果將不確定。5.6指針數(shù)組與指向指針的指針1.例如定義char*line[20],那么請注意,這時(shí)首先根據(jù)運(yùn)算符優(yōu)先級規(guī)則,line是一個(gè)數(shù)組,又由于有*的修飾,所以他是一個(gè)指針數(shù)組,即數(shù)組里面存儲的全部是指針。5.7多維數(shù)組1.數(shù)組在內(nèi)存中按行存儲。如果要將二維數(shù)組作為變元傳遞給函數(shù),那么函數(shù)的參數(shù)說明中應(yīng)該指明相應(yīng)數(shù)組的列數(shù),數(shù)組的行數(shù)不必指定。5.9指針與多維數(shù)組注意inta[10][20];int*b[10];這兩個(gè)的區(qū)別,對于b來說,每一維的長度可以不一致。5.10命令行變元5.11指向函數(shù)的指針1.在C語言中,函數(shù)本身不是變量,但可以定義指向函數(shù)的指針,這種指針可以被賦值、存放于數(shù)組中、傳遞給函數(shù)及作為函數(shù)返回值等等。2.定義指向函數(shù)的指針:return_type(*fun_name)(參數(shù)列表);調(diào)用則為(*fun_name)(實(shí)參);5.12復(fù)雜說明1.見定義:char**argv;argv:指向字符指針的指針int(*daytab)[13];daytab:指向由13個(gè)整形類型元素組成的一維數(shù)組的指針(這就是數(shù)組指針,指向數(shù)組的指針。就是一指針)。(這里這么用,intnArray[3]={1,2,3};int(*pArray)[3]=&nArray;)int*daytab[13];daytab:由13個(gè)指向整數(shù)類型對象的指針組成的一維數(shù)組(存儲的是13個(gè)指針)。void*comp();comp:返回值為指向通用類型的指針的函數(shù)。void(*comp)();comp:指向返回值為通用類型的函數(shù)的指針。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第4頁。char(*(*X())[])();X:返回值為指向一維數(shù)組的指針的函數(shù),該一維數(shù)組由指向返回字符類型的函數(shù)指針組成。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第4頁。char(*(*X[3])())[5];X:由3個(gè)指向函數(shù)的指針組成的一維數(shù)組,該函數(shù)返回指向由5個(gè)字符組成的一維數(shù)組的指針。 對于這種復(fù)雜的聲明,可以采用基于說明符的方式進(jìn)行解讀: 說明符:可選的*序列直接說明符 直接說明符:名字 (說明符) 直接說明符() 直接說明符[可選的大小] 簡而言之,說明符即前面也許帶有符號*的直接說明符。直接說明符可以是名字、由一對圓括號括住的說明符、后面跟有一對圓括號的直接說明符或后面跟有由方括號括住可選大小的直接說明符。 例如:(*pfa[])(),pfa首先是一個(gè)直接說明符,于是pfa[]也是一個(gè)直接說明符。接著*pfa[]被識別出是一個(gè)說明符,因而(*pfa[])是一個(gè)直接說明符。 于是,按照此規(guī)則,對于char(*(*X[3])())[5]作分析:X是一個(gè)直接說明符,那么*X[3]是一個(gè)說明符,這就定義了一個(gè)指針數(shù)組,類型待定。接著(*X[3])是一個(gè)直接說明符,那么(*X[3])()也是,它表示這是一個(gè)函數(shù)指針數(shù)組,接著*(*X[3])()表示這個(gè)函數(shù)返回的是一個(gè)指針,(*(*X[3])())表示這是一個(gè)直接說明符,char(*(*X[3])())[5]到這里表示這個(gè)函數(shù)返回由5個(gè)字符組成的一維數(shù)組的指針。 2.對于這種復(fù)雜的定義,有著名的右左法則:首先從最里面的圓括號看起,然后往右看,在往左看。每當(dāng)遇到圓括號時(shí),就應(yīng)該掉轉(zhuǎn)閱讀方向。一旦解析完圓括號里面所有的東西,就跳出圓括號。重復(fù)這個(gè)過程直到整個(gè)聲明解析完畢。 這里,應(yīng)該對這個(gè)法則作一個(gè)小小的修正,應(yīng)該從未定義的標(biāo)識符開始閱讀,即不是C語言的關(guān)鍵字開始。仍舊對上例說明:首先X是個(gè)未定義的標(biāo)識符,往右看是個(gè)[],說明這是個(gè)數(shù)組,再往右,遇到括號則往左,有*說明這是個(gè)指針數(shù)組。在往左,遇到括號,此時(shí)括號里面的東西解析完畢。接著括號外面往由,直接遇到括號,說明這是個(gè)函數(shù),即指針數(shù)組里的指針式函數(shù)指針。跳轉(zhuǎn)往左,遇到*說明函數(shù)返回值為指針,跳轉(zhuǎn)往右,括號則整個(gè)跳出,再往右,[],說明這是一個(gè)指向數(shù)組的指針。第六章結(jié)構(gòu)6.2結(jié)構(gòu)與函數(shù)1.對結(jié)構(gòu)的合法操作只有拷貝、作為一個(gè)單元對其賦值、通過&取其地址及訪問結(jié)構(gòu)成員這幾種。6.5自引用結(jié)構(gòu)即在結(jié)構(gòu)內(nèi)部定義指向自己的指針。6.9位字段1.當(dāng)存儲空間很寶貴時(shí),有必要將幾個(gè)對象打包到一個(gè)單一機(jī)器中去。一個(gè)常用的方法是使用類似編譯程序中符號表的單個(gè)位標(biāo)志集合。外部使用的數(shù)據(jù)格式(如硬件接口設(shè)備)也常需要能從字的部分位中讀取數(shù)據(jù)。2.有關(guān)字段對齊的規(guī)則請查資料。C專家編程C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第5頁。第四章數(shù)組和指針并不相同C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第5頁。4.2我的代碼為什么無法運(yùn)行1.對于以下定義:文件1:intmango[100];文件2:externint*mango;這種情況是不行的。雖說對數(shù)組的引用總是可以寫成對指針的引用,而且確實(shí)存在一種指針和數(shù)組的定義完全相同的上下文環(huán)境。但是并不是總是這樣。4.3什么是聲明,什么是定義1.C語言中的對象必須有且只有一個(gè)定義,但他可以有多個(gè)extern聲明。定義是一種特殊的聲明,它創(chuàng)建了一個(gè)對象;聲明簡單的說明了在其他地方創(chuàng)建的對象的名字,它允許你使用這個(gè)名字。extern對象聲明告訴編譯器對象的類型和名字,對象的內(nèi)存分配則在別處進(jìn)行。2.C語言引入了“可修改的左值”這個(gè)術(shù)語。它表示左值允許出現(xiàn)在賦值語句的左邊。這個(gè)奇怪的術(shù)語是為與數(shù)組名區(qū)分,數(shù)組名也用于確定對象在內(nèi)存中的位置,也是左值,但它不能作為賦值的對象。因此,數(shù)組名是個(gè)左值但不是可修改的左值。標(biāo)準(zhǔn)規(guī)定賦值符必須用可修改的左值作為他左側(cè)的操作數(shù)。通俗的說,只能給可以修改的東西賦值。3.出現(xiàn)在賦值符左邊的符號有時(shí)被稱為左值,出現(xiàn)在賦值符右邊的符號有時(shí)則被稱為右值。編譯器為每個(gè)變量分配一個(gè)地址(左值)。這個(gè)地址在編譯時(shí)可知,而且該變量在運(yùn)行時(shí)一直保存于這個(gè)地址。相反,存儲于變量中的值(它的右值)(對于這個(gè)地方解釋,例如x=y,這時(shí)y便稱為“它的右值”)只有在運(yùn)行時(shí)才可知。如果需要用到變量中存儲的值,編譯器就發(fā)出指令從指定地址讀入變量值并將它存于寄存器中。4.這里強(qiáng)調(diào)一點(diǎn),對于所有的變量,在編譯時(shí)都會分配一個(gè)地址,而且在編譯時(shí)可知。那么我們引用這個(gè)變量,就是相當(dāng)于直接對應(yīng)了這個(gè)地址。對于一般的變量,我們引用定義的名字就引用了編譯時(shí)分配的對應(yīng)地址里的內(nèi)容;對于指針變量,由于變量對應(yīng)的是一個(gè)表示地址的地址,所以我們要解引用。請?zhí)貏e注意理解這里?。?!5.externchara[]與externchara[100]等價(jià)的原因:這兩個(gè)聲明都提示a是一個(gè)數(shù)組,也就是一個(gè)內(nèi)存地址,數(shù)組內(nèi)的字符可以從這個(gè)地址找到。編譯器并不需要知道數(shù)組總共有多長,因?yàn)樗划a(chǎn)生偏離起始地址的偏移地址。6.當(dāng)你“定義為指針,但以數(shù)組方式引用”時(shí)會發(fā)生什么:其實(shí),無論對于你定義為指針卻以數(shù)組的方式引用,還是定義為數(shù)字卻以指針的方式引用,都會出現(xiàn)錯(cuò)誤(作為函數(shù)參數(shù)可以)。因?yàn)楫?dāng)另外去聲明這個(gè)變量的時(shí)候,接下來就會以你聲明的這種方式去引用這個(gè)變量。但是二者引用的原理卻不一樣:定義為指針聲明為數(shù)組,引用的時(shí)候會直接根據(jù)這個(gè)地址去算偏移量,而不是先解引用,所以會出錯(cuò)。定義為數(shù)組聲明為指針則會先去解引用,這樣對一個(gè)不是地址的變量去解引用必然會出錯(cuò)。4.5數(shù)組和指針的其他區(qū)別1.指針和數(shù)組都可以在它們的定義中用字符串常量進(jìn)行初始化。盡管看上去一樣,底層的機(jī)理卻不相同。定義指針時(shí),編譯器并不為指針?biāo)赶虻膶ο蠓峙淇臻g,它只是分配指針本身的空間,除非在定義時(shí)同時(shí)賦給指針一個(gè)字符串常量進(jìn)行初始化。2.在ANSIC中,初始化指針時(shí)所創(chuàng)建的字符串常量被定義為只讀。與指針相反,由字符串常量初始化的數(shù)組是可以修改的。第五章對鏈接的思考5.1函數(shù)庫、鏈接和載入C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第6頁。1.鏈接器基礎(chǔ)知識:編譯器創(chuàng)建一個(gè)輸出文件,這個(gè)文件包含了可重定位的對象。這些對象就是與源程序?qū)?yīng)的數(shù)據(jù)和機(jī)器指令。絕大多數(shù)編譯器并不是一個(gè)單一的龐大程序。他們通常由多大六七個(gè)稍小的程序所組成,這些程序由一個(gè)叫做“編譯器驅(qū)動(dòng)器”的控制程序來調(diào)用。這些可以方便地從編譯器中分離出來的單獨(dú)程序包括:預(yù)處理器、語法和語義檢查器、代碼生成器、匯編程序、優(yōu)化器、鏈接器,還包括一個(gè)調(diào)用所有這些程序并向各個(gè)程序傳遞正確選項(xiàng)的驅(qū)動(dòng)器程序。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第6頁。2.Gcc的編譯流程:(1)預(yù)處理階段:將必要的文件包含進(jìn)來gcc-Ehello.c-ohello.i(2)編譯階段:這個(gè)對應(yīng)的是語法和語義檢查器、代碼生成器。在這個(gè)階段中,Gcc首先要檢查代碼的規(guī)范性、是否有語法錯(cuò)誤等,以確定代碼的實(shí)際要做的工作,在檢查無誤后,Gcc把代碼翻譯成匯編語言gcc-shello.i-ohello.s(3)匯編階段:匯編階段是把編譯階段生成的”.s”文件轉(zhuǎn)成目標(biāo)文件,讀者在此可使用選項(xiàng)”-c”就可看到匯編代碼已轉(zhuǎn)化為”.o”的二進(jìn)制目標(biāo)代碼了gcc-chello.s-ohello.o(4)鏈接階段:3.目標(biāo)文件(這里只編譯進(jìn)行到匯編(把匯編語言代碼翻譯成目標(biāo)機(jī)器指令的過程)之后的文件,等鏈接完后就成了可執(zhí)行文件)不能直接執(zhí)行,它首先需要載入到鏈接器中。鏈接器確認(rèn)main函數(shù)為初始進(jìn)入點(diǎn),把符號引用綁定到內(nèi)存地址,把所有的目標(biāo)文件集中在一起,在加上庫文件,從而產(chǎn)生可執(zhí)行文件。4.如果函數(shù)庫(這個(gè)函數(shù)庫是指已經(jīng)編譯好的二進(jìn)制文件)的一份拷貝是可執(zhí)行文件的物理組成部分,那么我們稱之為靜態(tài)鏈接;如果可執(zhí)行文件只是包含了文件名,讓載入器在運(yùn)行時(shí)能夠?qū)ふ页绦蛩枰暮瘮?shù)庫,那么我們稱之為動(dòng)態(tài)鏈接。收集模塊準(zhǔn)備執(zhí)行的三個(gè)階段的規(guī)范名稱是鏈接-編輯、載入和運(yùn)行時(shí)鏈接。靜態(tài)鏈接的模塊被鏈接編輯并載入以便運(yùn)行。動(dòng)態(tài)鏈接的模塊被鏈接編輯后載入,并在運(yùn)行時(shí)進(jìn)行鏈接以便運(yùn)行。程序執(zhí)行時(shí),在main()函數(shù)被調(diào)用前,運(yùn)行時(shí)載入器把共享的數(shù)據(jù)對象載入到進(jìn)程的地址空間。外部函數(shù)被真正調(diào)用之前,運(yùn)行時(shí)載入器并不解析它們。所以即使鏈接了函數(shù)庫,如果并沒有實(shí)際調(diào)用,也不會帶來額外開銷。5.2動(dòng)態(tài)鏈接的優(yōu)點(diǎn)1.可執(zhí)行文件的體積非常小。雖然運(yùn)行速度稍微慢一些,但動(dòng)態(tài)鏈接能夠更加有效的利用磁盤空間,而且鏈接-編輯階段的時(shí)間也會縮短(因?yàn)殒溄悠鞯挠行┕步M被推遲到載入時(shí))。2.盡管單個(gè)可執(zhí)行文件的啟動(dòng)速度稍受影響,但動(dòng)態(tài)鏈接可以從兩個(gè)方面提高性能:(1)動(dòng)態(tài)鏈接可執(zhí)行文件比功能相同的靜態(tài)鏈接可執(zhí)行文件的體積小。它能夠節(jié)省磁盤空間和虛擬內(nèi)存,因?yàn)楹瘮?shù)庫只有在需要時(shí)才被映射到進(jìn)程中。以前,避免把函數(shù)庫的拷貝綁定到每個(gè)可執(zhí)行文件的唯一方法就是把服務(wù)至于內(nèi)核中而不是函數(shù)庫中,這就帶來了可怕的“內(nèi)核膨脹”問題。(2)所有動(dòng)態(tài)鏈接到某個(gè)特定函數(shù)庫的可執(zhí)行文件在運(yùn)行時(shí)共享改函數(shù)庫的一個(gè)單獨(dú)拷貝。操作系統(tǒng)內(nèi)核保證映射到內(nèi)存中的函數(shù)庫可以被所有使用他們的進(jìn)程共享。這就提供了更好的I/O和交換空間利用率,節(jié)省了物理內(nèi)存,從而提高了系統(tǒng)的整體性能。如果可執(zhí)行文件時(shí)靜態(tài)鏈接的,每個(gè)文件都擁有一份函數(shù)庫的拷貝,顯然極其浪費(fèi)。3.編譯時(shí)地址問題:在編譯階段,所有非局部變量地址已經(jīng)確定。這些這里有與位置無關(guān)的代碼和與位置有關(guān)的代碼。與位置無關(guān)的代碼表示這種代碼保證對于任何全局?jǐn)?shù)據(jù)的訪問都是通過間接地方法完成的。而與位置無關(guān)的代碼,其代碼會被對應(yīng)到固定的地址。對于這個(gè)問題,有待接下來的研究,還有進(jìn)程間的共享,以及與位置無關(guān)以及與位置有關(guān)的代碼,這個(gè)位置指的是直接對應(yīng)內(nèi)存還是相對于程序本身,都有待研究。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第7頁。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第7頁。第六章運(yùn)動(dòng)的詩章:運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)編程語言理論的經(jīng)典對立之一就是代碼和數(shù)據(jù)的區(qū)別。有些把二者視為一體。C語言通常維持二者的區(qū)別。代碼和數(shù)據(jù)的區(qū)別也可以認(rèn)為是編譯時(shí)和運(yùn)行時(shí)的分界線。編譯器的絕大部分工作都跟翻譯代碼有關(guān),必要的數(shù)據(jù)存儲管理的絕大部分都在運(yùn)行時(shí)進(jìn)行。6.2段1.在UNIX中,段表示一個(gè)二進(jìn)制文件相關(guān)的內(nèi)容塊。在Interx86的內(nèi)存模型中,段表示一種設(shè)計(jì)的結(jié)果。在這種設(shè)計(jì)中(基于兼容性的原因),地址空間并非一個(gè)整體,而是分成一些64K大小的區(qū)域,稱之為段。2.對于unix中的可執(zhí)行文件,是以段形式組織的。一般有文本段,數(shù)據(jù)段和bss段。3.段可以方便地映射到鏈接器在運(yùn)行時(shí)可以直接載入的對象中。載入器只是去文件中每個(gè)段的映像,并直接將他們放入內(nèi)存中。從本質(zhì)上說,段在正在執(zhí)行的程序中是一塊內(nèi)存區(qū)域,每個(gè)區(qū)域都有特定的目的。4.每個(gè)段的作用:(1)文本段包含程序的指令。鏈接器把指令直接從文件拷貝到內(nèi)存中,以后便再也不用管它。(2)數(shù)據(jù)段包含經(jīng)過初始化的全局和靜態(tài)變量以及它們的值。(3)bss段的大小從可執(zhí)行文件中得到,然后鏈接器得到這個(gè)大小的內(nèi)存塊,緊跟在數(shù)據(jù)段之后。當(dāng)這個(gè)內(nèi)存區(qū)進(jìn)入程序的地址空間后全部清零。包括數(shù)據(jù)段和bss段的整個(gè)區(qū)段此時(shí)通常稱為數(shù)據(jù)區(qū)。這是因?yàn)樵诓僮飨到y(tǒng)的內(nèi)存管理術(shù)語中,段就是一片連續(xù)的虛擬地址,所以相鄰的段被結(jié)合。6.4C語言運(yùn)行時(shí)系統(tǒng)在a.out里干了什么運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)有好幾種:堆棧、活動(dòng)記錄、數(shù)據(jù)、堆等。堆棧段主要有三個(gè)用途:堆棧為函數(shù)內(nèi)部聲明的局部變量提供存儲空間。進(jìn)行函數(shù)調(diào)用時(shí),堆棧存儲與此有關(guān)的一些維護(hù)性信息。堆棧也可以被用作暫時(shí)存儲區(qū)。有時(shí)候程序需要一些臨時(shí)存儲,比如計(jì)算一個(gè)很長的算術(shù)表達(dá)式時(shí),它可以把部分計(jì)算結(jié)果壓到堆棧中,當(dāng)需要時(shí)再把它從堆棧中取出。除了遞歸調(diào)用之外,堆棧并非必須。因?yàn)樵诰幾g時(shí)可以知道局部變量、參數(shù)和返回地址所需空間的固定大小,并可以將它們分配于BSS段。6.5當(dāng)函數(shù)被調(diào)用時(shí)發(fā)生了什么;過程活動(dòng)記錄1.C語言自動(dòng)提供的服務(wù)之一就是跟蹤調(diào)用鏈——哪些函數(shù)調(diào)用了哪些函數(shù),當(dāng)下一個(gè)“return”語句執(zhí)行后,控制將返回何處等。解決這個(gè)問題的經(jīng)典機(jī)制是堆棧中的過程活動(dòng)記錄。當(dāng)每個(gè)函數(shù)被調(diào)用時(shí),都會產(chǎn)生一個(gè)過程活動(dòng)記錄(或類似的結(jié)構(gòu))。過程活動(dòng)記錄是一種數(shù)據(jù)結(jié)構(gòu),用于支持過程調(diào)用,并記錄調(diào)用結(jié)束以后返回調(diào)用點(diǎn)所需要的全部信息。(如下圖):局部變量(localvaribales)參數(shù)(arguments)靜態(tài)鏈接(stacklink)指向先前結(jié)構(gòu)的指針返回地址(returnaddress)2.絕大多數(shù)的現(xiàn)代算法語言允許函數(shù)和數(shù)據(jù)一樣在函數(shù)內(nèi)部定義。C語言不允許以這種語法進(jìn)行函數(shù)的嵌套。C語言中的所有函數(shù)在詞法層次中都是位于最頂層。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第8頁。在允許嵌套過程的語言中,活動(dòng)記錄一般要包含一個(gè)指向它的外層函數(shù)的活動(dòng)記錄的指針。這個(gè)指針被稱為靜態(tài)鏈接,它允許內(nèi)層過程訪問外層過程的活動(dòng)記錄,因此也可以訪問外層過程的局部數(shù)據(jù)。記住在同一時(shí)刻一個(gè)外層過程可能有好幾個(gè)處于活動(dòng)狀態(tài)的調(diào)用。內(nèi)層過程活動(dòng)記錄的靜態(tài)鏈接將指向合適的活動(dòng)記錄,允許訪問局部數(shù)據(jù)的正確實(shí)例。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第8頁。這種類型的訪問(一個(gè)指向詞法上外層范圍的數(shù)據(jù)項(xiàng)的引用)被稱作上層引用。靜態(tài)鏈接(指向從詞法上講屬于外層過程的活動(dòng)記錄,由編譯時(shí)決定)之所以如此命名是因?yàn)樗c動(dòng)態(tài)鏈接相對照,后者是一個(gè)活動(dòng)記錄指針鏈(在運(yùn)行時(shí)指向最靠近自己的前一個(gè)過程調(diào)用的活動(dòng)記錄)。6.6auto和static關(guān)鍵字1.對于在函數(shù)內(nèi)部定義的自動(dòng)變量,函數(shù)調(diào)用結(jié)束就被回收。如果要返回指針,此時(shí)指針不是有malloc或靜態(tài)變量,那么當(dāng)函數(shù)調(diào)用結(jié)束時(shí),返回了改變量地址的副本,但是該地址(此時(shí)為副本代表的地址)所指向的內(nèi)存塊實(shí)際上已經(jīng)被回收。所以當(dāng)你再去調(diào)用這個(gè)返回的指針時(shí),其指向的內(nèi)容其實(shí)是未知的。這個(gè)指針也叫懸垂指針。2.存儲類型說明符auto關(guān)鍵字在實(shí)際中從來用不著。它通常由編譯器設(shè)計(jì)者使用,用于標(biāo)記符號表的條目——它表示“在進(jìn)入該塊后,自動(dòng)分配存儲”。3.過程活動(dòng)記錄并不一定要存在于堆棧中。事實(shí)上,盡可能地把過程活動(dòng)記錄的內(nèi)容放到寄存器中會使函數(shù)調(diào)用速度更快。SPARC架構(gòu)引入了一個(gè)概念,稱為“寄存器窗口”,CPU擁有一組寄存器,它們只用于保存過程活動(dòng)記錄中的參數(shù)。每當(dāng)函數(shù)調(diào)用時(shí),空的活動(dòng)記錄依然到堆棧中。當(dāng)函數(shù)調(diào)用鏈非常深而寄存器窗口不夠用時(shí),寄存器的內(nèi)容就會被保存到堆棧中保留的活動(dòng)記錄空間中,以便重新利用這些寄存器。第七章對內(nèi)存的思考7.2Inter80x86內(nèi)存模型以及它的工作原理1.在Inter80x86內(nèi)存模型中,段是內(nèi)存模型設(shè)計(jì)的結(jié)果,在80x86的內(nèi)存模型中,個(gè)各處理器的地址空間并不一致(因?yàn)橐3旨嫒菪裕?,但他們都被分割成?4KB為單位的區(qū)域,每個(gè)這樣的區(qū)域便稱為段。2.作為80x86內(nèi)存模型最基本的形式,8086中的段是一塊64KB的內(nèi)存區(qū)域,由一個(gè)段寄存器所指向。內(nèi)存地址的形成過程是:取得段寄存器的值,左移4位,然后加上16位的偏移地址(表示段內(nèi)的地址),就是最終的地址。3.8086有20位地址總線,總共是1MB的內(nèi)存。但是只有640KB可供應(yīng)用程序使用。因?yàn)槠渌囊A(yù)留給系統(tǒng)使用。7.3虛擬內(nèi)存1.虛擬內(nèi)存:為了去除安裝在機(jī)器上的物理內(nèi)存數(shù)量的限制,提出虛擬內(nèi)存這個(gè)概念?;舅悸肥怯昧畠r(jià)但緩慢的磁盤來擴(kuò)充快速卻昂貴的內(nèi)存。在任一給定時(shí)刻,程序?qū)嶋H需要使用的虛擬內(nèi)存區(qū)段的內(nèi)容就被載入物理內(nèi)存中。當(dāng)物理內(nèi)存中的數(shù)據(jù)有一段時(shí)間未被使用,他們就可能被轉(zhuǎn)移到硬盤中,節(jié)省下來的物理內(nèi)存空間用于載入需要使用的其它數(shù)據(jù)。2.以SunOS為例說明:SunOS中的進(jìn)程執(zhí)行于32位地址空間。操作系統(tǒng)負(fù)責(zé)具體細(xì)節(jié),使每個(gè)進(jìn)程都以為自己擁有整個(gè)地址空間的獨(dú)家訪問權(quán)。這個(gè)幻覺是通過“虛擬內(nèi)存”實(shí)現(xiàn)的。所有進(jìn)程共享機(jī)器的物理內(nèi)存,當(dāng)內(nèi)存用完時(shí)就用磁盤保存數(shù)據(jù)。在進(jìn)程運(yùn)行時(shí),數(shù)據(jù)在磁盤和內(nèi)存之間來回移動(dòng)。內(nèi)存管理硬件負(fù)責(zé)把虛擬地址翻譯為物理地址,并讓一個(gè)進(jìn)程始終運(yùn)行于系統(tǒng)的真正內(nèi)存中。應(yīng)用程序員只看到虛擬地址,并不知道自己的進(jìn)程在磁盤和內(nèi)存之間來回切換。虛擬內(nèi)存通過“頁”的形式組織。頁就是操作系統(tǒng)在磁盤和內(nèi)存之間移來移去或進(jìn)行保護(hù)的單位,一般為幾K字節(jié)。C程序設(shè)計(jì)語言(第二版)全文共11頁,當(dāng)前為第9頁。從潛在的可能性上說,與進(jìn)程有關(guān)的所有內(nèi)存都將被系統(tǒng)所使用。如果該進(jìn)程可能不會馬上運(yùn)行,操作系統(tǒng)可以暫時(shí)取回所有分配的給他的物理內(nèi)存資源,將該進(jìn)程的所有相關(guān)信息都備份到磁盤上。這樣這個(gè)進(jìn)程就被“換出”。在磁盤中有一個(gè)特殊的“交換區(qū)”

溫馨提示

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

評論

0/150

提交評論