Я наткнулся на странный опыт программирования на Си. Рассмотрим этот код:
int main(){
int array1[6] = {0, 1, 2, 3, 4, 5};
int array2[6] = {6, 7, 8, 9, 10, 11};
printf("%d\n", array1[-1]);
return 0;
}
Когда я компилирую и запускаю это, я не получаю никаких ошибок или предупреждений. Как сказал мой лектор, индекс массива -1
обращается к другой переменной. Я все еще в замешательстве, с какой стати язык программирования имеет такую возможность? Я имею в виду, зачем разрешать отрицательные индексы массива?
programming-languages
arrays
c
Мохаммед Фавзан
источник
источник
-1
подмассива является вполне допустимым способом ссылки на элемент перед этим массивом в большем массиве. Другая причина в том, что если индекс недопустим, программа недопустима, но в большинстве реализаций вы получите тихое плохое поведение, а не ошибку вне допустимого диапазона.Ответы:
Операция индексации массива
a[i]
приобретает смысл из следующих особенностей языка CСинтаксис
a[i]
эквивалентен*(a + i)
. Таким образом, справедливо сказать,5[a]
чтобы получить 5-й элементa
.Указатель арифметику говорит , что данный указатель
p
и целоеi
,p + i
указательp
выдвинутыхi * sizeof(*p)
байтИмя массива
a
очень быстро превращается в указатель на 0-й элементa
По сути, индексирование массивов является частным случаем индексирования указателей. Так как указатель может указывать на любое место внутри массива, любое произвольное выражение, которое выглядит, не
p[-1]
является неправильным при проверке, и поэтому компиляторы не (не могут) рассматривать все такие выражения как ошибки.Ваш пример,
a[-1]
гдеa
на самом деле имя массива фактически неверно. IIRC, оно не определено, если в результате выражения,a - 1
гдеa
известно, что это указатель на 0-й элемент массива , имеется значимое значение указателя . Таким образом, умный компилятор может обнаружить это и пометить как ошибку. Другие компиляторы могут быть совместимы, позволяя вам стрелять себе в ногу, указывая указатель на случайный слот стека.Ответ по информатике:
В Си
[]
оператор определяется по указателям, а не по массивам. В частности, это определяется с точки зрения арифметики указателя и разыменования указателя.В C указатель является абстрактным кортежем
(start, length, offset)
с условием, что0 <= offset <= length
. Арифметика указателя - это, по существу, арифметика отмены смещения, с оговоркой, что если результат операции нарушает условие указателя, это неопределенное значение. Отмена ссылки на указатель добавляет дополнительное ограничениеoffset < length
.В языке C есть понятие,
undefined behaviour
которое позволяет компилятору конкретно представлять этот кортеж как одно число и не обнаруживать каких-либо нарушений условия указателя. Любая программа, которая удовлетворяет абстрактной семантике, будет в безопасности с конкретной семантикой (с потерями). Все, что нарушает абстрактную семантику, может быть без комментариев принято компилятором и может делать с ним все, что захочет.источник
Массивы просто выкладываются как непрерывные куски памяти. Доступ к массиву, такой как [i], преобразуется в доступ к адресу ячейки памяти по адресу (a) + i. Этот код
a[-1]
вполне понятен, он просто ссылается на адрес перед началом массива.Это может показаться сумасшедшим, но есть много причин, почему это разрешено:
a[-1]
является действительным. Например, если я знаю, чтоa
это на самом деле не начало массива, а указатель на середину массива, тоa[-1]
просто получает элемент массива, который находится слева от указателя.источник
a[-1]
имеет смысл в некоторых случаяхa
, в данном конкретном случае это совершенно нелегально (но неКак объясняют другие ответы, это неопределенное поведение в Си. Предположим, что Си был определен (и в основном используется) как «ассемблер высокого уровня». Пользователи C ценят его за его бескомпромиссную скорость, и проверка материала во время выполнения (в основном) исключается ради чистой производительности. Некоторые конструкции C, которые выглядят бессмысленными для людей, пришедших с других языков, имеют идеальный смысл в C, как это
a[-1]
. Да, это не всегда имеет смысл (источник
Можно использовать такую функцию для написания методов выделения памяти, которые обращаются к памяти напрямую. Одним из таких применений является проверка предыдущего блока памяти с использованием отрицательного индекса массива, чтобы определить, можно ли объединить два блока. Я использовал эту функцию при разработке диспетчера энергонезависимой памяти.
источник
C не является строго типизированным. Стандартный компилятор C не будет проверять границы массива. Другое дело, что массив в C - это не что иное, как непрерывный блок памяти, и индексирование начинается с 0, поэтому индекс -1 - это местоположение любого битового шаблона до
a[0]
.Другие языки хорошо используют отрицательные индексы. В Python
a[-1]
вернет последний элемент,a[-2]
вернет второй к последнему элемент и так далее.источник
int
,a[-5]
и, в общем,int i; ... a[i] = ...;
они правильно напечатаны. Ошибки индекса обнаруживаются только во время выполнения. Конечно, умный компилятор может обнаружить некоторые нарушения.Простыми словами:
Все переменные (включая массивы) в C хранятся в памяти. Допустим, у вас есть 14 байтов «памяти», и вы инициализируете следующее:
Также рассмотрим размер int как 2 байта. Затем, гипотетически, в первых 2 байтах памяти будет сохранено целое число a. В следующих 2 байтах будет сохранено целое число первой позиции массива (что означает массив [0]).
Затем, когда вы говорите, что массив [-1] подобен обращению к целому числу, сохраненному в памяти, которое находится непосредственно перед массивом [0], которое в нашем гипотетически является целым числом a. На самом деле, это не совсем то, как переменные хранятся в памяти.
источник
источник