c – 指向开始的减法或减量随机访问迭代器

考虑下面的代码

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并中止您的程序.编写标准是为了允许这样,因为存在这样的硬件.

我们并未说这是普通桌面操作系统上通常会发生的情况,根据语言标准可能会发生什么.

上一篇:Azure Service Bus(2)


下一篇:第二十二节:再探DbContext生命周期、配置、和几种初始化方式