Подтвердить что ты не робот

Действительно ли отрицательный индекс для оператора [] определен?

Я знаю, что это будет очень плохой стиль кодирования, но следующий код отлично работает на моей машине. Но хорошо ли это поведение? Портативный?

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

Ответ 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;