當前位置:編程學習大全網 - 編程語言 - 為什麽C語言檢查數組訪問越界會這麽難

為什麽C語言檢查數組訪問越界會這麽難

C語言中數組和內存管理,是安全性和性能之間矛盾關系的重要部分。

我曾提到要討論性能和安全性之間的矛盾。這個矛盾的壹個重要部分就是因為C語言中數組和內存管理的本質特征導致的。

理論上,數組是壹個簡單的數據結構:當妳需要訪問其中的壹個元素時,只需要給出該元素的索引位置,就能對該元素進行讀或者寫操作。這句話中也隱含了

壹個問題,那就是妳需要訪問壹個元素時,都需要提供壹個索引位置。使用索引位置來找元素通常是壹個代價很高的計算,尤其是當元素的大小不是2的整數次冪

時: 在諸如表達式++a[i], 在地址遞增的過程中,其計算地址的代價可以輕松超過5倍於a[i]的地址的代價。

在至少50年的時間裏,編譯器開發人員壹直在努力讓訪問數組元素變得更快。其中很大壹部分的工作都圍繞想下面這種循環進行:

for (size_t i = 0; i < n; ++i)

c[i] = a[i] + b[i];

這段代碼在每次循環叠代中,都需要通過計算將三個索引地址轉換成對應的內存位置中,這種計算也帶入了壹些開銷。 許多編譯器都通過將循環重寫為如下代碼的方式來實現高效計算。在這段代碼中,我們假設Pointer類型是可以指向a,b,c三個數組中某個元素的指針。

Pointer ap = &a[0], bp = &b[0], cp = &c[0], aend = ap + n;

while (ap < aend) {

*cp++ = *ap++ + *bp++;

}

這段轉換後的代碼將三個數組索引計算操作轉換成了三個地址加操作,這樣加速顯著。不過,這個簡單的轉換操作看起來容易,做起來卻很復雜,

因為編譯器需要能夠確認在這個for循環體中沒有對i值本身的修改。 上面的例子可以很直觀的看到i不會被改變,不過在實際的代碼中,往往要困難很多。

C語言與在它之前的編程語言相比,壹個非常重要的不同就是C能提供給程序員壹些直接優化代碼的機會,而不是簡單的依賴編譯器去做優化。C語言通過將數組和指針的概念統壹化,使得程序員可以自己做大部分的數組索引計算,而在C語言之前,這些工作只能通過編譯器去做。

用手動計算索引取代自動計算是壹種進步,這個聽起來有點怪怪的。但是在實際編程中,可能很多程序員都寧願手工優化代碼,而不是依賴編譯器自動優化,因為無法確定編譯器到底對代碼做了什麽。這也可能是吸引C程序員使用指針而不是索引來訪問數組元素的原因之壹。

除了在很多情況下會更快外,指針相比數組還有另外壹個很大的優勢:可以只用指向數組中特定元素的壹個指針來識別數組中的元素。比如,假設我們想寫壹

個函數來對數組中某個區域內的元素做操作。如果沒有指針,我們需要三個參數來確定這個區域:數組名稱,區域開始索引,區域結束索引。而如果使用指針的話,

只要兩個參數就足夠了。

此外,不管是動態分配的內存,還是其他內存地址,都可以統壹使用指針。例如,在malloc庫函數返回壹個指向動態內存的指針後,我們可以用這個指

針創建任何我們需要的數據結構。壹旦我們在這塊動態分配的內存中創建了這些數據結構之後,我們就能使用指向這些數據結構某個部分的指針來讓其他函數可以直

接訪問這壹部分數據。 相應的,這些函數也無需知道他們將使用的內存到底是什麽性質的。

使用指針是方便了很多,但是也要付出代價的。相比於使用索引變量引用數組元素的表示形式,使用指針的表示形式將會引入三種潛在危害。

第壹,因為指向數組元素的指針和數組本身是完全獨立的。

因此,在數組不存在或者內存釋放之後,指針仍然有可能存在。比如,我們將數組元素的地址

&a[0]保存到指針ap中,我們同時也引入了在a不存在的時候,使用*ap的風險。這種風險在完全使用數組加索引的形式中是不存在的,因為壹旦

數組a消失了,我們也無法引用他的元素。

第二,指針運算的可行性。

如果我們使用壹對指針指向壹個數組區間的兩端,那麽我們就壹定能找到其中間元素的位置,因為可以直接使用數學運算得到。但

是這種指針的數學運算也同時引入了很多制造不可用地址的可能性,而且這種通過數學運算得到的不可用地址, 相比簡單的壹些針對整數的數學運算來說,

更難檢測到。

最後,使用指針來表示範圍,不僅僅需要指針本身存在且可用,還需要指針指向的內存是可用的內存

。上面代碼中的aend變量就是壹個典型例子。我們創

建了壹個aend變量,並用它指向循環的上界。但是如果我們想試圖對*aend取值,結果將是未定義的。這類指針被稱為off-the-end指針。這類

指針的存在,也讓驗證C語言是否存在越界錯誤變得非常困難。

  • 上一篇:有誰知道,對通過電磁感應偷竊應該如何防範?
  • 下一篇:牛網的名字跟編程有什麽關系?
  • copyright 2024編程學習大全網