Разрешены ли отрицательные индексы массива в C?

Я просто читал некоторый код и обнаружил, что человек использовал arr[-2] для доступа к 2-му элементу перед arr, например:

|a|b|c|d|e|f|g|
       ^------------ arr[0]
         ^---------- arr[1]
   ^---------------- arr[-2]

Разрешено ли это?

Я знаю, что arr[x] совпадает с *(arr + x). Итак, arr[-2] - *(arr - 2), что кажется ОК. Как вы думаете?

Ответ 1

Это правильно. Из C99 §6.5.2.1/2:

Определение индекса оператор [] состоит в том, что E1 [E2] идентичен (* ((E1) + (E2))).

Там нет волшебства. Это эквивалентность 1-1. Как всегда при разыменовании указателя (*), вы должны быть уверены, что он указывает на действительный адрес.

Ответ 2

Это допустимо только в том случае, если arr является указателем, указывающим на второй элемент в массиве или более позднем элементе. В противном случае это неверно, потому что вы будете получать доступ к памяти за пределами массива. Так, например, это было бы неправильно:

int arr[10];

int x = arr[-2]; // invalid; out of range

Но это было бы нормально:

int arr[10];
int* p = &arr[2];

int x = p[-2]; // valid:  accesses arr[0]

Однако необычно использовать отрицательный индекс.

Ответ 3

Звучит хорошо для меня. Было бы редким случаем, что вам это было бы законно необходимо.

Ответ 4

Вероятно, что arr указывал на середину массива, поэтому arr[-2] указывал на что-то в исходном массиве, не выходя за пределы.

Ответ 5

Я не уверен, насколько это надежно, но я просто прочитал следующую оговорку об отрицательных индексах массива в 64-битных системах (предположим, LP64): http://www.devx.com/tips/Tip/41349

Автор, кажется, говорит, что 32-битные индексы массива с 64-разрядной адресацией могут приводить к плохим вычислениям адресов, если индекс массива явно не продвинут до 64 бит (например, с помощью ptrdiff_t cast). Я действительно видел ошибку его природы с версией PowerPC gcc 4.1.0, но я не знаю, является ли она ошибкой компилятора (т.е. Должна работать в соответствии со стандартом C99) или правильным поведением (например, для индекса требуется бросок до 64 бит для правильного поведения)?

Ответ 6

Я знаю, что на вопрос ответили, но я не мог не согласиться с этим объяснением.

Я помню Принципы проектирования компилятора, Пусть предположим, что a - массив int, а размер int равен 2, & Амп; Базовый адрес для a - 1000.

Как a[5] будет работать →

Base Address of your Array a + (index of array *size of(data type for array a))
Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Это объяснение также является причиной того, что отрицательные индексы в массивах работают в C.

то есть. если я получу доступ к a[-5], он даст мне

Base Address of your Array a + (index of array *size of(data type for array a))
Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

Он вернет мне объект в точке 990. По этой логике мы можем получить доступ к отрицательным индексам в массиве в C.

Ответ 7

О том, почему кто-то хочет использовать отрицательные индексы, я использовал их в двух контекстах:

  • Наличие таблицы комбинаторных чисел, которая сообщает вам гребенку [1] [- 1] = 0; вы всегда можете проверять индексы перед доступом к таблице, но таким образом код выглядит более чистым и выполняется быстрее.

  • Вставка сантинеля в начале таблицы. Например, вы хотите использовать что-то вроде

     while (x < a[i]) i--;
    

но тогда вы также должны проверить, что i положительный.
Решение: сделайте так, чтобы a[-1] был -DBLE_MAX, так что x&lt;a[-1] всегда будет false.