C程序設(shè)計(jì)從零開(kāi)始_第1頁(yè)
C程序設(shè)計(jì)從零開(kāi)始_第2頁(yè)
C程序設(shè)計(jì)從零開(kāi)始_第3頁(yè)
C程序設(shè)計(jì)從零開(kāi)始_第4頁(yè)
C程序設(shè)計(jì)從零開(kāi)始_第5頁(yè)
已閱讀5頁(yè),還剩41頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1、1 C+程序設(shè)計(jì)從零開(kāi)始之何謂變量本篇說(shuō)明內(nèi)容是C+中的關(guān)鍵,基本大部分人對(duì)于這些內(nèi)容都是昏的,但這些內(nèi)容又是編程的基礎(chǔ)中的基礎(chǔ),必須詳細(xì)說(shuō)明。數(shù)字表示數(shù)學(xué)中,數(shù)只有數(shù)值大小的不同,絕不會(huì)有數(shù)值占用空間的區(qū)別,即數(shù)學(xué)中的數(shù)是邏輯上的一個(gè)概念,但電腦不是。考慮算盤,每個(gè)算盤上有很多列算子,每列都分成上下兩排算子。上排算子有2個(gè),每個(gè)代表5,下排算子有4個(gè),每個(gè)代表1(這并不重要)。因此算盤上的每列共有6個(gè)算子,每列共可以表示0到14這15個(gè)數(shù)字(因?yàn)樯吓潘阕拥目赡軤顟B(tài)有0到2個(gè)算子有效,而下排算子則可能有0到4個(gè)算子有效,故為3×5=15種組合方式)。上面的重點(diǎn)就是算盤的每列并沒(méi)有表示

2、0到14這15個(gè)數(shù)字,而是每列有15種狀態(tài),因此被人利用來(lái)表示數(shù)字而已(這很重要)。由于算盤的每列有15個(gè)狀態(tài),因此用兩列算子就可以有15×15=225個(gè)狀態(tài),因此可以表示0到224。阿拉伯?dāng)?shù)字的每一位有0到9這10個(gè)圖形符號(hào),用兩個(gè)阿拉伯?dāng)?shù)字圖形符號(hào)時(shí)就能有10×10=100個(gè)狀態(tài),因此可以表示0到99這100個(gè)數(shù)。這里的算盤其實(shí)就是一個(gè)基于15進(jìn)制的記數(shù)器(可以通過(guò)維持一列算子的狀態(tài)來(lái)記錄一位數(shù)字),它的一列算子就相當(dāng)于一位阿拉伯?dāng)?shù)字,每列有15種狀態(tài),故能表示從0到14這15個(gè)數(shù)字,超出14后就必須通過(guò)進(jìn)位來(lái)要求另一列算子的加入以表示數(shù)字。電腦與此一樣,其并不是數(shù)字計(jì)

3、算機(jī),而是電子計(jì)算機(jī),電腦中通過(guò)一根線的電位高低來(lái)表示數(shù)字。一根線中的電位規(guī)定只有兩種狀態(tài)高電位和低電位,因此電腦的數(shù)字表示形式是二進(jìn)制的。和上面的算盤一樣,一根電線只有兩個(gè)狀態(tài),當(dāng)要表示超出1的數(shù)字時(shí),就必須進(jìn)位來(lái)要求另一根線的加入以表示數(shù)字。所謂的32位電腦就是提供了32根線(被稱作數(shù)據(jù)總線)來(lái)表示數(shù)據(jù),因此就有2的32次方那么多種狀態(tài)。而16根線就能表示2的16次方那么多種狀態(tài)。所以,電腦并不是基于二進(jìn)制數(shù),而是基于狀態(tài)的變化,只不過(guò)這個(gè)狀態(tài)可以使用二進(jìn)制數(shù)表示出來(lái)而已。即電腦并不認(rèn)識(shí)二進(jìn)制數(shù),這是下面“類型”一節(jié)的基礎(chǔ)。內(nèi)存內(nèi)存就是電腦中能記錄數(shù)字的硬件,但其存儲(chǔ)速度很快(與硬盤等低速

4、存儲(chǔ)設(shè)備比較),又不能較長(zhǎng)時(shí)間保存數(shù)據(jù),所以經(jīng)常被用做草稿紙,記錄一些臨時(shí)信息。前面已經(jīng)說(shuō)過(guò),32位計(jì)算機(jī)的數(shù)字是通過(guò)32根線上的電位狀態(tài)的組合來(lái)表示的,因此內(nèi)存能記錄數(shù)字,也就是能維持32根線上各自的電位狀態(tài)(就好象算盤的算子撥動(dòng)后就不會(huì)改變位置,除非再次撥動(dòng)它)。不過(guò)依舊考慮上面的算盤,假如一個(gè)算盤上有15列算子,則一個(gè)算盤能表示15的15次方個(gè)狀態(tài),是很大的數(shù)字,但經(jīng)常實(shí)際是不會(huì)用到變化那么大的數(shù)字的,因此讓一個(gè)算盤只有兩列算子,則只能表示225個(gè)狀態(tài),當(dāng)數(shù)字超出時(shí)就使用另一個(gè)或多個(gè)算盤來(lái)一起表示。上面不管是2列算子還是15列算子,都是算盤的粒度,粒度分得過(guò)大造成不必要的浪費(fèi)(很多列算子

5、都不使用),太小又很麻煩(需要多個(gè)算盤)。電腦與此一樣。2的32次方可表示的數(shù)字很大,一般都不會(huì)用到,如果直接以32位存儲(chǔ)在內(nèi)存中勢(shì)必造成相當(dāng)大的資源浪費(fèi)。于是如上,規(guī)定內(nèi)存的粒度為8位二進(jìn)制數(shù),稱為一個(gè)內(nèi)存單元,而其大小稱為一個(gè)字節(jié)(Byte)。就是說(shuō),內(nèi)存存儲(chǔ)數(shù)字,至少都會(huì)記錄8根線上的電位狀態(tài),也就是2的8次方共256種狀態(tài)。所以如果一個(gè)32位的二進(jìn)制數(shù)要存儲(chǔ)在內(nèi)存中,就需要占據(jù)4個(gè)內(nèi)存單元,也就是4個(gè)字節(jié)的內(nèi)存空間。我們?cè)诩埳蠈懽郑峭ㄟ^(guò)肉眼判斷出字在紙上的相對(duì)橫坐標(biāo)和縱坐標(biāo)以查找到要看的字或要寫字的位置。同樣,由于內(nèi)存就相當(dāng)于草稿紙,因此也需要某種定位方式來(lái)定位,在電腦中,就是通過(guò)一

6、個(gè)數(shù)字來(lái)定位的。這就和旅館的房間號(hào)一樣,內(nèi)存單元就相當(dāng)于房間(假定每個(gè)房間只能住一個(gè)人),而前面說(shuō)的那個(gè)數(shù)字就相當(dāng)于房間號(hào)。為了向某塊內(nèi)存中寫入數(shù)據(jù)(就是使用某塊內(nèi)存來(lái)記錄數(shù)據(jù)總線上的電位狀態(tài)),就必須知道這塊內(nèi)存對(duì)應(yīng)的數(shù)字,而這個(gè)數(shù)字就被稱為地址。而通過(guò)給定的地址找到對(duì)應(yīng)的內(nèi)存單元就稱為尋址。因此地址就是一個(gè)數(shù)字,用以唯一標(biāo)識(shí)某一特定內(nèi)存單元。此數(shù)字一般是32位長(zhǎng)的二進(jìn)制數(shù),也就可以表示4G個(gè)狀態(tài),也就是說(shuō)一般的32位電腦都具有4G的內(nèi)存空間尋址能力,即電腦最多裝4G的內(nèi)存,如果電腦有超過(guò)4G的內(nèi)存,此時(shí)就需要增加地址的長(zhǎng)度,如用40位長(zhǎng)的二進(jìn)制數(shù)來(lái)表示。類型在本系列最開(kāi)頭時(shí)已經(jīng)說(shuō)明了何謂編

7、程,而剛才更進(jìn)一步說(shuō)明了電腦其實(shí)連數(shù)字都不認(rèn)識(shí),只是狀態(tài)的記錄,而所謂的加法也只是人為設(shè)計(jì)那個(gè)加法器以使得兩個(gè)狀態(tài)經(jīng)過(guò)加法器的處理而生成的狀態(tài)正好和數(shù)學(xué)上的加法的結(jié)果一樣而已。這一切的一切都只說(shuō)明一點(diǎn):電腦所做的工作是什么,全視使用的人以為是什么。因此為了利用電腦那很快的“計(jì)算”能力(實(shí)際是狀態(tài)的變換能力),人為規(guī)定了如何解釋那些狀態(tài)。為了方便其間,對(duì)于前面提出的電位的狀態(tài),我們使用1位二進(jìn)制數(shù)來(lái)表示,則上面提出的狀態(tài)就可以使用一個(gè)二進(jìn)制數(shù)來(lái)表示,而所謂的“如何解釋那些狀態(tài)”就變成了如何解釋一個(gè)二進(jìn)制數(shù)。C+是高級(jí)語(yǔ)言,為了幫助解釋那些二進(jìn)制數(shù),提供了類型這個(gè)概念。類型就是人為制訂的如何解釋內(nèi)

8、存中的二進(jìn)制數(shù)的協(xié)議。C+提供了下面的一些標(biāo)準(zhǔn)類型定義。·signed char 表示所指向的內(nèi)存中的數(shù)字使用補(bǔ)碼形式,表示的數(shù)字為-128到+127,長(zhǎng)度為1個(gè)字節(jié)·unsigned char 表示所指向的內(nèi)存中的數(shù)字使用原碼形式,表示的數(shù)字為0到255,長(zhǎng)度為1個(gè)字節(jié)·signed short 表示所指向的內(nèi)存中的數(shù)字使用補(bǔ)碼形式,表示的數(shù)字為32768到+32767,長(zhǎng)度為2個(gè)字節(jié)·unsigned short 表示所指向的內(nèi)存中的數(shù)字使用原碼形式,表示的數(shù)字為0到65535,長(zhǎng)度為2個(gè)字節(jié)·signed long 表示所指向的內(nèi)存中的數(shù)

9、字使用補(bǔ)碼形式,表示的數(shù)字為-2147483648到+2147483647,長(zhǎng)度為4個(gè)字節(jié)·unsigned long 表示所指向的內(nèi)存中的數(shù)字使用原碼形式,表示的數(shù)字為0到4294967295,長(zhǎng)度為4個(gè)字節(jié)·signed int 表示所指向的內(nèi)存中的數(shù)字使用補(bǔ)碼形式,表示的數(shù)字則視編譯器。如果編譯器編譯時(shí)被指明編譯為在16位操作系統(tǒng)上運(yùn)行,則等同于signed short;如果是編譯為32位的,則等同于signed long;如果是編譯為在64位操作系統(tǒng)上運(yùn)行,則為8個(gè)字節(jié)長(zhǎng),而范圍則如上一樣可以自行推算出來(lái)。·unsigned int 表示所指向的內(nèi)存中的數(shù)

10、字使用原碼形式,其余和signed int一樣,表示的是無(wú)符號(hào)數(shù)。·bool 表示所指向的內(nèi)存中的數(shù)字為邏輯值,取值為false或true。長(zhǎng)度為1個(gè)字節(jié)。·float 表示所指向的內(nèi)存按IEEE標(biāo)準(zhǔn)進(jìn)行解釋,為real*4,占用4字節(jié)內(nèi)存空間,等同于上篇中提到的單精度浮點(diǎn)數(shù)。·double 表示所指向的內(nèi)存按IEEE標(biāo)準(zhǔn)進(jìn)行解釋,為real*8,可表示數(shù)的精度較float高,占用8字節(jié)內(nèi)存空間,等同于上篇提到的雙精度浮點(diǎn)數(shù)。·long double 表示所指向的內(nèi)存按IEEE標(biāo)準(zhǔn)進(jìn)行解釋,為real*10,可表示數(shù)的精度較double高,但在為32位W

11、indows操作系統(tǒng)編寫程序時(shí),仍占用8字節(jié)內(nèi)存空間,等效于double,只是如果CPU支持此類浮點(diǎn)類型則還是可以進(jìn)行這個(gè)精度的計(jì)算。標(biāo)準(zhǔn)類型不止上面的幾個(gè),后面還會(huì)陸續(xù)提到。上面的長(zhǎng)度為2個(gè)字節(jié)也就是將兩個(gè)連續(xù)的內(nèi)存單元中的數(shù)字取出并合并在一起以表示一個(gè)數(shù)字,這和前面說(shuō)的一個(gè)算盤表示不了的數(shù)字,就進(jìn)位以加入另一個(gè)算盤幫助表示是同樣的道理。上面的signed關(guān)鍵字是可以去掉的,即char等同于signed char,用以簡(jiǎn)化代碼的編寫。但也僅限于signed,如果是unsigned char,則在使用時(shí)依舊必須是unsigned char?,F(xiàn)在應(yīng)該已經(jīng)了解上篇中為什么數(shù)字還要分什么有符號(hào)無(wú)符號(hào)

12、、長(zhǎng)整型短整型之類的了,而上面的short、char等也都只是長(zhǎng)度不同,這就由程序員自己根據(jù)可能出現(xiàn)的數(shù)字變化幅度來(lái)進(jìn)行選用了。類型只是對(duì)內(nèi)存中的數(shù)字的解釋,但上面的類型看起來(lái)相對(duì)簡(jiǎn)單了點(diǎn),且語(yǔ)義并不是很強(qiáng),即沒(méi)有什么特殊意思。為此,C+提供了自定義類型,也就是后繼文章中將要說(shuō)明的結(jié)構(gòu)、類等。在本系列的第一篇中已經(jīng)說(shuō)過(guò),電腦編程的絕大部分工作就是操作內(nèi)存,而上面說(shuō)了,為了操作內(nèi)存,需要使用地址來(lái)標(biāo)識(shí)要操作的內(nèi)存塊的首地址(上面的long表示連續(xù)的4個(gè)字節(jié)內(nèi)存,其第一個(gè)內(nèi)存單元的地址稱作這連續(xù)4個(gè)字節(jié)內(nèi)存塊的首地址)。為此我們?cè)诰帉懗绦驎r(shí)必須記下地址。做5+2/3-5*2的計(jì)算,先計(jì)算出2/3的

13、值,寫在草稿紙上,接著算出5*2的值,又寫在草稿紙上。為了接下來(lái)的加法和減法運(yùn)算,必須能夠知道草稿紙上的兩個(gè)數(shù)字哪個(gè)是2/3的值哪個(gè)是5*2的值。人就是通過(guò)記憶那兩個(gè)數(shù)在紙上的位置來(lái)記憶的,而電腦就是通過(guò)地址來(lái)標(biāo)識(shí)的。但電腦只會(huì)做加減乘除,不會(huì)去主動(dòng)記那些2/3、5*2的中間值的位置,也就是地址。因此程序員必須完成這個(gè)工作,將那兩個(gè)地址記下來(lái)。問(wèn)題就是這里只有兩個(gè)值,也許好記一些,但如果多了,人是很難記住哪個(gè)地址對(duì)應(yīng)哪個(gè)值的,但人對(duì)符號(hào)比對(duì)數(shù)字要敏感得多,即人很容易記下一個(gè)名字而不是一個(gè)數(shù)字。為此,程序員就自己寫了一個(gè)表,表有兩列,一列是“2/3的值”,一列是對(duì)應(yīng)的地址。如果式子稍微復(fù)雜點(diǎn),那

14、么那個(gè)表可能就有個(gè)二三十行,而每寫一行代碼就要去翻查相應(yīng)的地址,如果來(lái)個(gè)幾萬(wàn)行代碼那是人都不能忍受。C+作為高級(jí)語(yǔ)言,很正常地提供了上面問(wèn)題的解決之道,就是由編譯器來(lái)幫程序員維護(hù)那個(gè)表,要查的時(shí)候是編譯器去查,這也就是變量的功能。變量是一個(gè)映射元素。上面提到的表由編譯器維護(hù),而表中的每一行都是這個(gè)表的一個(gè)元素(也稱記錄)。表有三列:變量名、對(duì)應(yīng)地址和相應(yīng)類型。變量名是一個(gè)標(biāo)識(shí)符,因此其命名規(guī)則完全按照上一篇所說(shuō)的來(lái)。當(dāng)要對(duì)某塊內(nèi)存寫入數(shù)據(jù)時(shí),程序員使用相應(yīng)的變量名進(jìn)行內(nèi)存的標(biāo)識(shí),而表中的對(duì)應(yīng)地址就記錄了這個(gè)地址,進(jìn)而將程序員給出的變量名,一個(gè)標(biāo)識(shí)符,映射成一個(gè)地址,因此變量是一個(gè)映射元素。而相

15、應(yīng)類型則告訴編譯器應(yīng)該如何解釋此地址所指向的內(nèi)存,是2個(gè)連續(xù)字節(jié)還是4個(gè)?是原碼記錄還是補(bǔ)碼?而變量所對(duì)應(yīng)的地址所標(biāo)識(shí)的內(nèi)存的內(nèi)容叫做此變量的值。有如下的變量解釋:“可變的量,其相當(dāng)于一個(gè)盒子,數(shù)字就裝在盒子里,而變量名就寫在盒子外面,這樣電腦就知道我們要處理哪一個(gè)盒子,且不同的盒子裝不同的東西,裝字符串的盒子就不能裝數(shù)字?!鄙厦婢褪俏业谝淮螌W(xué)習(xí)編程時(shí),書上寫的(是BASIC語(yǔ)言)。對(duì)于初學(xué)者也許很容易理解,也不能說(shuō)錯(cuò),但是造成的誤解將導(dǎo)致以后的程序編寫地千瘡百孔。上面的解釋隱含了一個(gè)意思變量是一塊內(nèi)存。這是嚴(yán)重錯(cuò)誤的!如果變量是一塊內(nèi)存,那么C+中著名的引用類型將被棄置荒野。變量實(shí)際并不是一

16、塊內(nèi)存,只是一個(gè)映射元素,這是致關(guān)重要的。內(nèi)存的種類前面已經(jīng)說(shuō)了內(nèi)存是什么及其用處,但內(nèi)存是不能隨便使用的,因?yàn)椴僮飨到y(tǒng)自己也要使用內(nèi)存,而且現(xiàn)在的操作系統(tǒng)正常情況下都是多任務(wù)操作系統(tǒng),即可同時(shí)執(zhí)行多個(gè)程序,即使只有一個(gè)CPU。因此如果不對(duì)內(nèi)存訪問(wèn)加以節(jié)制,可能會(huì)破壞另一個(gè)程序的運(yùn)作。比如我在紙上寫了2/3的值,而你未經(jīng)我同意且未通知我就將那個(gè)值擦掉,并寫上5*2的值,結(jié)果我后面的所有計(jì)算也就出錯(cuò)了。因此為了使用一塊內(nèi)存,需要向操作系統(tǒng)申請(qǐng),由操作系統(tǒng)統(tǒng)一管理所有程序使用的內(nèi)存。所以為了記錄一個(gè)long類型的數(shù)字,先向操作系統(tǒng)申請(qǐng)一塊連續(xù)的4字節(jié)長(zhǎng)的內(nèi)存空間,然后操作系統(tǒng)就會(huì)在內(nèi)存中查看,看是

17、否還有連續(xù)的4個(gè)字節(jié)長(zhǎng)的內(nèi)存,如果找到,則返回此4字節(jié)內(nèi)存的首地址,然后編譯器編譯的指令將其記錄在前面提到的變量表中,最后就可以用它記錄一些臨時(shí)計(jì)算結(jié)果了。上面的過(guò)程稱為要求操作系統(tǒng)分配一塊內(nèi)存。這看起來(lái)很不錯(cuò),但是如果只為了4個(gè)字節(jié)就要求操作系統(tǒng)搜索一下內(nèi)存狀況,那么如果需要100個(gè)臨時(shí)數(shù)據(jù),就要求操作系統(tǒng)分配內(nèi)存100次,很明顯地效率低下(無(wú)謂的99次查看內(nèi)存狀況)。因此C+發(fā)現(xiàn)了這個(gè)問(wèn)題,并且操作系統(tǒng)也提出了相應(yīng)的解決方法,最后提出了如下的解決之道。棧(Stack) 任何程序執(zhí)行前,預(yù)先分配一固定長(zhǎng)度的內(nèi)存空間,這塊內(nèi)存空間被稱作棧(這種說(shuō)法并不準(zhǔn)確,但由于實(shí)際涉及到線程,在此為了不將問(wèn)

18、題復(fù)雜化才這樣說(shuō)明),也被叫做堆棧。那么在要求一個(gè)4字節(jié)內(nèi)存時(shí),實(shí)際是在這個(gè)已分配好的內(nèi)存空間中獲取內(nèi)存,即內(nèi)存的維護(hù)工作由程序員自己來(lái)做,即程序員自己判斷可以使用哪些內(nèi)存,而不是操作系統(tǒng),直到已分配的內(nèi)存用完。很明顯,上面的工作是由編譯器來(lái)做的,不用程序員操心,因此就程序員的角度來(lái)看什么事情都沒(méi)發(fā)生,還是需要像原來(lái)那樣向操作系統(tǒng)申請(qǐng)內(nèi)存,然后再使用。但工作只是從操作系統(tǒng)變到程序自己而已,要維護(hù)內(nèi)存,依然要耗費(fèi)CPU的時(shí)間,不過(guò)要簡(jiǎn)單多了,因?yàn)椴挥脴?biāo)記一塊內(nèi)存是否有人使用,而專門記錄一個(gè)地址。此地址以上的內(nèi)存空間就是有人正在使用的,而此地址以下的內(nèi)存空間就是無(wú)人使用的。之所以是以下的空間為無(wú)人

19、使用而不是以上,是當(dāng)此地址減小到0時(shí)就可以知道堆棧溢出了(如果你已經(jīng)有些基礎(chǔ),請(qǐng)不要把0認(rèn)為是虛擬內(nèi)存地址,關(guān)于虛擬內(nèi)存將會(huì)在C+從零開(kāi)始(十八)中進(jìn)行說(shuō)明,這里如此解釋只是為了方便理解)。而且CPU還專門對(duì)此法提供了支持,給出了兩條指令,轉(zhuǎn)成匯編語(yǔ)言就是push和pop,表示壓棧和出棧,分別減小和增大那個(gè)地址。而最重要的好處就是由于程序一開(kāi)始執(zhí)行時(shí)就已經(jīng)分配了一大塊連續(xù)內(nèi)存,用一個(gè)變量記錄這塊連續(xù)內(nèi)存的首地址,然后程序中所有用到的,程序員以為是向操作系統(tǒng)分配的內(nèi)存都可以通過(guò)那個(gè)首地址加上相應(yīng)偏移來(lái)得到正確位置,而這很明顯地由編譯器做了。因此實(shí)際上等同于在編譯時(shí)期(即編譯器編譯程序的時(shí)候)就已

20、經(jīng)分配了內(nèi)存(注意,實(shí)際編譯時(shí)期是不能分配內(nèi)存的,因?yàn)榉峙鋬?nèi)存是指程序運(yùn)行時(shí)向操作系統(tǒng)申請(qǐng)內(nèi)存,而這里由于使用堆棧,則編譯器將生成一些指令,以使得程序一開(kāi)始就向操作系統(tǒng)申請(qǐng)內(nèi)存,如果失敗則立刻退出,而如果不退出就表示那些內(nèi)存已經(jīng)分配到了,進(jìn)而代碼中使用首地址加偏移來(lái)使用內(nèi)存也就是有效的),但壞處也就是只能在編譯時(shí)期分配內(nèi)存。堆(Heap) 上面的工作是編譯器做的,即程序員并不參與堆棧的維護(hù)。但上面已經(jīng)說(shuō)了,堆棧相當(dāng)于在編譯時(shí)期分配內(nèi)存,因此一旦計(jì)算好某塊內(nèi)存的偏移,則這塊內(nèi)存就只能那么大,不能變化了(如果變化會(huì)導(dǎo)致其他內(nèi)存塊的偏移錯(cuò)誤)。比如要求客戶輸入定單數(shù)據(jù),可能有10份定單,也可能有10

21、0份定單,如果一開(kāi)始就定好了內(nèi)存大小,則可能造成不必要的浪費(fèi),又或者內(nèi)存不夠。為了解決上面的問(wèn)題,C+提供了另一個(gè)途徑,即允許程序員有兩種向操作系統(tǒng)申請(qǐng)內(nèi)存的方式。前一種就是在棧上分配,申請(qǐng)的內(nèi)存大小固定不變。后一種是在堆上分配,申請(qǐng)的內(nèi)存大小可以在運(yùn)行的時(shí)候變化,不是固定不變的。那么什么叫堆?在Windows操作系統(tǒng)下,由操作系統(tǒng)分配的內(nèi)存就叫做堆,而??梢哉J(rèn)為是在程序開(kāi)始時(shí)就分配的堆(這并不準(zhǔn)確,但為了不復(fù)雜化問(wèn)題,故如此說(shuō)明)。因此在堆上就可以分配大小變化的內(nèi)存塊,因?yàn)槭沁\(yùn)行時(shí)期即時(shí)分配的內(nèi)存,而不是編譯時(shí)期已計(jì)算好大小的內(nèi)存塊。變量的定義上面說(shuō)了那么多,你可能看得很暈,畢竟連一個(gè)實(shí)例都

22、沒(méi)有,全是文字,下面就來(lái)幫助加深對(duì)上面的理解。定義一個(gè)變量,就是向上面說(shuō)的由編譯器維護(hù)的變量表中添加元素,其語(yǔ)法如下:long a;先寫變量的類型,然后一個(gè)或多個(gè)空格或制表符(t)或其它間隔符,接著變量的名字,最后用分號(hào)結(jié)束。要同時(shí)定義多個(gè)變量,則各變量間使用逗號(hào)隔開(kāi),如下:long a, b, c; unsigned short e, a_34c;上面是兩條變量定義語(yǔ)句,各語(yǔ)句間用分號(hào)隔開(kāi),而各同類型變量間用逗號(hào)隔開(kāi)。而前面的式子5+2/3-5*2,則如下書寫。long a = 2/3, b = 5*2; long c = 5 + a b;可以不用再去記那煩人的地址了,只需記著a、b這種簡(jiǎn)單

23、的標(biāo)識(shí)符。當(dāng)然,上面的式子不一定非要那么寫,也可以寫成:long c = 5 + 2 / 3 5 * 2; 而那些a、b等中間變量編譯器會(huì)自動(dòng)生成并使用(實(shí)際中編譯器由于優(yōu)化的原因?qū)⒅苯佑?jì)算出結(jié)果,而不會(huì)生成實(shí)際的計(jì)算代碼)。下面就是問(wèn)題的關(guān)鍵,定義變量就是添加一個(gè)映射。前面已經(jīng)說(shuō)了,這個(gè)映射是將變量名和一個(gè)地址關(guān)聯(lián),因此在定義一個(gè)變量時(shí),編譯器為了能將變量名和某個(gè)地址對(duì)應(yīng)起來(lái),幫程序員在前面提到的棧上分配了一塊內(nèi)存,大小就視這個(gè)變量類型的大小。如上面的a、b、c的大小都是4個(gè)字節(jié),而e、a_34c的大小都是2個(gè)字節(jié)。假設(shè)編譯器分配的棧在一開(kāi)始時(shí)的地址是1000,并假設(shè)變量a所對(duì)應(yīng)的地址是10

24、00-56,則b所對(duì)應(yīng)的地址就是1000-60,而c所對(duì)應(yīng)的就是1000-64,e對(duì)應(yīng)的是1000-66,a_34c是1000-68。如果這時(shí)b突然不想是4字節(jié)了,而希望是8字節(jié),則后續(xù)的c、e、a_34c都將由于還是原來(lái)的偏移位置而使用了錯(cuò)誤的內(nèi)存,這也就是為什么棧上分配的內(nèi)存必須是固定大小。考慮前面說(shuō)的紅色文字:“變量實(shí)際并不是一塊內(nèi)存,只是一個(gè)映射元素”??墒侵灰x一個(gè)變量,就會(huì)相應(yīng)地得到一塊內(nèi)存,為什么不說(shuō)變量就是一塊內(nèi)存?上面定義變量時(shí)之所以會(huì)分配一塊內(nèi)存是因?yàn)樽兞渴且粋€(gè)映射元素,需要一個(gè)對(duì)應(yīng)地址,因此才在棧上分配了一塊內(nèi)存,并將其地址記錄到變量表中。但是變量是可以有別名的,即另一

25、個(gè)名字。這個(gè)說(shuō)法是不準(zhǔn)確的,應(yīng)該是變量所對(duì)應(yīng)的內(nèi)存塊有另一個(gè)名字,而不止是這個(gè)變量的名字。為什么要有別名?這是語(yǔ)義的需要,表示既是什么又是什么。比如一塊內(nèi)存,里面記錄了老板的信息,因此起名為Boss,但是老板又是另一家公司的行政經(jīng)理,故變量名應(yīng)該為Manager,而在程序中有段代碼是老板的公司相關(guān)的,而另一段是老板所在公司相關(guān)的,在這兩段程序中都要使用到老板的信息,那到底是使用Boss還是Manager?其實(shí)使用什么都不會(huì)對(duì)最終生成的機(jī)器代碼產(chǎn)生什么影響,但此處出于語(yǔ)義的需要就應(yīng)該使用別名,以期從代碼上表現(xiàn)出所編寫程序的意思。在C+中,為了支持變量別名,提供了引用變量這個(gè)概念。要定義一個(gè)引用變

26、量,在定義變量時(shí),在變量名的前面加一個(gè)“&”,如下書寫:long a; long &a1 = a, &a2 = a, &a3 = a2;上面的a1、a2、a3都是a所對(duì)應(yīng)的內(nèi)存塊的別名。這里在定義變量a時(shí)就在棧上分配了一塊4字節(jié)內(nèi)存,而在定義a1時(shí)卻沒(méi)有分配任何內(nèi)存,直接將變量a所映射的地址作為變量a1的映射地址,進(jìn)而形成對(duì)定義a時(shí)所分配的內(nèi)存的別名。因此上面的Boss和Manager,應(yīng)該如下(其中Person是一個(gè)結(jié)構(gòu)或類或其他什么自定義類型,這將在后繼的文章中陸續(xù)說(shuō)明):Person Boss; Person &Manager = Boss;由于變

27、量一旦定義就不能改變(指前面說(shuō)的變量表里的內(nèi)容,不是變量的值),直到其被刪除,所以上面在定義引用變量的時(shí)候必須給出欲別名的變量以初始化前面的變量表,否則編譯器編譯時(shí)將報(bào)錯(cuò)?,F(xiàn)在應(yīng)該就更能理解前面關(guān)于變量的紅字的意思了。并不是每個(gè)變量定義時(shí)都會(huì)分配內(nèi)存空間的。而關(guān)于如何在堆上分配內(nèi)存,將在介紹完指針后予以說(shuō)明,并進(jìn)而說(shuō)明上一篇遺留下來(lái)的關(guān)于字符串的問(wèn)題。本篇說(shuō)明內(nèi)容是C+中的關(guān)鍵,基本大部分人對(duì)于這些內(nèi)容都是昏的,但這些內(nèi)容又是編程的基礎(chǔ)中的基礎(chǔ),必須詳細(xì)說(shuō)明。數(shù)字表示數(shù)學(xué)中,數(shù)只有數(shù)值大小的不同,絕不會(huì)有數(shù)值占用空間的區(qū)別,即數(shù)學(xué)中的數(shù)是邏輯上的一個(gè)概念,但電腦不是??紤]算盤,每個(gè)算盤上有很多

28、列算子,每列都分成上下兩排算子。上排算子有2個(gè),每個(gè)代表5,下排算子有4個(gè),每個(gè)代表1(這并不重要)。因此算盤上的每列共有6個(gè)算子,每列共可以表示0到14這15個(gè)數(shù)字(因?yàn)樯吓潘阕拥目赡軤顟B(tài)有0到2個(gè)算子有效,而下排算子則可能有0到4個(gè)算子有效,故為3×5=15種組合方式)。上面的重點(diǎn)就是算盤的每列并沒(méi)有表示0到14這15個(gè)數(shù)字,而是每列有15種狀態(tài),因此被人利用來(lái)表示數(shù)字而已(這很重要)。由于算盤的每列有15個(gè)狀態(tài),因此用兩列算子就可以有15×15=225個(gè)狀態(tài),因此可以表示0到224。阿拉伯?dāng)?shù)字的每一位有0到9這10個(gè)圖形符號(hào),用兩個(gè)阿拉伯?dāng)?shù)字圖形符號(hào)時(shí)就能有10

29、15;10=100個(gè)狀態(tài),因此可以表示0到99這100個(gè)數(shù)。這里的算盤其實(shí)就是一個(gè)基于15進(jìn)制的記數(shù)器(可以通過(guò)維持一列算子的狀態(tài)來(lái)記錄一位數(shù)字),它的一列算子就相當(dāng)于一位阿拉伯?dāng)?shù)字,每列有15種狀態(tài),故能表示從0到14這15個(gè)數(shù)字,超出14后就必須通過(guò)進(jìn)位來(lái)要求另一列算子的加入以表示數(shù)字。電腦與此一樣,其并不是數(shù)字計(jì)算機(jī),而是電子計(jì)算機(jī),電腦中通過(guò)一根線的電位高低來(lái)表示數(shù)字。一根線中的電位規(guī)定只有兩種狀態(tài)高電位和低電位,因此電腦的數(shù)字表示形式是二進(jìn)制的。和上面的算盤一樣,一根電線只有兩個(gè)狀態(tài),當(dāng)要表示超出1的數(shù)字時(shí),就必須進(jìn)位來(lái)要求另一根線的加入以表示數(shù)字。所謂的32位電腦就是提供了32根線

30、(被稱作數(shù)據(jù)總線)來(lái)表示數(shù)據(jù),因此就有2的32次方那么多種狀態(tài)。而16根線就能表示2的16次方那么多種狀態(tài)。所以,電腦并不是基于二進(jìn)制數(shù),而是基于狀態(tài)的變化,只不過(guò)這個(gè)狀態(tài)可以使用二進(jìn)制數(shù)表示出來(lái)而已。即電腦并不認(rèn)識(shí)二進(jìn)制數(shù),這是下面“類型”一節(jié)的基礎(chǔ)。內(nèi)存內(nèi)存就是電腦中能記錄數(shù)字的硬件,但其存儲(chǔ)速度很快(與硬盤等低速存儲(chǔ)設(shè)備比較),又不能較長(zhǎng)時(shí)間保存數(shù)據(jù),所以經(jīng)常被用做草稿紙,記錄一些臨時(shí)信息。前面已經(jīng)說(shuō)過(guò),32位計(jì)算機(jī)的數(shù)字是通過(guò)32根線上的電位狀態(tài)的組合來(lái)表示的,因此內(nèi)存能記錄數(shù)字,也就是能維持32根線上各自的電位狀態(tài)(就好象算盤的算子撥動(dòng)后就不會(huì)改變位置,除非再次撥動(dòng)它)。不過(guò)依舊考慮

31、上面的算盤,假如一個(gè)算盤上有15列算子,則一個(gè)算盤能表示15的15次方個(gè)狀態(tài),是很大的數(shù)字,但經(jīng)常實(shí)際是不會(huì)用到變化那么大的數(shù)字的,因此讓一個(gè)算盤只有兩列算子,則只能表示225個(gè)狀態(tài),當(dāng)數(shù)字超出時(shí)就使用另一個(gè)或多個(gè)算盤來(lái)一起表示。上面不管是2列算子還是15列算子,都是算盤的粒度,粒度分得過(guò)大造成不必要的浪費(fèi)(很多列算子都不使用),太小又很麻煩(需要多個(gè)算盤)。電腦與此一樣。2的32次方可表示的數(shù)字很大,一般都不會(huì)用到,如果直接以32位存儲(chǔ)在內(nèi)存中勢(shì)必造成相當(dāng)大的資源浪費(fèi)。于是如上,規(guī)定內(nèi)存的粒度為8位二進(jìn)制數(shù),稱為一個(gè)內(nèi)存單元,而其大小稱為一個(gè)字節(jié)(Byte)。就是說(shuō),內(nèi)存存儲(chǔ)數(shù)字,至少都會(huì)記

32、錄8根線上的電位狀態(tài),也就是2的8次方共256種狀態(tài)。所以如果一個(gè)32位的二進(jìn)制數(shù)要存儲(chǔ)在內(nèi)存中,就需要占據(jù)4個(gè)內(nèi)存單元,也就是4個(gè)字節(jié)的內(nèi)存空間。我們?cè)诩埳蠈懽郑峭ㄟ^(guò)肉眼判斷出字在紙上的相對(duì)橫坐標(biāo)和縱坐標(biāo)以查找到要看的字或要寫字的位置。同樣,由于內(nèi)存就相當(dāng)于草稿紙,因此也需要某種定位方式來(lái)定位,在電腦中,就是通過(guò)一個(gè)數(shù)字來(lái)定位的。這就和旅館的房間號(hào)一樣,內(nèi)存單元就相當(dāng)于房間(假定每個(gè)房間只能住一個(gè)人),而前面說(shuō)的那個(gè)數(shù)字就相當(dāng)于房間號(hào)。為了向某塊內(nèi)存中寫入數(shù)據(jù)(就是使用某塊內(nèi)存來(lái)記錄數(shù)據(jù)總線上的電位狀態(tài)),就必須知道這塊內(nèi)存對(duì)應(yīng)的數(shù)字,而這個(gè)數(shù)字就被稱為地址。而通過(guò)給定的地址找到對(duì)應(yīng)的內(nèi)存

33、單元就稱為尋址。因此地址就是一個(gè)數(shù)字,用以唯一標(biāo)識(shí)某一特定內(nèi)存單元。此數(shù)字一般是32位長(zhǎng)的二進(jìn)制數(shù),也就可以表示4G個(gè)狀態(tài),也就是說(shuō)一般的32位電腦都具有4G的內(nèi)存空間尋址能力,即電腦最多裝4G的內(nèi)存,如果電腦有超過(guò)4G的內(nèi)存,此時(shí)就需要增加地址的長(zhǎng)度,如用40位長(zhǎng)的二進(jìn)制數(shù)來(lái)表示。類型在本系列最開(kāi)頭時(shí)已經(jīng)說(shuō)明了何謂編程,而剛才更進(jìn)一步說(shuō)明了電腦其實(shí)連數(shù)字都不認(rèn)識(shí),只是狀態(tài)的記錄,而所謂的加法也只是人為設(shè)計(jì)那個(gè)加法器以使得兩個(gè)狀態(tài)經(jīng)過(guò)加法器的處理而生成的狀態(tài)正好和數(shù)學(xué)上的加法的結(jié)果一樣而已。這一切的一切都只說(shuō)明一點(diǎn):電腦所做的工作是什么,全視使用的人以為是什么。因此為了利用電腦那很快的“計(jì)算”

34、能力(實(shí)際是狀態(tài)的變換能力),人為規(guī)定了如何解釋那些狀態(tài)。為了方便其間,對(duì)于前面提出的電位的狀態(tài),我們使用1位二進(jìn)制數(shù)來(lái)表示,則上面提出的狀態(tài)就可以使用一個(gè)二進(jìn)制數(shù)來(lái)表示,而所謂的“如何解釋那些狀態(tài)”就變成了如何解釋一個(gè)二進(jìn)制數(shù)。C+是高級(jí)語(yǔ)言,為了幫助解釋那些二進(jìn)制數(shù),提供了類型這個(gè)概念。類型就是人為制訂的如何解釋內(nèi)存中的二進(jìn)制數(shù)的協(xié)議。C+提供了下面的一些標(biāo)準(zhǔn)類型定義。·signed char 表示所指向的內(nèi)存中的數(shù)字使用補(bǔ)碼形式,表示的數(shù)字為-128到+127,長(zhǎng)度為1個(gè)字節(jié)·unsigned char 表示所指向的內(nèi)存中的數(shù)字使用原碼形式,表示的數(shù)字為0到255,長(zhǎng)

35、度為1個(gè)字節(jié)·signed short 表示所指向的內(nèi)存中的數(shù)字使用補(bǔ)碼形式,表示的數(shù)字為32768到+32767,長(zhǎng)度為2個(gè)字節(jié)·unsigned short 表示所指向的內(nèi)存中的數(shù)字使用原碼形式,表示的數(shù)字為0到65535,長(zhǎng)度為2個(gè)字節(jié)·signed long 表示所指向的內(nèi)存中的數(shù)字使用補(bǔ)碼形式,表示的數(shù)字為-2147483648到+2147483647,長(zhǎng)度為4個(gè)字節(jié)·unsigned long 表示所指向的內(nèi)存中的數(shù)字使用原碼形式,表示的數(shù)字為0到4294967295,長(zhǎng)度為4個(gè)字節(jié)·signed int 表示所指向的內(nèi)存中的數(shù)字使

36、用補(bǔ)碼形式,表示的數(shù)字則視編譯器。如果編譯器編譯時(shí)被指明編譯為在16位操作系統(tǒng)上運(yùn)行,則等同于signed short;如果是編譯為32位的,則等同于signed long;如果是編譯為在64位操作系統(tǒng)上運(yùn)行,則為8個(gè)字節(jié)長(zhǎng),而范圍則如上一樣可以自行推算出來(lái)。·unsigned int 表示所指向的內(nèi)存中的數(shù)字使用原碼形式,其余和signed int一樣,表示的是無(wú)符號(hào)數(shù)。·bool 表示所指向的內(nèi)存中的數(shù)字為邏輯值,取值為false或true。長(zhǎng)度為1個(gè)字節(jié)。·float 表示所指向的內(nèi)存按IEEE標(biāo)準(zhǔn)進(jìn)行解釋,為real*4,占用4字節(jié)內(nèi)存空間,等同于上篇中提

37、到的單精度浮點(diǎn)數(shù)。·double 表示所指向的內(nèi)存按IEEE標(biāo)準(zhǔn)進(jìn)行解釋,為real*8,可表示數(shù)的精度較float高,占用8字節(jié)內(nèi)存空間,等同于上篇提到的雙精度浮點(diǎn)數(shù)。·long double 表示所指向的內(nèi)存按IEEE標(biāo)準(zhǔn)進(jìn)行解釋,為real*10,可表示數(shù)的精度較double高,但在為32位Windows操作系統(tǒng)編寫程序時(shí),仍占用8字節(jié)內(nèi)存空間,等效于double,只是如果CPU支持此類浮點(diǎn)類型則還是可以進(jìn)行這個(gè)精度的計(jì)算。標(biāo)準(zhǔn)類型不止上面的幾個(gè),后面還會(huì)陸續(xù)提到。上面的長(zhǎng)度為2個(gè)字節(jié)也就是將兩個(gè)連續(xù)的內(nèi)存單元中的數(shù)字取出并合并在一起以表示一個(gè)數(shù)字,這和前面說(shuō)的一個(gè)算

38、盤表示不了的數(shù)字,就進(jìn)位以加入另一個(gè)算盤幫助表示是同樣的道理。上面的signed關(guān)鍵字是可以去掉的,即char等同于signed char,用以簡(jiǎn)化代碼的編寫。但也僅限于signed,如果是unsigned char,則在使用時(shí)依舊必須是unsigned char?,F(xiàn)在應(yīng)該已經(jīng)了解上篇中為什么數(shù)字還要分什么有符號(hào)無(wú)符號(hào)、長(zhǎng)整型短整型之類的了,而上面的short、char等也都只是長(zhǎng)度不同,這就由程序員自己根據(jù)可能出現(xiàn)的數(shù)字變化幅度來(lái)進(jìn)行選用了。類型只是對(duì)內(nèi)存中的數(shù)字的解釋,但上面的類型看起來(lái)相對(duì)簡(jiǎn)單了點(diǎn),且語(yǔ)義并不是很強(qiáng),即沒(méi)有什么特殊意思。為此,C+提供了自定義類型,也就是后繼文章中將要說(shuō)明

39、的結(jié)構(gòu)、類等。在本系列的第一篇中已經(jīng)說(shuō)過(guò),電腦編程的絕大部分工作就是操作內(nèi)存,而上面說(shuō)了,為了操作內(nèi)存,需要使用地址來(lái)標(biāo)識(shí)要操作的內(nèi)存塊的首地址(上面的long表示連續(xù)的4個(gè)字節(jié)內(nèi)存,其第一個(gè)內(nèi)存單元的地址稱作這連續(xù)4個(gè)字節(jié)內(nèi)存塊的首地址)。為此我們?cè)诰帉懗绦驎r(shí)必須記下地址。做5+2/3-5*2的計(jì)算,先計(jì)算出2/3的值,寫在草稿紙上,接著算出5*2的值,又寫在草稿紙上。為了接下來(lái)的加法和減法運(yùn)算,必須能夠知道草稿紙上的兩個(gè)數(shù)字哪個(gè)是2/3的值哪個(gè)是5*2的值。人就是通過(guò)記憶那兩個(gè)數(shù)在紙上的位置來(lái)記憶的,而電腦就是通過(guò)地址來(lái)標(biāo)識(shí)的。但電腦只會(huì)做加減乘除,不會(huì)去主動(dòng)記那些2/3、5*2的中間值的

40、位置,也就是地址。因此程序員必須完成這個(gè)工作,將那兩個(gè)地址記下來(lái)。問(wèn)題就是這里只有兩個(gè)值,也許好記一些,但如果多了,人是很難記住哪個(gè)地址對(duì)應(yīng)哪個(gè)值的,但人對(duì)符號(hào)比對(duì)數(shù)字要敏感得多,即人很容易記下一個(gè)名字而不是一個(gè)數(shù)字。為此,程序員就自己寫了一個(gè)表,表有兩列,一列是“2/3的值”,一列是對(duì)應(yīng)的地址。如果式子稍微復(fù)雜點(diǎn),那么那個(gè)表可能就有個(gè)二三十行,而每寫一行代碼就要去翻查相應(yīng)的地址,如果來(lái)個(gè)幾萬(wàn)行代碼那是人都不能忍受。C+作為高級(jí)語(yǔ)言,很正常地提供了上面問(wèn)題的解決之道,就是由編譯器來(lái)幫程序員維護(hù)那個(gè)表,要查的時(shí)候是編譯器去查,這也就是變量的功能。變量是一個(gè)映射元素。上面提到的表由編譯器維護(hù),而表

41、中的每一行都是這個(gè)表的一個(gè)元素(也稱記錄)。表有三列:變量名、對(duì)應(yīng)地址和相應(yīng)類型。變量名是一個(gè)標(biāo)識(shí)符,因此其命名規(guī)則完全按照上一篇所說(shuō)的來(lái)。當(dāng)要對(duì)某塊內(nèi)存寫入數(shù)據(jù)時(shí),程序員使用相應(yīng)的變量名進(jìn)行內(nèi)存的標(biāo)識(shí),而表中的對(duì)應(yīng)地址就記錄了這個(gè)地址,進(jìn)而將程序員給出的變量名,一個(gè)標(biāo)識(shí)符,映射成一個(gè)地址,因此變量是一個(gè)映射元素。而相應(yīng)類型則告訴編譯器應(yīng)該如何解釋此地址所指向的內(nèi)存,是2個(gè)連續(xù)字節(jié)還是4個(gè)?是原碼記錄還是補(bǔ)碼?而變量所對(duì)應(yīng)的地址所標(biāo)識(shí)的內(nèi)存的內(nèi)容叫做此變量的值。有如下的變量解釋:“可變的量,其相當(dāng)于一個(gè)盒子,數(shù)字就裝在盒子里,而變量名就寫在盒子外面,這樣電腦就知道我們要處理哪一個(gè)盒子,且不同的

42、盒子裝不同的東西,裝字符串的盒子就不能裝數(shù)字?!鄙厦婢褪俏业谝淮螌W(xué)習(xí)編程時(shí),書上寫的(是BASIC語(yǔ)言)。對(duì)于初學(xué)者也許很容易理解,也不能說(shuō)錯(cuò),但是造成的誤解將導(dǎo)致以后的程序編寫地千瘡百孔。上面的解釋隱含了一個(gè)意思變量是一塊內(nèi)存。這是嚴(yán)重錯(cuò)誤的!如果變量是一塊內(nèi)存,那么C+中著名的引用類型將被棄置荒野。變量實(shí)際并不是一塊內(nèi)存,只是一個(gè)映射元素,這是致關(guān)重要的。內(nèi)存的種類前面已經(jīng)說(shuō)了內(nèi)存是什么及其用處,但內(nèi)存是不能隨便使用的,因?yàn)椴僮飨到y(tǒng)自己也要使用內(nèi)存,而且現(xiàn)在的操作系統(tǒng)正常情況下都是多任務(wù)操作系統(tǒng),即可同時(shí)執(zhí)行多個(gè)程序,即使只有一個(gè)CPU。因此如果不對(duì)內(nèi)存訪問(wèn)加以節(jié)制,可能會(huì)破壞另一個(gè)程序的

43、運(yùn)作。比如我在紙上寫了2/3的值,而你未經(jīng)我同意且未通知我就將那個(gè)值擦掉,并寫上5*2的值,結(jié)果我后面的所有計(jì)算也就出錯(cuò)了。因此為了使用一塊內(nèi)存,需要向操作系統(tǒng)申請(qǐng),由操作系統(tǒng)統(tǒng)一管理所有程序使用的內(nèi)存。所以為了記錄一個(gè)long類型的數(shù)字,先向操作系統(tǒng)申請(qǐng)一塊連續(xù)的4字節(jié)長(zhǎng)的內(nèi)存空間,然后操作系統(tǒng)就會(huì)在內(nèi)存中查看,看是否還有連續(xù)的4個(gè)字節(jié)長(zhǎng)的內(nèi)存,如果找到,則返回此4字節(jié)內(nèi)存的首地址,然后編譯器編譯的指令將其記錄在前面提到的變量表中,最后就可以用它記錄一些臨時(shí)計(jì)算結(jié)果了。上面的過(guò)程稱為要求操作系統(tǒng)分配一塊內(nèi)存。這看起來(lái)很不錯(cuò),但是如果只為了4個(gè)字節(jié)就要求操作系統(tǒng)搜索一下內(nèi)存狀況,那么如果需要1

44、00個(gè)臨時(shí)數(shù)據(jù),就要求操作系統(tǒng)分配內(nèi)存100次,很明顯地效率低下(無(wú)謂的99次查看內(nèi)存狀況)。因此C+發(fā)現(xiàn)了這個(gè)問(wèn)題,并且操作系統(tǒng)也提出了相應(yīng)的解決方法,最后提出了如下的解決之道。棧(Stack) 任何程序執(zhí)行前,預(yù)先分配一固定長(zhǎng)度的內(nèi)存空間,這塊內(nèi)存空間被稱作棧(這種說(shuō)法并不準(zhǔn)確,但由于實(shí)際涉及到線程,在此為了不將問(wèn)題復(fù)雜化才這樣說(shuō)明),也被叫做堆棧。那么在要求一個(gè)4字節(jié)內(nèi)存時(shí),實(shí)際是在這個(gè)已分配好的內(nèi)存空間中獲取內(nèi)存,即內(nèi)存的維護(hù)工作由程序員自己來(lái)做,即程序員自己判斷可以使用哪些內(nèi)存,而不是操作系統(tǒng),直到已分配的內(nèi)存用完。很明顯,上面的工作是由編譯器來(lái)做的,不用程序員操心,因此就程序員的角

45、度來(lái)看什么事情都沒(méi)發(fā)生,還是需要像原來(lái)那樣向操作系統(tǒng)申請(qǐng)內(nèi)存,然后再使用。但工作只是從操作系統(tǒng)變到程序自己而已,要維護(hù)內(nèi)存,依然要耗費(fèi)CPU的時(shí)間,不過(guò)要簡(jiǎn)單多了,因?yàn)椴挥脴?biāo)記一塊內(nèi)存是否有人使用,而專門記錄一個(gè)地址。此地址以上的內(nèi)存空間就是有人正在使用的,而此地址以下的內(nèi)存空間就是無(wú)人使用的。之所以是以下的空間為無(wú)人使用而不是以上,是當(dāng)此地址減小到0時(shí)就可以知道堆棧溢出了(如果你已經(jīng)有些基礎(chǔ),請(qǐng)不要把0認(rèn)為是虛擬內(nèi)存地址,關(guān)于虛擬內(nèi)存將會(huì)在C+從零開(kāi)始(十八)中進(jìn)行說(shuō)明,這里如此解釋只是為了方便理解)。而且CPU還專門對(duì)此法提供了支持,給出了兩條指令,轉(zhuǎn)成匯編語(yǔ)言就是push和pop,表示壓

46、棧和出棧,分別減小和增大那個(gè)地址。而最重要的好處就是由于程序一開(kāi)始執(zhí)行時(shí)就已經(jīng)分配了一大塊連續(xù)內(nèi)存,用一個(gè)變量記錄這塊連續(xù)內(nèi)存的首地址,然后程序中所有用到的,程序員以為是向操作系統(tǒng)分配的內(nèi)存都可以通過(guò)那個(gè)首地址加上相應(yīng)偏移來(lái)得到正確位置,而這很明顯地由編譯器做了。因此實(shí)際上等同于在編譯時(shí)期(即編譯器編譯程序的時(shí)候)就已經(jīng)分配了內(nèi)存(注意,實(shí)際編譯時(shí)期是不能分配內(nèi)存的,因?yàn)榉峙鋬?nèi)存是指程序運(yùn)行時(shí)向操作系統(tǒng)申請(qǐng)內(nèi)存,而這里由于使用堆棧,則編譯器將生成一些指令,以使得程序一開(kāi)始就向操作系統(tǒng)申請(qǐng)內(nèi)存,如果失敗則立刻退出,而如果不退出就表示那些內(nèi)存已經(jīng)分配到了,進(jìn)而代碼中使用首地址加偏移來(lái)使用內(nèi)存也就是

47、有效的),但壞處也就是只能在編譯時(shí)期分配內(nèi)存。堆(Heap) 上面的工作是編譯器做的,即程序員并不參與堆棧的維護(hù)。但上面已經(jīng)說(shuō)了,堆棧相當(dāng)于在編譯時(shí)期分配內(nèi)存,因此一旦計(jì)算好某塊內(nèi)存的偏移,則這塊內(nèi)存就只能那么大,不能變化了(如果變化會(huì)導(dǎo)致其他內(nèi)存塊的偏移錯(cuò)誤)。比如要求客戶輸入定單數(shù)據(jù),可能有10份定單,也可能有100份定單,如果一開(kāi)始就定好了內(nèi)存大小,則可能造成不必要的浪費(fèi),又或者內(nèi)存不夠。為了解決上面的問(wèn)題,C+提供了另一個(gè)途徑,即允許程序員有兩種向操作系統(tǒng)申請(qǐng)內(nèi)存的方式。前一種就是在棧上分配,申請(qǐng)的內(nèi)存大小固定不變。后一種是在堆上分配,申請(qǐng)的內(nèi)存大小可以在運(yùn)行的時(shí)候變化,不是固定不變的

48、。那么什么叫堆?在Windows操作系統(tǒng)下,由操作系統(tǒng)分配的內(nèi)存就叫做堆,而??梢哉J(rèn)為是在程序開(kāi)始時(shí)就分配的堆(這并不準(zhǔn)確,但為了不復(fù)雜化問(wèn)題,故如此說(shuō)明)。因此在堆上就可以分配大小變化的內(nèi)存塊,因?yàn)槭沁\(yùn)行時(shí)期即時(shí)分配的內(nèi)存,而不是編譯時(shí)期已計(jì)算好大小的內(nèi)存塊。變量的定義上面說(shuō)了那么多,你可能看得很暈,畢竟連一個(gè)實(shí)例都沒(méi)有,全是文字,下面就來(lái)幫助加深對(duì)上面的理解。定義一個(gè)變量,就是向上面說(shuō)的由編譯器維護(hù)的變量表中添加元素,其語(yǔ)法如下:long a;先寫變量的類型,然后一個(gè)或多個(gè)空格或制表符(t)或其它間隔符,接著變量的名字,最后用分號(hào)結(jié)束。要同時(shí)定義多個(gè)變量,則各變量間使用逗號(hào)隔開(kāi),如下:lo

49、ng a, b, c; unsigned short e, a_34c;上面是兩條變量定義語(yǔ)句,各語(yǔ)句間用分號(hào)隔開(kāi),而各同類型變量間用逗號(hào)隔開(kāi)。而前面的式子5+2/3-5*2,則如下書寫。long a = 2/3, b = 5*2; long c = 5 + a b;可以不用再去記那煩人的地址了,只需記著a、b這種簡(jiǎn)單的標(biāo)識(shí)符。當(dāng)然,上面的式子不一定非要那么寫,也可以寫成:long c = 5 + 2 / 3 5 * 2; 而那些a、b等中間變量編譯器會(huì)自動(dòng)生成并使用(實(shí)際中編譯器由于優(yōu)化的原因?qū)⒅苯佑?jì)算出結(jié)果,而不會(huì)生成實(shí)際的計(jì)算代碼)。下面就是問(wèn)題的關(guān)鍵,定義變量就是添加一個(gè)映射。前面已經(jīng)

50、說(shuō)了,這個(gè)映射是將變量名和一個(gè)地址關(guān)聯(lián),因此在定義一個(gè)變量時(shí),編譯器為了能將變量名和某個(gè)地址對(duì)應(yīng)起來(lái),幫程序員在前面提到的棧上分配了一塊內(nèi)存,大小就視這個(gè)變量類型的大小。如上面的a、b、c的大小都是4個(gè)字節(jié),而e、a_34c的大小都是2個(gè)字節(jié)。假設(shè)編譯器分配的棧在一開(kāi)始時(shí)的地址是1000,并假設(shè)變量a所對(duì)應(yīng)的地址是1000-56,則b所對(duì)應(yīng)的地址就是1000-60,而c所對(duì)應(yīng)的就是1000-64,e對(duì)應(yīng)的是1000-66,a_34c是1000-68。如果這時(shí)b突然不想是4字節(jié)了,而希望是8字節(jié),則后續(xù)的c、e、a_34c都將由于還是原來(lái)的偏移位置而使用了錯(cuò)誤的內(nèi)存,這也就是為什么棧上分配的內(nèi)存

51、必須是固定大小??紤]前面說(shuō)的紅色文字:“變量實(shí)際并不是一塊內(nèi)存,只是一個(gè)映射元素”??墒侵灰x一個(gè)變量,就會(huì)相應(yīng)地得到一塊內(nèi)存,為什么不說(shuō)變量就是一塊內(nèi)存?上面定義變量時(shí)之所以會(huì)分配一塊內(nèi)存是因?yàn)樽兞渴且粋€(gè)映射元素,需要一個(gè)對(duì)應(yīng)地址,因此才在棧上分配了一塊內(nèi)存,并將其地址記錄到變量表中。但是變量是可以有別名的,即另一個(gè)名字。這個(gè)說(shuō)法是不準(zhǔn)確的,應(yīng)該是變量所對(duì)應(yīng)的內(nèi)存塊有另一個(gè)名字,而不止是這個(gè)變量的名字。為什么要有別名?這是語(yǔ)義的需要,表示既是什么又是什么。比如一塊內(nèi)存,里面記錄了老板的信息,因此起名為Boss,但是老板又是另一家公司的行政經(jīng)理,故變量名應(yīng)該為Manager,而在程序中有段代

52、碼是老板的公司相關(guān)的,而另一段是老板所在公司相關(guān)的,在這兩段程序中都要使用到老板的信息,那到底是使用Boss還是Manager?其實(shí)使用什么都不會(huì)對(duì)最終生成的機(jī)器代碼產(chǎn)生什么影響,但此處出于語(yǔ)義的需要就應(yīng)該使用別名,以期從代碼上表現(xiàn)出所編寫程序的意思。在C+中,為了支持變量別名,提供了引用變量這個(gè)概念。要定義一個(gè)引用變量,在定義變量時(shí),在變量名的前面加一個(gè)“&”,如下書寫:long a; long &a1 = a, &a2 = a, &a3 = a2;上面的a1、a2、a3都是a所對(duì)應(yīng)的內(nèi)存塊的別名。這里在定義變量a時(shí)就在棧上分配了一塊4字節(jié)內(nèi)存,而在定義a1時(shí)

53、卻沒(méi)有分配任何內(nèi)存,直接將變量a所映射的地址作為變量a1的映射地址,進(jìn)而形成對(duì)定義a時(shí)所分配的內(nèi)存的別名。因此上面的Boss和Manager,應(yīng)該如下(其中Person是一個(gè)結(jié)構(gòu)或類或其他什么自定義類型,這將在后繼的文章中陸續(xù)說(shuō)明):Person Boss; Person &Manager = Boss;由于變量一旦定義就不能改變(指前面說(shuō)的變量表里的內(nèi)容,不是變量的值),直到其被刪除,所以上面在定義引用變量的時(shí)候必須給出欲別名的變量以初始化前面的變量表,否則編譯器編譯時(shí)將報(bào)錯(cuò)。現(xiàn)在應(yīng)該就更能理解前面關(guān)于變量的紅字的意思了。并不是每個(gè)變量定義時(shí)都會(huì)分配內(nèi)存空間的。而關(guān)于如何在堆上分配內(nèi)

54、存,將在介紹完指針后予以說(shuō)明,并進(jìn)而說(shuō)明上一篇遺留下來(lái)的關(guān)于字符串的問(wèn)題。2 重載操作符和函數(shù)對(duì)象1.重載操作符跟重載函數(shù)一樣的,只不過(guò)要區(qū)分成員的和非成員的成員的重載操作符,第一個(gè)參數(shù)默認(rèn)了是this 指針形參,所以重載操作符作為成員函數(shù),都應(yīng)該是右操作符比如:ostream& operator<<(ostream &out)  /右操作運(yùn)算符  調(diào)用方式:Screen s;s<<std:cout;out<<'('<<height<<','<<width

55、<<')'<<*(pContents)<<std:endl;return out;friend ostream& operator<<(ostream& out,Screen& s) /友原函數(shù)的重載操作符out<<'('<<s.height<<','<<s.width<<')'<<*(s.pContents)<<std:endl;return out;上述一個(gè)成員,一個(gè)非成員

56、重載方式,調(diào)用相應(yīng)如下:s1<<std:cout; / 調(diào)用的是作為成員函數(shù)的重載操作符std:cout<<s2; /調(diào)用的是友原函數(shù)2.至于返回值,值得注意的地方是:何時(shí)返回類型的引用值何時(shí)返回類類型,應(yīng)該跟內(nèi)置的操作符一致;比如+=返回引用,+則應(yīng)該返回類類型本身。如下:Screen& operator+=(Screen& rhs)  /復(fù)合賦值操作符height += rhs.height;width  += rhs.width;*pContents += *(rhs.pContents);return *this;friend

57、 Screen operator+(Screen& s1,Screen& s2 )Screen s;s.width = s1.width+s2.width;s.height = s1.height+s2.height;*(s.pContents) = *(s1.pContents)+*(s2.pContents);return s;/可以兩種方式調(diào)用:Screen s0,s1,s2;s0=s1+s2;  /隱式調(diào)用/或者s0=operator+(s1,s2);/我理解為顯式調(diào)用3.函數(shù)對(duì)象可以直接調(diào)用類的構(gòu)造函數(shù)產(chǎn)生一個(gè)臨時(shí)對(duì)象,作為參數(shù)傳遞給函數(shù)實(shí)參比如:Class

58、 GT_clspublic:GT_cls(int val):bool(val)bool operator(const string s) ;return s.size()>=bound;private:std:string:size_type bound;然后count_if(word.begin(),word.end(),GT_cls(6);  /count_if標(biāo)準(zhǔn)庫(kù)算法 這里GT_cls先構(gòu)造臨時(shí)對(duì)象,然后count_if傳遞word對(duì)象進(jìn)GT_cls然后調(diào)用函數(shù)()/C+ Primer里面的例子再比如如下:struct IterOpvirtual void operator()(std:vector<char>:const_iterator& iter) = 0;struct IterAdd : public IterOpvoid operator()(std:vector<char>:const_iterato

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論