2010-04-02 64 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