Разрешены ли отрицательные индексы массива в 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<a[-1]
всегда будет false.