Действительно ли отрицательный индекс для оператора [] определен?
Я знаю, что это будет очень плохой стиль кодирования, но следующий код отлично работает на моей машине. Но хорошо ли это поведение? Портативный?
int main()
{
int *p = new int[3];
int *q = &p[2];
q[-1] = 41;
std::cout << p[1];
delete[] p;
}
Ответы
Ответ 1
Это хорошо определено, как синтаксически, так и семантически.
[expr.sub]/1 (N3337):
Выражение E1[E2]
идентично (по определению) до *((E1)+(E2))
.
Итак, ваше выражение такое же, как *(q-1) = 41;
, поэтому синтаксически корректно.
[expr.add]/5 (N3337)
Когда выражение, которое имеет интегральный тип, добавляется или вычитается из указателя, результат имеет тип операнда указателя. Если операнд указателя указывает на элемент объекта массива, и массив достаточно велик, результат указывает на смещение элемента от исходного элемента, так что разность индексов результирующих и исходных элементов массива равна интегральному выражению.
Так как q
указывает на элемент объекта массива допустимого размера для вашего интегрального выражения, он семантически корректен.
Ответ 2
Да, это четко определено. Встроенный operator[]
определяется в терминах арифметики указателя. Это:
p[N]
где p
- указатель, а N
- целое число, эквивалентно этому:
*(p + N)
Интересным итогом этого является то, что это:
N[p]
также эквивалентно, так как сложение коммутативно.
Ответ 3
В соответствии со стандартом С++ (подзаголовок 5.2.1)
1 Постфиксное выражение, за которым следует выражение в квадратных скобках, является постфиксное выражение. Одно из выражений должно иметь тип "массив T" или "указатель на T", а другой должен иметь незапечатанную перечисление или интегральный тип. Результат имеет тип "T". Тип "Т" должен быть полностью определенным типом объекта .65 Выражение E1 [E2] (по определению) совпадает с * ((E1) + (E2))...
Таким образом, вы можете использовать любой интегральный тип, включая тип int
и соответственно отрицательные значения при условии, что результат выражения *((E1)+(E2))
корректно сформирован.
Учтите, что для пользовательских типов вы можете использовать в качестве индекса список команд-скобок. Например
#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 ( " << p.x << ", " << p.y << " )\n";
return *this;
}
unsigned int r;
};
int main()
{
Circle circle( 10 );
circle[ { 0, 0 } ];
}
Выход программы
Drawing a circle at ( 0, 0 )
Ответ 4
Оператор индекса x[idx]
равен (*(x +idx))
и да idx
может быть отрицательным. Однако вы должны убедиться, что разыменованный указатель указывает на действительный адрес памяти.
Обратите внимание, что мы можем переписать его разными способами (подобно алгебре).
x[idx] = (*(x +idx)) = (*(idx + x)) = idx[x]
Ответ 5
Это абсолютно нормально, если вы не пытаетесь разыменовать указатель за пределами массива, на который указывает p
.
Кроме того, вы можете установить указатель q на любой элемент массива и, кроме того, на один элемент за массивом. (Не пытайтесь разыграть элемент на конце сиденья, хотя.)
Не забывайте delete[] p;
в конце вашей функции.
Ответ 6
Это совершенно безопасно и портативно. Вы просто используете арифметику указателя для адресации памяти, выделенной оператором new
.
Ваш код эквивалентен:
int* p = new int[3];
int* q = p + 2;
*(q-1) = 41;