C迷途指針詳解
本文較為詳盡的講述了C語(yǔ)言的迷途指針,分析了其概念、原理與檢測(cè)方法。分享給大家供大家參考。具體如下:
一般來(lái)說(shuō),在計(jì)算機(jī)編程領(lǐng)域中,迷途指針,或稱懸空指針、野指針,指的是不指向任何合法的對(duì)象的指針。
當(dāng)所指向的對(duì)象被釋放或者收回,但是對(duì)該指針沒(méi)有作任何的修改,以至于該指針仍舊指向已經(jīng)回收的內(nèi)存地址,此情況下該指針便稱迷途指針。若操作系統(tǒng)將這部分已經(jīng)釋放的內(nèi)存重新分配給另外一個(gè)進(jìn)程,而原來(lái)的程序重新引用現(xiàn)在的迷途指針,則將產(chǎn)生無(wú)法預(yù)料的后果。因?yàn)榇藭r(shí)迷途指針?biāo)赶虻膬?nèi)存現(xiàn)在包含的已經(jīng)完全是不同的數(shù)據(jù)。通常來(lái)說(shuō),若原來(lái)的程序繼續(xù)往迷途指針?biāo)赶虻膬?nèi)存地址寫(xiě)入數(shù)據(jù),這些和原來(lái)程序不相關(guān)的數(shù)據(jù)將被損壞,進(jìn)而導(dǎo)致不可預(yù)料的程序錯(cuò)誤。這種類型的程序錯(cuò)誤,不容易找到問(wèn)題的原因,通常會(huì)導(dǎo)致段錯(cuò)誤(Linux系統(tǒng)中)和一般保護(hù)錯(cuò)誤(Windows系統(tǒng)中)。如果操作系統(tǒng)的內(nèi)存分配器將已經(jīng)被覆蓋的數(shù)據(jù)區(qū)域再分配,就可能會(huì)影響系統(tǒng)的穩(wěn)定性。
某些編程語(yǔ)言允許未初始化的指針的存在,而這類指針即為野指針。野指針?biāo)鶎?dǎo)致的錯(cuò)誤和迷途指針?lè)浅O嗨?,但野指針的?wèn)題更容易被發(fā)現(xiàn)。
迷途指針的成因
在很多編程語(yǔ)言中(如C語(yǔ)言)從內(nèi)存中刪除一個(gè)對(duì)象或者返回時(shí)刪除棧幀后,并不會(huì)改變相關(guān)的指針的值。該指針仍然指向原來(lái)的內(nèi)存地址,即使引用已經(jīng)刪除,現(xiàn)在也可能已經(jīng)被其它進(jìn)程使用了。
一個(gè)直接的例子,如下所示:
{
  char *cp = NULL;
  /* ... */
  {
    char c;
    cp = &c;
  } /* c falls out of scope */     
   /* cp is now a dangling pointer */
}
上述問(wèn)題的解決方法是在該部分程序退出之前立即給CP賦0值(NULL)。另一個(gè)辦法是保證CP在沒(méi)有初始化之前,將不再被使用。
迷途指針經(jīng)常出現(xiàn)在混雜使用malloc() 和 free() 庫(kù)調(diào)用: 當(dāng)指針指向的內(nèi)存釋放了,這時(shí)該指針就是迷途的。和前面的例子一樣,一個(gè)避免這個(gè)錯(cuò)誤的方法是在釋放它的引用后將該指針的值重置為NULL,如下所示:
#include <stdlib.h>
{
  char *cp = malloc ( A_CONST );
  /* ... */
  free ( cp );   /* cp 現(xiàn)在變成了一個(gè)懸空指針 */
  cp = NULL;    /* cp 現(xiàn)在不是懸空了 */
  /* ... */
}
有個(gè)常見(jiàn)的錯(cuò)誤是當(dāng)返回一個(gè)基于棧分配的局部變量的地址時(shí),一旦調(diào)用的函數(shù)返回,分配給這些變量的空間將被回收,此時(shí)它們擁有的是"垃圾值"。
int * func ( void )
{
  int num = 1234;
  /* ... */
  return #
}
在調(diào)用func之后一段時(shí)間,嘗試從該指針中讀取num的值,可能仍然能夠返回正確的值(1234),但是任何接下來(lái)的函數(shù)調(diào)用會(huì)覆蓋原來(lái)的棧為num分配的空間。這時(shí),再?gòu)脑撝羔樧x取num的值就不正確了。如果要使一個(gè)指向num的指針都返回正確的num值,則需要將該變量聲明為static。
野指針的產(chǎn)生
野指針指的是還沒(méi)有初始化的指針。嚴(yán)格地說(shuō),編程語(yǔ)言中每個(gè)指針在初始化前都是野指針。
一般于未初始化時(shí)便使用指針就會(huì)產(chǎn)生問(wèn)題。大多數(shù)的編譯器都能檢測(cè)到這一問(wèn)題并警告用戶。
int f(int i)
{
  char* cp;  //cp 是野指針
  static char* scp; //scp 不是野指針,靜態(tài)變量自動(dòng)初始化為0并保留它們的值
//使用這種特征可能被認(rèn)為壞的編程風(fēng)格
}
迷途指針導(dǎo)致的安全漏洞
如同緩存溢出錯(cuò)誤,迷途指針/野指針這類錯(cuò)誤經(jīng)常會(huì)導(dǎo)致安全漏洞。 例如,如果一個(gè)指針用來(lái)調(diào)用一個(gè)虛函數(shù),由于vtable指針被覆蓋了,因此可能會(huì)訪問(wèn)一個(gè)不同的地址(指向被利用的代碼)?;蛘?,如果該指針用來(lái)寫(xiě)入內(nèi)存,其它的數(shù)據(jù)結(jié)構(gòu)就有可能損壞了。一旦該指針成為迷途指針,即使這段內(nèi)存是只讀的,仍然會(huì)導(dǎo)致信息的泄露(如果感興趣的數(shù)據(jù)放在下一個(gè)數(shù)據(jù)結(jié)構(gòu)里面,恰好分配在這段內(nèi)存之中)或者訪問(wèn)權(quán)限的增加(如果現(xiàn)在不可使用的內(nèi)存恰恰被用來(lái)安全檢測(cè)).
避免迷途指針的錯(cuò)誤
避免迷途指針,有一種受歡迎的方法——即使用智能指針(Smart pointer)。智能指針使用引用計(jì)數(shù)來(lái)回收對(duì)象。一些其它的技術(shù)包括tombstone法和locks-and-keys法。
另外,可以使用 DieHard 內(nèi)存分配器,它虛擬消除了類似其它內(nèi)存錯(cuò)誤(不合法或者兩次釋放內(nèi)存)的迷途指針錯(cuò)誤。
還有一種辦法是貝姆垃圾收集器,一種保守的垃圾回收方法,能夠替代C和C++中標(biāo)準(zhǔn)內(nèi)存分配函數(shù)。這種方法完全消除了迷途指針的錯(cuò)誤,通過(guò)去除內(nèi)存釋放的函數(shù)代之以垃圾回收器完成對(duì)象的回收。
像Java語(yǔ)言,迷途指針這樣的錯(cuò)誤是不會(huì)發(fā)生的,因?yàn)镴ava中沒(méi)有明確地重新分配內(nèi)存的機(jī)制。而且垃圾回收器只會(huì)在對(duì)象的引用數(shù)為零時(shí)重新分配內(nèi)存。
迷途指針的檢測(cè)
為了能發(fā)現(xiàn)迷途指針,一種普遍的編程技術(shù)——一旦指針指向的內(nèi)存空間被釋放,就立即把該指針置為空指針或者為一個(gè)非法的地址。當(dāng)空指針被重新引用時(shí),此時(shí)程序?qū)?huì)立即停止,這將避免數(shù)據(jù)損壞或者某些無(wú)法預(yù)料的后果。這將使接下來(lái)的編程過(guò)程產(chǎn)生的錯(cuò)誤變得容易發(fā)現(xiàn)和解決了。這種技術(shù)在該指針有多個(gè)復(fù)制時(shí)就無(wú)法起到應(yīng)有的作用了。
一些調(diào)試器會(huì)自動(dòng)地用特定的模式來(lái)覆蓋已經(jīng)釋放的數(shù)據(jù),如0xDEADBEEF (Microsoft's Visual C/C++ 調(diào)試器,例如,根據(jù)哪種類型被釋放采用 0xCC,0xCD 或者 0xDD)。這種方法通過(guò)將數(shù)據(jù)無(wú)用化,來(lái)防止已經(jīng)釋放的數(shù)據(jù)重新被使用。這種方法的作用是非常顯著的 (該模式可以幫助程序來(lái)區(qū)分哪些內(nèi)存是剛剛釋放的)。
某些工具,如Valgrind, Mudflap或者 LLVM可以用來(lái)檢測(cè)迷途指針的使用。
總的來(lái)說(shuō),迷途指針對(duì)C程序安全性影響巨大,是C程序員必須謹(jǐn)慎處理的一個(gè)問(wèn)題。相信本文所述對(duì)大家C程序設(shè)計(jì)有一定的借鑒價(jià)值。
上一篇:C++編譯器無(wú)法捕捉到的8種錯(cuò)誤實(shí)例分析
欄 目:C語(yǔ)言
下一篇:提高C++程序運(yùn)行效率的10個(gè)簡(jiǎn)單方法
本文標(biāo)題:C迷途指針詳解
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/3414.html
您可能感興趣的文章
- 01-10求子數(shù)組最大和的解決方法詳解
 - 01-10深入二叉樹(shù)兩個(gè)結(jié)點(diǎn)的最低共同父結(jié)點(diǎn)的詳解
 - 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)- 解析最少換車(chē)次數(shù)的問(wèn)題詳解
 - 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)-用棧實(shí)現(xiàn)表達(dá)式求值的方法詳解
 - 01-10HDOJ 1443 約瑟夫環(huán)的最新應(yīng)用分析詳解
 - 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
 - 01-10如何查看進(jìn)程實(shí)際的內(nèi)存占用情況詳解
 - 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
 - 01-10APUE筆記之:進(jìn)程環(huán)境詳解
 - 01-10深入第K大數(shù)問(wèn)題以及算法概要的詳解
 


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
 - 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
 - 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
 - 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
 - 5c語(yǔ)言計(jì)算三角形面積代碼
 - 6什么是 WSH(腳本宿主)的詳細(xì)解釋
 - 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
 - 8正則表達(dá)式匹配各種特殊字符
 - 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
 - 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
 
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
 - 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
 - 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
 - 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
 - 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
 - 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
 - 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
 - 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
 - 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
 - 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
 
隨機(jī)閱讀
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
 - 04-02jquery與jsp,用jquery
 - 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
 - 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
 - 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
 - 01-10delphi制作wav文件的方法
 - 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
 - 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
 - 01-10C#中split用法實(shí)例總結(jié)
 - 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
 


