5

在过去十年左右的时间里,C 和 C++ 程序员因经常未能执行正确的边界检查而受到打击,尤其是在字符串上。这些故障常常导致主要软件产品出现严重的安全问题。由于缓冲区溢出的不安全性已为人们所熟知,建立适当边界检查的驱动力已将许多程序员推离了传统的缓冲区和字符串操作函数strcpy()sprintf()至少部分是因为这些函数倾向于通过使关于目标缓冲区大小的假设。std::stringSTL 类型的优点之一std::vector是它们对缓冲区访问的强大控制。

但有一件事让我很困惑。C++ 头文件中许多最广泛使用的函数<algorithms>似乎都在积极地乞求溢出滥用:特别是那些接受begin迭代器(尤其是 InputIterator)而没有匹配end迭代器的函数。例如:

template <class InputIterator, class OutputIterator>
  OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result);

template <class InputIterator, class OutputIterator, class UnaryOperation>
  OutputIterator transform (InputIterator first1, InputIterator last1,
                            OutputIterator result, UnaryOperation op);

template <class ForwardIterator1, class ForwardIterator2>
   bool is_permutation (ForwardIterator1 first1, ForwardIterator1 last1,
                        ForwardIterator2 first2);

最后一个例子——<code>is_permutation() 尤其具有指导意义。copy()并且transform()很好理解,因此 C++ 程序员应该知道在调用这些函数之前手动检查输出容器的边界,或者使用类似 a 的东西back_inserter来确保输出容器根据需要增长。因此,可以这样说,尽管copy()并且transform()可以被滥用,但任何东西都可以,并且程序员很容易接受有关此类功能的最佳实践的教育。

is_permutation()是一个更棘手的案例。看看上面的函数声明,你会假设第二个范围(以 开头的那个first2)的大小是多少?第二个范围是否需要与第一个范围相同,或者不更小,或者不更大?我敢打赌,这些问题的简单答案不会浮现在您的脑海中。对于大多数程序员来说,“排列”的概念不如复制的概念那么舒服和熟悉。因此,似乎相对容易is_permutation()出错并以一种或另一种方式溢出缓冲区。

“查一下!” 我听你说。是的,当然。但是,如果程序员记住他们应该记住的所有内容并查看其他所有内容,那么我们就不会有错误和安全漏洞,对吗?

那么,为什么不is_permutation()和类似的函数(即函数获取所有输入迭代器但不是每个范围的完整开始-结束迭代器对)需要所有输入范围的完整开始-结束对?(请注意lexicographical_compare(),例如,确实满足此要求。)像这样的函数is_permutation()实际上并不像我想象的那样不安全吗?

4

3 回答 3

9

大多数语言本质上是不安全的,正确使用它取决于程序员。程序员在调用函数之前必须知道所使用的参数是否正确。

此外,在某些情况下,copy它允许在开放范围上使用前向迭代器。例如:

std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout," "));

没有对应的迭代器来标记流的结束,而且流真的没有结束,你可以不断的往里面添加。

于 2013-07-10T23:09:15.267 回答
7

在 C++14 中,有四个迭代器版本equal,is_permutationmismatch准确地解决了这一点。

于 2013-07-11T01:26:30.377 回答
1

我不确定在第二个范围内引入最后一个迭代器is_permutation会使函数变得不那么笨拙。我认为这会让事情变得更加混乱。

排列的问题是语义在名称本身中。要检查一个序列是否是另一个序列的排列,您希望没有最后一个迭代器的序列至少与第一个序列一样长。

如果不是这种情况,那么你不需要打电话is_permutation,因为它根本不可能是一个排列。如果它更长,您希望它不会迭代超过第一个序列的长度 - 为什么会这样?好吧,它没有——这就是你所期望的,所以没有失去信心。

C++ 确实希望程序员采取基本的预防措施,并让我们在许多情况下负责边界检查。如果不将这种控制权交给程序员,语言的力量就会减弱。如果我打电话,is_permutation那么我知道我的第二个迭代器不会溢出,因为我知道排列是什么。我当然不想浪费循环进行毫无意义的边界检查。

我认为那句老话很适用:权力越大,责任越大。这很公平,不是吗?

于 2013-07-10T23:22:15.457 回答