C++ 中的反向迭代器和负跨步迭代器,在开始之前使用一个作为哨兵

Reverse iterators and negative strided iterators in C++, using one before beginning as sentinel

Another way of looking at C++ reverse iterators 中,Raymond Chen 写道:

... a quirk of the C++ language: You are allowed to have a pointer "one past the end" of a collection, but you are not allowed to have a pointer "one before the beginning" of a collection.

我知道它可能意味着“未定义的行为”,这几乎是一个对话结束者。但是 我很好奇如果忽略这条规则,在现实系统中可能发生的最坏情况是什么。分段错误?指针算术溢出?不必要的分页?

请记住,也不应引用开头“之前”(如“结尾”)的指针,问题似乎是指针 试图 指向它。

我能想到的唯一可能出现问题的情况是内存位置“0”有效的系统。 但即便如此,如果是这样,就会出现更大的问题,(例如,nullptr 本身也会有问题,我想按照惯例环绕可能仍然有效。)

我不是在质疑 reverse_iterator 的执行情况,其中包含一个特殊代码。 我想到了这个问题,因为如果你有一个通用的“跨步”迭代器实现,将需要一个特殊的负步逻辑,并且有成本(在代码和运行时)。

多维数组自然会出现任意步幅,包括负步幅。 我有一个多维数组库,原则上我总是允许负步幅,但现在我意识到如果我完全允许它们并且我不想暴露未定义的行为,它们应该有一个特殊的代码(不同于正例) .

But I am curious what is the worst that can happen in a realistic system if one ignores this rule. A segmentation fault? an overflow of pointer arithmetic? unnecessary paginations?

浪费了space。

为了能够形成“之前”的地址,您需要 space 可用。例如,如果您有一个 1k 的结构,它必须从内存的开头至少 1k 处开始。在内存有限的小型设备或具有分段内存的 x86 上,这可能很棘手。

要形成“一个过去”pointer/iterator,您只需要在末尾之后多一个字节。因为无论如何都不能取消引用,所以不需要为整个对象设置 space,只需要构成地址的可能性即可。


来自评论:

在定义规则时,我们拥有带有专用地址寄存器的真实 CPU,可在寄存器加载时根据段大小验证地址。这确保了 -1 地址会陷入...

https://en.wikipedia.org/wiki/Motorola_68000#Architecture

这排除了“一个之前”环绕到片段末尾的情况。因为如果段很小,结果端可能不在那里。