运营商的负指数是否定义良好?

我知道这将是非常糟糕的编码风格,但下面的代码在我的机器上完美运行。 但是,行为是否明确? 便携式?

int main() { int *p = new int[3]; int *q = &p[2]; q[-1] = 41; std::cout << p[1]; delete[] p; } 

这在句法和语义上都有明确的定义。

[expr.sub] / 1(N3337):

expression式E1[E2]*((E1)+(E2))相同(根据定义*((E1)+(E2))

所以你的expression与*(q-1) = 41; ,所以在句法上是有效的。

[expr.add] / 5(N3337)

当具有整数types的expression式被添加到指针或从指针中减去时,结果具有指针操作数的types。 如果指针操作数指向数组对象的一个​​元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果数组元素和原始数组元素的下标之差等于整数expression式。

由于q指向您的整数expression式的有效大小的数组对象的元素,它在语义上是有效的。

是的,它是明确的。 内置operator[]是根据指针运算来定义的。 这个:

 p[N] 

其中p是一个指针, N是一个整数,相当于这个:

 *(p + N) 

这个有趣的结果是:

 N[p] 

也是等价的,因为加法是可交换的。

根据C ++标准(5.2.1下标)

1后缀expression式后跟方括号中的expression式是后缀expression式。 其中一个expression式的types应该是“T的数组”或者“指向T的指针”,另一个expression式应该是非范型的枚举或者整型 。 结果是“T”型。“T”型应该是一个完全定义的对象types。

因此,只要expression式*((E1)+(E2))的结果是正确的,就可以使用任何整数types,包括inttypes和相应的负值。

考虑到对于用户定义的types,你可以使用一个brace-init-list作为索引。 例如

 #include <iostream> class Point { public: Point( int x, int y ) : x( x ), y( y ) {} int x, y; }; class Circle { public: Circle( unsigned int r ) : r( r ) {} Circle & operator []( Point p ) { std::cout << "Drawing a circle at ( " << px << ", " << py << " )\n"; return *this; } unsigned int r; }; int main() { Circle circle( 10 ); circle[ { 0, 0 } ]; } 

程序输出是

 Drawing a circle at ( 0, 0 ) 

索引运算符x[idx]等于(*(x +idx)) ,是的, idx可能是负数。 但是,您必须确保解除引用的指针指向有效的内存地址。

请注意,我们可以用许多方式重写它(就像代数一样)。

 x[idx] = (*(x +idx)) = (*(idx + x)) = idx[x] 

只要你不试图在p指向的数组的边界之外解引用一个指针,这是绝对没问题的。

此外,您可以将指针q设置为数组的任何元素,另外还可以将其指定给数组中的一个元素。 (不要试图通过座位的尽头解除引用元素。)

不要忘记delete[] p; 在你的函数结束。

它非常安全和便携。 您只是使用指针算术来寻址由new运算符分配的内存。

您的代码相当于:

 int* p = new int[3]; int* q = p + 2; *(q-1) = 41;