EasyC++16,指針初探之二
大家好,我是梁唐。
想要追求更好閱讀體驗的同學,可以點擊文末的「閱讀原文」,訪問github倉庫。
危險的case
指針由于能夠操作內(nèi)存,所以如果使用的時候不夠仔細,很容易引發(fā)一些意想不到的錯誤。
C++ Primer當中給了這樣一個例子:
- int *ptr;
- *ptr = 2333;
在這段代碼當中我們聲明了一個int型的指針,并且將它指向了2333。然而,這里有一個問題,我們在聲明指針的時候并沒有進行初始化。沒有初始化的指針并不為空,而是指向一個未知的地方。如果說它指向的是一個常量1200的地址,我們讓它等于2333,那么之后當我們使用1200這個常量的時候,得到的結果都是2333。
更可怕的是,整個過程非常地隱蔽,很難察覺。debug的時候會令人抓狂。
所以千萬不要修改一個沒有初始化的指針指向的值。
指針和數(shù)字
C++ Primer當中還給了另外一個例子,當我們輸出指針的時候,得到的是一串十六進制的數(shù)。那我們能不能反過來將一個十六進制的數(shù)賦值給指針呢?
- int *p;
- p = 0xB8000000;
答案是不行,因為類型不一致。雖然我們打印指針的時候看起來得到是十六進制數(shù),但它的類型其實是指針類型,而不是整數(shù)類型。所以我們將一個整數(shù)賦值給一個指針是不行的,如果非要賦值,必須要進行類型轉換。
- int *p;
- p = (int*) 0xB8000000;
但是這一轉換之后顯然又出現(xiàn)了一個問題,我們知道0xB8000000這個地址指向哪里么?顯然不知道,自然也就說不清改了這里的值之后會引發(fā)什么結果。
所以雖然這么做可行,但也強烈不建議這樣干。
new操作
前文說過使用指針有一個非常大的好處就是可以在程序運行的時候,動態(tài)分配內(nèi)存。其實在C語言當中也有類似的功能,可以使用malloc來分配內(nèi)存。不過在C++當中有了更好用的運算符——new。
比如我們要動態(tài)創(chuàng)建一個int類型的變量,可以這樣寫:
- int *ptr = new int;
new運算符根據(jù)之后的類型確定需要的內(nèi)存大小,找到這樣的內(nèi)存之后,返回地址。剛好指針接收的值就是內(nèi)存地址,因此剛好可以完成這樣的賦值操作。
上面的代碼也可以寫成這樣:
- int a;
- int *ptr = &a;
這兩者有什么區(qū)別呢?表面上看沒有區(qū)別,都是創(chuàng)建了一個int類型的變量。只不過第二種寫法除了可以使用指針ptr之外,還可以使用變量名a來訪問這個int。
但實際上這兩者的內(nèi)部實現(xiàn)完全不同,我們直接通過變量名創(chuàng)建的變量它的值會被存儲在棧內(nèi)存當中,而通過new創(chuàng)建的對象則被存儲在堆內(nèi)存當中。棧內(nèi)存是由系統(tǒng)自動分配,而堆內(nèi)存則是由程序員進行申請使用。這兩者的內(nèi)存模型是完全不同的,我們會在之后的文章詳細地討論這點。目前簡單來理解的話,就是堆內(nèi)存更加靈活,它的空間也更大,可以存儲下更大的數(shù)據(jù)。
delete操作
有了動態(tài)創(chuàng)建,自然也就有動態(tài)刪除,所以C++當中有一個delete操作和new相對應。
delete運算符可以在變量使用結束之后,將內(nèi)存歸還給內(nèi)存池。因為很多時候程序當中的變量都是一次性使用或者是有生命周期的,當生命周期結束,使命完成就沒有必要繼續(xù)占用著資源了。畢竟系統(tǒng)內(nèi)的內(nèi)存資源是有限的,尤其是在一些大型項目或者嵌入式系統(tǒng)當中,內(nèi)存資源非常緊張。
delete運算符之后跟一個指針,它會釋放改指針指向的內(nèi)存。
- int *ptr = new int;
- delete ptr;
這里面有很多坑,千萬要當心。首先是使用了new創(chuàng)建了內(nèi)存之后,一定要記得delete,否則這塊內(nèi)存將會永遠被占用無法得到釋放,這種情況被稱為內(nèi)存泄漏(memory leak)。另外,我們不能delete一個已經(jīng)delete過的指針,這也會引發(fā)嚴重錯誤。C++ Primer對此的描述是:什么情況都可能發(fā)生。當然也不能再使用一個已經(jīng)被delete的指針,這會引發(fā)空指針錯誤。
指針對于C++來說是一把雙刃劍,像是Java、Python、Go等其他語言,內(nèi)存回收的工作都是由系統(tǒng)自動執(zhí)行的。例如Java的JVM虛擬機設計了嚴密的GC(垃圾回收)機制,程序員無須關心內(nèi)存的回收問題,全部交給程序自動完成。
而在C++當中,這一過程是由程序員手動執(zhí)行的,某種程度上來說,這當然非常好,程序員擁有了很高的權限以及靈活度。但同樣也是一個坑,尤其是在復雜系統(tǒng)當中,很難準確判斷delete執(zhí)行的時間。這會引發(fā)嚴重的問題,例如內(nèi)存泄漏嚴重,野指針到處飛等……