是的,這一篇的文章主題是「指針與內(nèi)存模型」 說(shuō)到指針,就不可能脫離開(kāi)內(nèi)存,學(xué)會(huì)指針的人分為兩種,一種是不了解內(nèi)存模型,另外一種則是了解。 不了解的對(duì)指針的理解就停留在“指針就是變量的地址”這句話(huà),會(huì)比較害怕使用指針,特別是各種高級(jí)操作。 而了解內(nèi)存模型的則可以把指針用得爐火純青,各種 byte 隨意操作,讓人直呼 666。 這篇看完,相信你會(huì)對(duì)指針有一個(gè)新的認(rèn)識(shí),坐等打臉?? 一、內(nèi)存本質(zhì)編程的本質(zhì)其實(shí)就是操控?cái)?shù)據(jù),數(shù)據(jù)存放在內(nèi)存中。 因此,如果能更好地理解內(nèi)存的模型,以及 C 如何管理內(nèi)存,就能對(duì)程序的工作原理洞若觀火,從而使編程能力更上一層樓。 大家真的別認(rèn)為這是空話(huà),我大一整年都不敢用 C 寫(xiě)上千行的程序也很抗拒寫(xiě) C。 因?yàn)橐坏┥锨?,?jīng)常出現(xiàn)各種莫名其妙的內(nèi)存錯(cuò)誤,一不小心就發(fā)生了 coredump...... 而且還無(wú)從排查,分析不出原因。 相比之下,那時(shí)候最喜歡 Java,在 Java 里隨便怎么寫(xiě)都不會(huì)發(fā)生類(lèi)似的異常,頂多偶爾來(lái)個(gè) NullPointerException,也是比較好排查的。 直到后來(lái)對(duì)內(nèi)存和指針有了更加深刻的認(rèn)識(shí),才慢慢會(huì)用 C 寫(xiě)上千行的項(xiàng)目,也很少會(huì)再有內(nèi)存問(wèn)題了。(過(guò)于自信 「指針存儲(chǔ)的是變量的內(nèi)存地址」這句話(huà)應(yīng)該任何講 C 語(yǔ)言的書(shū)都會(huì)提到吧。 所以,要想徹底理解指針,首先要理解 C 語(yǔ)言中變量的存儲(chǔ)本質(zhì),也就是內(nèi)存。 1.1 內(nèi)存編址計(jì)算機(jī)的內(nèi)存是一塊用于存儲(chǔ)數(shù)據(jù)的空間,由一系列連續(xù)的存儲(chǔ)單元組成,就像下面這樣, 每一個(gè)單元格都表示 1 個(gè) Bit,一個(gè) bit 在 EE 專(zhuān)業(yè)的同學(xué)看來(lái)就是高低電位,而在 CS 同學(xué)看來(lái)就是 0、1 兩種狀態(tài)。 由于 1 個(gè) bit 只能表示兩個(gè)狀態(tài),所以大佬們規(guī)定 8個(gè) bit 為一組,命名為 byte。 并且將 byte 作為內(nèi)存尋址的最小單元,也就是給每個(gè) byte 一個(gè)編號(hào),這個(gè)編號(hào)就叫內(nèi)存的地址。 這就相當(dāng)于,我們給小區(qū)里的每個(gè)單元、每個(gè)住戶(hù)都分配一個(gè)門(mén)牌號(hào):301、302、403、404、501...... 在生活中,我們需要保證門(mén)牌號(hào)唯一,這樣就能通過(guò)門(mén)牌號(hào)很精準(zhǔn)的定位到一家人。 同樣,在計(jì)算機(jī)中,我們也要保證給每一個(gè) byte 的編號(hào)都是唯一的,這樣才能夠保證每個(gè)編號(hào)都能訪問(wèn)到唯一確定的 byte。 1.2 內(nèi)存地址空間上面我們說(shuō)給內(nèi)存中每個(gè) byte 唯一的編號(hào),那么這個(gè)編號(hào)的范圍就決定了計(jì)算機(jī)可尋址內(nèi)存的范圍。 所有編號(hào)連起來(lái)就叫做內(nèi)存的地址空間,這和大家平時(shí)常說(shuō)的電腦是 32 位還是 64 位有關(guān)。 早期 Intel 8086、8088 的 CPU 就是只支持 16 位地址空間,寄存器和地址總線(xiàn)都是 16 位,這意味著最多對(duì) 這點(diǎn)內(nèi)存空間顯然不夠用,后來(lái),80286 在 8086 的基礎(chǔ)上將地址總線(xiàn)和地址寄存器擴(kuò)展到了20 位,也被叫做 A20 地址總線(xiàn)。 當(dāng)時(shí)在寫(xiě) mini os 的時(shí)候,還需要通過(guò) BIOS 中斷去啟動(dòng) A20 地址總線(xiàn)的開(kāi)關(guān)。 但是,現(xiàn)在的計(jì)算機(jī)一般都是 32 位起步了,32 位意味著可尋址的內(nèi)存范圍是 所以,如果你的電腦是 32 位的,那么你裝超過(guò) 4G 的內(nèi)存條也是無(wú)法充分利用起來(lái)的。 好了,這就是內(nèi)存和內(nèi)存編址。 1.3 變量的本質(zhì)有了內(nèi)存,接下來(lái)我們需要考慮,int、double 這些變量是如何存儲(chǔ)在 0、1 單元格的。 在 C 語(yǔ)言中我們會(huì)這樣定義變量: int a = 999;當(dāng)你寫(xiě)下一個(gè)變量定義的時(shí)候,實(shí)際上是向內(nèi)存申請(qǐng)了一塊空間來(lái)存放你的變量。 我們都知道 int 類(lèi)型占 4 個(gè)字節(jié),并且在計(jì)算機(jī)中數(shù)字都是用補(bǔ)碼(不了解補(bǔ)碼的記得去百度)表示的。
這里有 4 個(gè)byte,所以需要四個(gè)單元格來(lái)存儲(chǔ): 有沒(méi)有注意到,我們把高位的字節(jié)放在了低地址的地方。 那能不能反過(guò)來(lái)呢? 當(dāng)然,這就引出了大端和小端。 像上面這種將高位字節(jié)放在內(nèi)存低地址的方式叫做大端 反之,將低位字節(jié)放在內(nèi)存低地址的方式就叫做小端: 上面只說(shuō)明了 int 型的變量如何存儲(chǔ)在內(nèi)存,而 float、char 等類(lèi)型實(shí)際上也是一樣的,都需要先轉(zhuǎn)換為補(bǔ)碼。 對(duì)于多字節(jié)的變量類(lèi)型,還需要按照大端或者小端的格式,依次將字節(jié)寫(xiě)入到內(nèi)存單元。 記住上面這兩張圖,這就是編程語(yǔ)言中所有變量的在內(nèi)存中的樣子,不管是 int、char、指針、數(shù)組、結(jié)構(gòu)體、對(duì)象... 都是這樣放在內(nèi)存的。 二、指針是什么東西?2.1 變量放在哪?上面我說(shuō),定義一個(gè)變量實(shí)際就是向計(jì)算機(jī)申請(qǐng)了一塊內(nèi)存來(lái)存放。 那如果我們要想知道變量到底放在哪了呢? 可以通過(guò)運(yùn)算符 (PS: 實(shí)際上這個(gè)地址是虛擬地址,并不是真正物理內(nèi)存上的地址 我們可以把這個(gè)地址打印出來(lái): 大概會(huì)是像這樣的一串?dāng)?shù)字: 2.2 指針本質(zhì)上面說(shuō),我們可以通過(guò) 也就是在 C 語(yǔ)言中如何表示地址這個(gè)概念呢? 對(duì),就是指針,你可以這樣: int *pa = &a; pa 中存儲(chǔ)的就是變量 在這里我想談幾個(gè)看起來(lái)有點(diǎn)無(wú)聊的話(huà)題:
當(dāng)然可以,但是變量名是有局限的。
是變量地址的符號(hào)化,變量是為了讓我們編程時(shí)更加方便,對(duì)人友好,可計(jì)算機(jī)可不認(rèn)識(shí)什么變量 所以當(dāng)你去查看 C 語(yǔ)言編譯后的匯編代碼,就會(huì)發(fā)現(xiàn)變量名消失了,取而代之的是一串串抽象的地址。 你可以認(rèn)為,編譯器會(huì)自動(dòng)維護(hù)一個(gè)映射,將我們程序中的變量名轉(zhuǎn)換為變量所對(duì)應(yīng)的地址,然后再對(duì)這個(gè)地址去進(jìn)行讀寫(xiě)。 也就是有這樣一個(gè)映射表存在,將變量名自動(dòng)轉(zhuǎn)化為地址: 說(shuō)的好! 可是我還是不知道指針存在的必要性,那么問(wèn)題來(lái)了,看下面代碼: int func(...) {假設(shè)我有一個(gè)需求:
你說(shuō)可以通過(guò) 這樣在 理論上這是完全沒(méi)有問(wèn)題的,但是問(wèn)題在于: 編譯器該如何區(qū)分一個(gè) int 里你存的到底是 int 類(lèi)型的值,還是另外一個(gè)變量的地址(即指針)。 這如果完全靠我們編程人員去人腦記憶了,會(huì)引入復(fù)雜性,并且無(wú)法通過(guò)編譯器檢測(cè)一些語(yǔ)法錯(cuò)誤。 而通過(guò) 編譯器也可以通過(guò)類(lèi)型檢查來(lái)排除一些編譯錯(cuò)誤。 這就是指針存在的必要性。 實(shí)際上任何語(yǔ)言都有這個(gè)需求,只不過(guò)很多語(yǔ)言為了安全性,給指針戴上了一層枷鎖,將指針包裝成了引用。 可能大家學(xué)習(xí)的時(shí)候都是自然而然的接受指針這個(gè)東西,但是還是希望這段啰嗦的解釋對(duì)你有一定啟發(fā)。 同時(shí),在這里提點(diǎn)小問(wèn)題: 既然指針的本質(zhì)都是變量的內(nèi)存首地址,即一個(gè) int 類(lèi)型的整數(shù)。
2.3 解引用上面的問(wèn)題,就是為了引出指針解引用的。
這個(gè)操作就叫做解引用,在 C 語(yǔ)言中通過(guò)運(yùn)算符 比如 我們說(shuō)指針存儲(chǔ)的是變量?jī)?nèi)存的首地址,那編譯器怎么知道該從首地址開(kāi)始取多少個(gè)字節(jié)呢? 這就是指針類(lèi)型發(fā)揮作用的時(shí)候,編譯器會(huì)根據(jù)指針的所指元素的類(lèi)型去判斷應(yīng)該取多少個(gè)字節(jié)。 如果是 int 型的指針,那么編譯器就會(huì)產(chǎn)生提取四個(gè)字節(jié)的指令,char 則只提取一個(gè)字節(jié),以此類(lèi)推。 下面是指針內(nèi)存示意圖:
當(dāng)解引用的時(shí)候,就會(huì)從這個(gè)首地址連續(xù)劃出 4 個(gè) byte,然后按照 int 類(lèi)型的編碼方式解釋。 2.4 活學(xué)活用別看這個(gè)地方很簡(jiǎn)單,但卻是深刻理解指針的關(guān)鍵。 舉兩個(gè)例子來(lái)詳細(xì)說(shuō)明: 比如: float f = 1.0;你能解釋清楚上面過(guò)程,對(duì)于 或者 實(shí)際上,從內(nèi)存層面來(lái)說(shuō), 如圖: 假設(shè)這是 詳細(xì)過(guò)程如下:
上面第二步什么都沒(méi)做,這個(gè)表達(dá)式只是說(shuō) : “噢,我認(rèn)為 最后當(dāng)去解引用的時(shí)候 這個(gè)過(guò)程 當(dāng)然,這里最后的值肯定不是 1,至于是什么,大家可以去真正算一下。 那反過(guò)來(lái),這樣呢? 如圖: 具體過(guò)程和上述一樣,但上面肯定不會(huì)報(bào)錯(cuò),這里卻不一定。 為什么?
但是 當(dāng)然,如果只是讀,大概率是沒(méi)問(wèn)題的。 但是,有時(shí)候需要向這個(gè)區(qū)域?qū)懭胄碌闹?,比如?/p> *(float*)&c = 1.0;那么就可能發(fā)生 coredump,也就是訪存失敗。 另外,就算是不會(huì) coredump,這種也會(huì)破壞這塊內(nèi)存原有的值,因?yàn)楹芸赡苓@是是其它變量的內(nèi)存空間,而我們?nèi)ジ采w了人家的內(nèi)容,肯定會(huì)導(dǎo)致隱藏的 bug。 如果你理解了上面這些內(nèi)容,那么使用指針一定會(huì)更加的自如。 2.6 看個(gè)小問(wèn)題講到這里,我們來(lái)看一個(gè)問(wèn)題,這是一位群友問(wèn)的,這是他的需求: 這是他寫(xiě)的代碼: 他把 double 寫(xiě)進(jìn)文件再讀出來(lái),然后發(fā)現(xiàn)打印的值對(duì)不上。 而關(guān)鍵的地方就在于這里: 他可能認(rèn)為 注意,這一切都是他認(rèn)為的,實(shí)際上編譯器會(huì)認(rèn)為: “哦, 然后把第一個(gè)字節(jié)的值傳遞給了 printf 函數(shù),printf 函數(shù)會(huì)發(fā)現(xiàn), 這就是整個(gè)過(guò)程。 錯(cuò)誤關(guān)鍵就是,這個(gè)同學(xué)誤認(rèn)為,任何指針解引用都是拿到里面“我們認(rèn)為的那個(gè)值”,實(shí)際上編譯器并不知道,編譯器只會(huì)傻傻的按照指針的類(lèi)型去解釋。 所以這里改成: printf('%f %x\n', *(float*)buffer, *(float*)buffer);相當(dāng)于明確的告訴編譯器: “ 三、 結(jié)構(gòu)體和指針結(jié)構(gòu)體內(nèi)包含多個(gè)成員,這些成員之間在內(nèi)存中是如何存放的呢? 比如: 這是一個(gè)定點(diǎn)小數(shù)結(jié)構(gòu)體,它在內(nèi)存占 8 個(gè)字節(jié)(這里不考慮內(nèi)存對(duì)齊),兩個(gè)成員域是這樣存儲(chǔ)的: 我們把 10 放在了結(jié)構(gòu)體中基地址偏移為 0 的域,2 放在了偏移為 4 的域。 接下來(lái)我們做一個(gè)正常人永遠(yuǎn)不會(huì)做的操作: ((fraction*)(&fp.denom))->num = 5; 上面這個(gè)究竟會(huì)輸出多少呢?自己先思考下噢~ 接下來(lái)我分析下這個(gè)過(guò)程發(fā)生了什么: ![]() 首先, 在這個(gè)新結(jié)構(gòu)體中,最上面四個(gè)字節(jié)變成了 denom 域,而 fp 的 denom 域相當(dāng)于新結(jié)構(gòu)體的 num 域。 因此:
實(shí)際上改變的是
則是將最上面四個(gè)字節(jié)賦值為 12。 當(dāng)然,往那四字節(jié)內(nèi)存寫(xiě)入值,結(jié)果是無(wú)法預(yù)測(cè)的,可能會(huì)造成程序崩潰,因?yàn)橐苍S那里恰好存儲(chǔ)著函數(shù)調(diào)用棧幀的關(guān)鍵信息,也可能那里沒(méi)有寫(xiě)入權(quán)限。 大家初學(xué) C 語(yǔ)言的很多 coredump 錯(cuò)誤都是類(lèi)似原因造成的。 所以最后輸出的是 5。 為什么要講這種看起來(lái)莫名其妙的代碼? 就是為了說(shuō)明結(jié)構(gòu)體的本質(zhì)其實(shí)就是一堆的變量打包放在一起,而訪問(wèn)結(jié)構(gòu)體中的域,就是通過(guò)結(jié)構(gòu)體的起始地址,也叫基地址,然后加上域的偏移。 其實(shí),C++、Java 中的對(duì)象也是這樣存儲(chǔ)的,無(wú)非是他們?yōu)榱藢?shí)現(xiàn)某些面向?qū)ο蟮奶匦?,?huì)在數(shù)據(jù)成員以外,添加一些 Head 信息,比如C++ 的虛函數(shù)表。 實(shí)際上,我們是完全可以用 C 語(yǔ)言去模仿的。 這就是為什么一直說(shuō) C 語(yǔ)言是基礎(chǔ),你真正懂了 C 指針和內(nèi)存,對(duì)于其它語(yǔ)言你也會(huì)很快的理解其對(duì)象模型以及內(nèi)存布局。 四、多級(jí)指針說(shuō)起多級(jí)指針這個(gè)東西,我以前大一,最多理解到 2 級(jí),再多真的會(huì)把我繞暈,經(jīng)常也會(huì)寫(xiě)錯(cuò)代碼。 你要是給我寫(xiě)個(gè)這個(gè): 其實(shí),多級(jí)指針也沒(méi)那么復(fù)雜,就是指針的指針的指針的指針......非常簡(jiǎn)單。 今天就帶大家認(rèn)識(shí)一下多級(jí)指針的本質(zhì)。 首先,我要說(shuō)一句話(huà),沒(méi)有多級(jí)指針這種東西,指針就是指針,多級(jí)指針只是為了我們方便表達(dá)而取的邏輯概念。 首先看下生活中的快遞柜: ![]() 這種大家都用過(guò)吧,豐巢或者超市儲(chǔ)物柜都是這樣,每個(gè)格子都有一個(gè)編號(hào),我們只需要拿到編號(hào),然后就能找到對(duì)應(yīng)的格子,取出里面的東西。 這里的格子就是內(nèi)存單元,編號(hào)就是地址,格子里放的東西就對(duì)應(yīng)存儲(chǔ)在內(nèi)存中的內(nèi)容。 假設(shè)我把一本書(shū),放在了 03 號(hào)格子,然后把 03 這個(gè)編號(hào)告訴你,你就可以根據(jù) 03 去取到里面的書(shū)。 那如果我把書(shū)放在 05 號(hào)格子,然后在 03 號(hào)格子只放一個(gè)小紙條,上面寫(xiě)著:「書(shū)放在 05 號(hào)」。 你會(huì)怎么做? 當(dāng)然是打開(kāi) 03 號(hào)格子,然后取出了紙條,根據(jù)上面內(nèi)容去打開(kāi) 05 號(hào)格子得到書(shū)。 這里的 03 號(hào)格子就叫指針,因?yàn)樗锩娣诺氖侵赶蚱渌褡拥男〖垪l(地址)而不是具體的書(shū)。 明白了嗎? 那我如果把書(shū)放在 07 號(hào)格子,然后在 05 號(hào)格子 放一個(gè)紙條:「書(shū)放在 07號(hào)」,同時(shí)在03號(hào)格子放一個(gè)紙條「書(shū)放在 05號(hào)」 ![]() 這里的 03 號(hào)格子就叫二級(jí)指針,05 號(hào)格子就叫指針,而 07 號(hào)就是我們平常用的變量。 依次,可類(lèi)推出 N 級(jí)指針。 所以你明白了嗎?同樣的一塊內(nèi)存,如果存放的是別的變量的地址,那么就叫指針,存放的是實(shí)際內(nèi)容,就叫變量。 上面這段代碼, 內(nèi)存示意圖如下: ![]() 不管幾級(jí)指針有兩個(gè)最核心的東西:
這就是我為什么多級(jí)指針是邏輯上的概念,實(shí)際上一塊內(nèi)存要么放實(shí)際內(nèi)容,要么放其它變量地址,就這么簡(jiǎn)單。 怎么去解讀
只能存放 對(duì)于二級(jí)指針甚至多級(jí)指針,我們都可以把它拆成兩部分。 首先不管是多少級(jí)的指針變量,它首先是一個(gè)指針變量,指針變量就是一個(gè) 比如 五、指針與數(shù)組5.1 一維數(shù)組數(shù)組是 C 自帶的基本數(shù)據(jù)結(jié)構(gòu),徹底理解數(shù)組及其用法是開(kāi)發(fā)高效應(yīng)用程序的基礎(chǔ)。 數(shù)組和指針表示法緊密關(guān)聯(lián),在合適的上下文中可以互換。 如下: int array[10] = {10, 9, 8, 7};在內(nèi)存中,數(shù)組是一塊連續(xù)的內(nèi)存空間: ![]() 第 0 個(gè)元素的地址稱(chēng)為數(shù)組的首地址,數(shù)組名實(shí)際就是指向數(shù)組首地址,當(dāng)我們通過(guò) 實(shí)際上可以看做 學(xué)過(guò)匯編的同學(xué),一定對(duì)這種方式不陌生,這是匯編中尋址方式的一種:基址變址尋址。 看完上面的代碼,很多同學(xué)可能會(huì)認(rèn)為指針和數(shù)組完全一致,可以互換,這是完全錯(cuò)誤的。 盡管數(shù)組名字有時(shí)候可以當(dāng)做指針來(lái)用,但數(shù)組的名字不是指針。 最典型的地方就是在 sizeof: 第一個(gè)將會(huì)輸出 40,因?yàn)?nbsp; 為什么會(huì)這樣呢? 站在編譯器的角度講,變量名、數(shù)組名都是一種符號(hào),它們都是有類(lèi)型的,它們最終都要和數(shù)據(jù)綁定起來(lái)。 變量名用來(lái)指代一份數(shù)據(jù),數(shù)組名用來(lái)指代一組數(shù)據(jù)(數(shù)據(jù)集合),它們都是有類(lèi)型的,以便推斷出所指代的數(shù)據(jù)的長(zhǎng)度。 對(duì),數(shù)組也有類(lèi)型,我們可以將 int、float、char 等理解為基本類(lèi)型,將數(shù)組理解為由基本類(lèi)型派生得到的稍微復(fù)雜一些的類(lèi)型, 數(shù)組的類(lèi)型由元素的類(lèi)型和數(shù)組的長(zhǎng)度共同構(gòu)成。而 編譯器在編譯過(guò)程中會(huì)創(chuàng)建一張專(zhuān)門(mén)的表格用來(lái)保存變量名及其對(duì)應(yīng)的數(shù)據(jù)類(lèi)型、地址、作用域等信息。
所以,這里對(duì)數(shù)組名使用
雖然在這里它指向的是一個(gè)數(shù)組,但數(shù)組也只是一塊連續(xù)的內(nèi)存,沒(méi)有開(kāi)始和結(jié)束標(biāo)志,也沒(méi)有額外的信息來(lái)記錄數(shù)組到底多長(zhǎng)。 所以對(duì) 也就是說(shuō),編譯器并沒(méi)有把 5.2 二維數(shù)組大家不要認(rèn)為二維數(shù)組在內(nèi)存中就是按行、列這樣二維存儲(chǔ)的,實(shí)際上,不管二維、三維數(shù)組... 都是編譯器的語(yǔ)法糖。 存儲(chǔ)上和一維數(shù)組沒(méi)有本質(zhì)區(qū)別,舉個(gè)例子: int array[3][3] = {{1, 2,3}, {4, 5,6},{7, 8, 9}};或許你以為在內(nèi)存中 可實(shí)際上它是這樣的: 1 2 3 4 5 6 7 8 9和一維數(shù)組沒(méi)有什么區(qū)別,都是一維線(xiàn)性排列。 當(dāng)我們像 為了更加通用化,假設(shè)數(shù)組定義是這樣的:
訪問(wèn): 那么被訪問(wèn)元素地址的計(jì)算方式就是: 這個(gè)就是二維數(shù)組在內(nèi)存中的本質(zhì),其實(shí)和一維數(shù)組是一樣的,只是語(yǔ)法糖包裝成一個(gè)二維的樣子。 六、神奇的 void 指針想必大家一定看到過(guò) void 的這些用法: 在這些情況下,void 表達(dá)的意思就是沒(méi)有返回值或者參數(shù)為空。 但是對(duì)于 void 型指針卻表示通用指針,可以用來(lái)存放任何數(shù)據(jù)類(lèi)型的引用。 下面的例子就 是一個(gè) void 指針: void *ptr;void 指針最大的用處就是在 C 語(yǔ)言中實(shí)現(xiàn)泛型編程,因?yàn)槿魏沃羔樁伎梢员毁x給 void 指針,void 指針也可以被轉(zhuǎn)換回原來(lái)的指針類(lèi)型, 并且這個(gè)過(guò)程指針實(shí)際所指向的地址并不會(huì)發(fā)生變化。 比如: 這兩次輸出的值都會(huì)是一樣: ![]() 平??赡芎苌贂?huì)這樣去轉(zhuǎn)換,但是當(dāng)你用 C 寫(xiě)大型軟件或者寫(xiě)一些通用庫(kù)的時(shí)候,一定離不開(kāi) void 指針,這是 C 泛型的基石,比如 std 庫(kù)里的 sort 函數(shù)申明是這樣的: void qsort(void *base,int nelem,int width,int (*fcmp)(const void *,const void *));所有關(guān)于具體元素類(lèi)型的地方全部用 void 代替。 void 還可以用來(lái)實(shí)現(xiàn) C 語(yǔ)言中的多態(tài),這是一個(gè)挺好玩的東西。 不過(guò)也有需要注意的:
比如: 為什么? 因?yàn)榻庖玫谋举|(zhì)就是編譯器根據(jù)指針?biāo)傅念?lèi)型,然后從指針?biāo)赶虻膬?nèi)存連續(xù)取 N 個(gè)字節(jié),然后將這 N 個(gè)字節(jié)按照指針的類(lèi)型去解釋。 比如 int *型指針,那么這里 N 就是 4,然后按照 int 的編碼方式去解釋數(shù)字。 但是 void,編譯器是不知道它到底指向的是 int、double、或者是一個(gè)結(jié)構(gòu)體,所以編譯器沒(méi)法對(duì) void 型指針解引用。 七、花式秀技很多同學(xué)認(rèn)為 C 就只能面向過(guò)程編程,實(shí)際上利用指針和結(jié)構(gòu)體,我們一樣可以在 C 中模擬出對(duì)象、繼承、多態(tài)等東西。 也可以利用 void 指針實(shí)現(xiàn)泛型編程,也就是 Java、C++ 中的模板。 大家如果對(duì) C 實(shí)現(xiàn)面向?qū)ο蟆⒛0?、繼承這些感興趣的話(huà),可以積極一點(diǎn),點(diǎn)贊,留言~ 呼聲高的話(huà),我就再寫(xiě)一篇。 實(shí)際上也是很有趣的東西,當(dāng)你知道了如何用 C 去實(shí)現(xiàn)這些東西,那你對(duì) C++ 中的對(duì)象、Java 中的對(duì)象也會(huì)理解得更加透徹。 比如為啥有 關(guān)于指針想寫(xiě)的內(nèi)容還有很多,這其實(shí)也只算是開(kāi)了個(gè)頭,限于篇幅,以后有機(jī)會(huì)補(bǔ)齊以下內(nèi)容:
分享C/C++技術(shù)文章 |
|
|
來(lái)自: 西北望msm66g9f > 《編程》