我对以下示例有 2 个问题:1)
std::vector<int> v(5,1);
cout << *v.end();
打印结果是否未定义(取决于编译器)
2)
int x = 5,y = 6;
std::vector<int*> pv;
pv.push_back(&x);
pv.push_back(&y);
cout << *pv.end();
打印结果是未定义的(取决于编译器)还是 NULL
您在 处没有项目end()
,它是向量中最后一个有效项目之后的迭代器。
*v.end();
这是未定义的行为。您可以end()
用于比较迭代器是否指向最后一个项目之后的项目。
访问最后一项值的简单方法是back()
,例如:
cout << v.back();
end()
迭代器指向的位置是容器最后一个元素之后的一个元素。访问它指向的数据将调用未定义的行为,这在您的两个示例中都是如此。
如果我们查看 C++ 标准草案的迭代器要求草案,然后一般第5段说(强调我的),那么取消引用结束可能会很糟糕,但看起来它是实现定义的:24.21
24.2.1
正如指向数组的常规指针保证有一个指针值指向数组的最后一个元素,所以对于任何迭代器类型,都有一个迭代器值指向对应序列的最后一个元素。这些值称为过去值。定义了表达式i 的迭代器 i 的值称为可解引用。该库从不假定过去的值是可取消引用的。迭代器也可以具有不与任何序列相关联的奇异值。[ 示例:在声明未初始化的指针 x 之后(与 int x; 一样),必须始终假定 x 具有指针的奇异值。—结束示例]对于奇异值,大多数表达式的结果是未定义的;[...]可取消引用的值总是非单一的。
首先,在这两种情况下,行为都是未定义的。请注意,这不是未定义的“打印结果”。你的代码甚至没有机会打印任何东西。仅将*
运算符应用于结束迭代器已经导致未定义的行为。例如这个单独
*v.end();
已经是未定义的行为。
其次,在这种情况下,未定义并不意味着“取决于编译器”。实现定义的行为取决于编译器。未定义意味着“完全不可预测”,即使您使用的是相同的编译器。
PS 对于一些密切相关的问题,标准委员会似乎正在进行一些工作。
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#208
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1213
希望它将导致对过去的迭代器合法和不合法的规范更加清晰。但很明显,在一般情况下,过去的迭代器可以合法地成为单数迭代器,这意味着在一般情况下它可以是不可解引用的。
vector::end
- 返回迭代器结束(公共成员函数)
你可以在这里阅读更多。
你的第一个例子:
std::vector<int> v(5,1);
cout << *(v.end()-1);
它是未定义的(看图片),v.end()
指向最后一个元素之后的地址,如果容器为空,此函数返回与v.begin()
.
你的第二个例子:
int x = 5,y = 6;
std::vector<int*> pv;
pv.push_back(&x);
pv.push_back(&y);
cout << **(pv.end()-1);
是的,这两个都是未定义的。