С
Это напомнило мне об ошибке, с которой я столкнулся, когда узнал C. К сожалению, оригинальный вариант, похоже, не работает с текущим GCC, но этот по-прежнему работает:
#define ARR_SIZE 1234
int main() {
int i = ARR_SIZE;
int arr[ARR_SIZE];
while(i >= 0) {
(--i)[arr] = 0;
}
i = *(int*)0;
}
Это очевидно segfaults, потому что мы разыменовываем нулевой указатель, правильно?
Неверно - на самом деле, это бесконечный цикл, поскольку наше условие цикла отключено на единицу. Из-за префикса декремента, i
работает с 1023 до -1. Это означает, что присвоение перезаписывает не только все элементы arr
, но также и область памяти непосредственно перед ним - что, как оказалось, является местом, где i
хранится. Достигнув -1
, i
переписывает себя 0
и , таким образом , условие цикла выполняется снова ...
Это был оригинальный вариант I, который я больше не могу воспроизвести:
То же самое сработало с i
подъемом вверх от 0 и отключением на единицу. Последний GCC всегда хранится i
раньше arr
в памяти; это должно было отличаться в старых версиях (возможно, в зависимости от порядка объявления). Это была настоящая ошибка, которую я произвел в одной из моих первых игрушечных программ, работающих с массивами.
Кроме того, это очевидно, если вы знаете, как работают указатели в C, но может удивить, если вы этого не сделаете:
Вы можете подумать, что присваивание для (--i)[arr]
бросает ошибку, но это допустимо и эквивалентно arr[--i]
. Выражение a[x]
является просто синтаксическим сахаром, для *(a + x)
которого вычисляется и разыменовывается указатель на индексированный элемент; дополнение, конечно, коммутативно и, следовательно, эквивалентно *(x + a)
.