C++: Vector bounds
C++: Vector bounds
我来自Java,目前正在学习c++。我正在使用Stroustrup的编程原理和使用c++的实践。我现在正在研究向量。在第117页,他说访问vector中不存在的元素会导致运行时错误(在Java中也是如此,索引越界)。我正在使用MinGW编译器,当我编译并运行此代码时:
#include <iostream>
#include <cstdio>
#include <vector>
int main()
{
std::vector<int> v(6);
v[8] = 10;
std::cout << v[8];
return 0;
}
输出10。更有趣的是,如果我不修改不存在的vector元素(我只是打印它,预计会出现运行时错误或至少是默认值),它会打印一些大整数。所以…是Stroustrup错了,还是GCC有一些奇怪的编译c++的方式?
这本书有点模糊。与其说是"运行时错误",不如说是未定义行为在运行时表现出来。这意味着任何事情都可能发生。但是,错误严格地与有关,而与程序执行无关,实际上,甚至谈论具有未定义行为的程序的执行都是不可能的,也是不明智的。
c++中没有任何东西可以保护你避免编程错误,这与Java很不一样。
正如@sftrabbit所说,std::vector
有一个替代接口.at()
,它总是给出一个正确的程序(尽管它可能抛出异常),因此是一个可以推理的程序。
让我用一个例子来重复这一点,因为我相信这是c++的一个重要的基本方面。假设我们正在从用户读取一个整数:
int read_int()
{
std::cout << "Please enter a number: ";
int n;
return (std::cin >> n) ? n : 18;
}
现在考虑以下三个程序:
危险的:这个程序的正确性取决于用户的输入!它不一定是不正确的,但它是不安全的(到了我称之为坏的地步)。
int main()
{
int n = read_int();
int k = read_int();
std::vector<int> v(n);
return v[k];
}
无条件正确:无论用户输入什么,我们都知道程序的行为。
int main() try
{
int n = read_int();
int k = read_int();
std::vector<int> v(n);
return v.at(k);
}
catch (...)
{
return 0;
}
相同的:上面的.at()
版本是尴尬的。最好是检查并提供反馈。因为执行了动态检查,所以实际上可以保证未检查的vector访问是正确的。
int main()
{
int n = read_int();
if (n <= 0) { std::cout << "Bad container size!n"; return 0; }
int k = read_int();
if (k < 0 || k >= n) { std::cout << "Bad index!n"; return 0; }
std::vector<int> v(n);
return v[k];
}
(我们忽略了vector构造可能抛出自身异常的可能性)
寓意是c++中的许多操作都是不安全的,并且只有在条件下才正确,但是程序员应该提前进行必要的检查。语言不会为你做这些,所以你不用为它付钱,但你必须记得去做。其思想是,无论如何您都需要处理错误条件,因此,与其在库或语言级别强制执行昂贵的非特定操作,不如将责任留给程序员,他们处于更好的位置,可以将检查集成到需要编写的代码中。
如果我想开玩笑,我会将这种方法与Python进行对比,Python允许您编写令人难以置信的短和正确的程序,而根本不需要任何用户编写的错误处理。另一方面,任何使用这种程序的尝试,只要与程序员的意图有轻微的偏差,就会给您留下一个非特定的、难以阅读的异常和堆栈跟踪,并且几乎没有指导您应该做得更好。您不会被迫编写任何错误处理,而且通常最终不会编写任何错误处理。(我不能将c++与Java进行比较,因为Java通常是安全的,但我还没有看到一个短的 Java程序)
这是@Evgeny Sergeev的一个有价值的评论,我将其推广到答案:
对于GCC,您可以-D_GLIBCXX_DEBUG以安全实现替换标准容器。最近,这似乎也适用于std::array。更多信息在这里:gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html
我想补充一点,也可以通过使用gnu_debug::命名空间前缀而不是std::.
来捆绑单独的"安全"版本的vector和其他实用程序类。换句话说,不要重新发明轮子,数组检查至少在GCC中是可用的。
C和c++并不总是做边界检查。它可能导致运行时错误。如果你把你的数字过度,比如10000左右,几乎肯定会引起问题。
你也可以使用vector.at(10),这肯定会给你一个异常。看到的:http://www.cplusplus.com/reference/vector/vector/at/相比:http://www.cplusplus.com/reference/vector/vector/operator%5B%5D/
我希望vector的"operator[]"会像"at()"那样检查边界,因为我不太小心。: -)
一种方法是继承vector类并覆盖操作符[]来调用at(),这样就可以使用更具可读性的"[]",而不需要将所有的"[]"替换为"at()"。还可以将继承的向量(例如:safer_vector)定义为法向量。代码将是这样的(在c++ 11, Xcode 5的llvm3.5中)。
#include <vector>
using namespace std;
template <class _Tp, class _Allocator = allocator<_Tp> >
class safer_vector:public vector<_Tp, _Allocator>{
private:
typedef __vector_base<_Tp, _Allocator> __base;
public:
typedef _Tp value_type;
typedef _Allocator allocator_type;
typedef typename __base::reference reference;
typedef typename __base::const_reference const_reference;
typedef typename __base::size_type size_type;
public:
reference operator[](size_type __n){
return this->at(__n);
};
safer_vector(_Tp val):vector<_Tp, _Allocator>(val){;};
safer_vector(_Tp val, const_reference __x):vector<_Tp, _Allocator>(val,__x){;};
safer_vector(initializer_list<value_type> __il):vector<_Tp, _Allocator>(__il){;}
template <class _Iterator>
safer_vector(_Iterator __first, _Iterator __last):vector<_Tp,_Allocator>(__first, __last){;};
// If C++11 Constructor inheritence is supported
// using vector<_Tp, _Allocator>::vector;
};
#define safer_vector vector
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 在c++中用vector填充一个简单的动态数组
- vector.resize()中的分配错误
- 使用std::vector的OpenCL矩阵乘法
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 在某些循环内使用vector.push_back时出现分段错误
- 当vector是tje全局变量时,c++中vector的内存管理
- std::vector的包装器,使数组的结构看起来像结构的数组
- 为什么(-1)%vector::size()总是返回0
- 在C++中将类(带有Vector成员)保存为二进制文件
- 编译器如何区分std::vector的构造函数
- 将 int 数组转换为 std::vector<int*>
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 在std::vector上存储带有模板的类实例
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 为什么std::vector比数组慢
- std::vector::迭代器是否可以合法地作为指针
- 如何将二进制格式的 C++ 对象的 std::vector 保存到磁盘?
- 循环中的条件:为什么每次都调用strlen(),而vector.size()只调用一次
- C++: Vector bounds