2010-04-02 99 views
3

我是GCC的忠實粉絲,但最近我發現了一個模糊的異常。使用__gnu_cxx :: __ normal_iterator(即libstdC++中使用的最常用的迭代器類型,即C++ STL),可以引用任意內存位置,甚至可以更改其值,而不會導致異常!這是預期的行爲?如果是這樣,是不是安全漏洞?使用C++迭代器引用無效的內存位置

下面是一個例子:

#include <iostream> 
using namespace std; 

int main() { 
     basic_string<char> str("Hello world!"); 
     basic_string<char>::iterator iter = str.end(); 



     iter += str.capacity() + 99999; 
     *iter = 'x'; 

     cout << "Value: " << *iter << endl; 
} 

回答

6

提領的迭代器超出從中獲得它的容器的到底是不確定的行爲,而什麼都不做只是存在可能性。

請注意,這是一個折中的問題,迭代器檢查開發的有效性是很好的,但是會爲代碼增加額外的操作。在MSVS迭代器中默認檢查(他們將驗證它們是有效的,並且在以錯誤的方式使用時會失敗,但這也會影響運行時的性能。)

Dinkumware(STL裏面的VS )提供(默認選中,可以通過編譯器選項取消選中)實際上是一個不錯的選擇,用戶可以選擇是否需要緩慢安全的迭代器或快速的不安全版本,但從語言的角度來看,兩者都是有效的。

+0

@David:感謝您的解釋;這是精確的,正是我所期待的。我目前正在改進我的代碼庫。我知道'g ++'分配的任何字符串總是以'EOF','WEOF'或者至少是'CharType()'結尾(即分配內存的最後一個單元被分配給它)。那麼你認爲在這種情況下使用調試斷言是個好主意嗎?你有什麼其他的建議? – themoondothshine 2010-04-02 16:25:39

+0

你是指字符串文字還是'std :: string'實例?字符串文字「asdf」總是以「(char)0」結尾。使用'std :: string'不能保證任何給定點處的內存內容。請注意,作爲一個例子,在g ++ 4.4和空字符串中沒有分配內存,所以肯定不是EOF/WEOF/0終止。 – 2010-04-02 17:12:49

+0

我指的是'std :: string'。是的,你對g ++ 44空字符串是正確的:它持有一個指向(靜態)共享空空指針的指針。但是,假設如果我在非空std :: string上使用迭代器,我可以假設它始終以EOF/WEOF/0結束?g ++ 44跟隨'traits_type :: assign(this - > _ M_refdata()[__ n],_S_terminal)的stirng分配;''不是嗎? – themoondothshine 2010-04-02 17:30:49

1

C++一般也沒有讓你付出你不使用什麼的哲學。您需要驗證您是否正確使用了迭代器。對於一個隨機訪問迭代器,你隨時可以測試它:

if (iter < str.begin() || iter >= str.end()) 
    throw something; 
0

你很幸運,還是不幸。使用您的具體例子,我segfaulted。

$ ./a.exe 
    11754 [main] a 4992 _cygtls::handle_exceptions: Error while dumping state (probably corrupted stack) 
Segmentation fault (core dumped) 

未定義行爲在不同的編譯,平臺,日子裏可能意味着不同的事情。也許當你運行它時,所有這些添加創建的地址都會在一些其他有效的內存空間中結束,這只是偶然。例如,也許你從堆棧增加到堆。

+0

嗯...有趣。我大家都確信它不會拋出段錯誤:每次我運行它,它都可以工作! – themoondothshine 2010-04-02 16:16:38

+0

「拋出段錯誤」 - 只是想澄清一些事情。 segfault並不是一個「拋出」的異常,因爲某些軟件邏輯檢測到溢出溢出。可能在Java/C#中以這種方式工作,但不能在C++中工作。導致段錯誤的原因是內存管理器檢測到訪問衝突(讀/寫內存不允許)。因此,當你運行程序時,無論是碰巧創建一個指向你的程序的有效內存位置的指針,它不受MMU保護,或者MMU根本沒有做任何檢查(在運行所有東西的嵌入式系統中很常見在內核空間中)。 – Dan 2010-04-02 21:49:09

2

不,這不是問題。請記住,典型的迭代器的用法是:

 
for (type::const_iterator it = obj.begin(); it != obj.end(); ++it){ 
    // Refer to element using (*it) 
} 

正確的迭代器的使用需要一個要檢查的end()迭代器。使用隨機訪問迭代器(如您正在使用的迭代器),您還可以使用<和>以及針對end()的迭代器。 C和C++通常不像在Java中那樣進行邊界檢查,並且它是確保您這樣做的地方。

+1

+1。這與C++不會邊界檢查指針訪問的事實一樣多。也就是說,這是一個大問題,但它是程序員的問題,而不是實現問題。當我們犯錯的時候,這個實現就可以指向並笑。 – 2010-04-02 17:11:58

+0

在C和C++中,標準庫的實現對程序員的無知漠不關心。 「在腳下射擊自己,同時吹動你的整條腿,保證!」 – bitek 2013-01-25 08:47:31