考虑下面的代码
void foo( bool forwad )
{
vector<MyObject>::iterator it, end_it;
int dir;
it = some_global_vector.begin() + some_position;
if( forward )
{
dir = 1;
it += 1;
end_it = some_global_vector.end();
}
else
{
dir = -1;
it -= 1;
end_it = some_global_vector.begin()-1;
}
while( it != end_it )
{
if( do_domething() )
break;
it += dir;
}
}
正如你所看到的那样,当forward == false时会有一些疑问,因为begin()和迭代器都有一个减法,当它指向begin()时它可以被减去.我找不到任何地方,如果它没有取消引用这个糟糕的指向迭代器).
编辑
我阅读了ISO C标准并得出了一些结论.
没有承诺vector :: begin()不能在地址0处内部指向内存,我认为它是结束,但所有容器都依赖于标准的alocator.该alocator依赖于新的运算符.而且,没有新的信息永远不会返回0.但标准的alocator也依赖于删除操作符,如果你传递0,这个操作符就是什么都不做.所以这个事实,新的不能返回0因为会有无法删除该指针,由此,非空向量无法返回指向0的begin().
结论:
如果上面正确递减指向vector :: begin()的interator应该是安全的,因为向量的内部存储器是连续的.
我对吗?
终极答案
即使它现在有效并将在未来工作,它仍然是根据标准未定义的行为.如果你这样做,你自己承担风险.有关更多信息,请参见此simmilar question.
解决方法:
你不能减少传递开始的迭代器,或者计算begin() – 1.
虽然实现需要有一个通过最后一个元素的位置,但在开始之前不需要任何可用的地址空间.所以begin() – 1可能不是一个有效的地址(绝对不是一个有效的迭代器).
关于问题2:
即使if(p == 0)测试指针是否为空,这并不意味着空指针必须由所有位零表示.它也可以是所有位1或其他东西.编译器魔术无论如何都会使测试工作.
无效地址的另一个示例是,当您释放大块内存时,堆管理器也可能还会从您的进程中删除相应的虚拟地址空间.
在解除分配的空间之后启动的另一个内存块可能会有一个地址,比如0x10000,其中地址0x10000-1不再存在.一些使用专用地址寄存器指针的硬件在加载无效指针时会被捕获.它只是可以检测到0x10000 – 1不再映射到RAM并中止您的程序.编写标准是为了允许这样,因为存在这样的硬件.
我们并未说这是普通桌面操作系统上通常会发生的情况,根据语言标准可能会发生什么.