В этом коде ниже я пытаюсь получить доступ к -1-му элементу массива, но не получаю никаких ошибок во время выполнения.
#include <stdio.h>
int A[10] = {0};
int main(){
A[-1] += 12;
printf("%d",A[-1]);
return 0;
}
Когда я запускаю код, он выводит 12
это означает, что он добавляет 12 к несуществующему A [-1]. До сегодняшнего дня всякий раз, когда я пытался получить доступ к элементу вне пределов, я получал ошибку времени выполнения. Я никогда не пробовал это на простом коде раньше.
Может кто-нибудь объяснить, почему мой код работает успешно?
Я запустил его на своем компьютере, а также на ideone, в обоих случаях она прошла успешно.
Видите ли, когда вы выделяете такую переменную, она попадает в стек. Стек содержит небольшие пакеты информации о локальных переменных в каждой вызываемой вами функции, чтобы сказать это простыми словами. Среда выполнения может проверить, превышаете ли вы границы выделенного стека, но не можете ли вы записать некоторые данные в недопустимое место в стеке. Стек может выглядеть следующим образом:
[4 байта — немного ptr] [4 байта — первый элемент А] [4 байта — второй элемент А] …Когда вы пытаетесь присвоить -1-й элемент массива, вы фактически пытаетесь прочитать четыре байта, предшествующих массиву (четыре байта, потому что это массив int). Вы перезаписываете некоторые данные, хранящиеся в стеке, но они все еще находятся в действительной памяти процесса, поэтому никаких жалоб со стороны системы нет.
Попробуйте запустить этот код в режиме выпуска в Visual Studio:
#include <stdio.h>
int main(int argc, char * argv[])
{
// NEVER DO IT ON PURPOSE!
int i = 0;
int A[5];
A[-1] = 42;
printf("%d\n", i);
getchar();
return 0;
}
Редактировать: в ответ на комментарии.
Я упустил тот факт, что A является глобальным. Он не будет храниться в стеке, но вместо этого (в основном, вероятно) в сегменте .data двоичного модуля, однако остальная часть объяснения остается: A [-1] все еще находится в памяти процесса, поэтому назначение не вызовет AV. Однако такое присваивание будет перезаписывать что-либо, то есть перед A (возможно, указателем или другой частью двоичного модуля), что приведет к неопределенному поведению.
Обратите внимание, что мой пример может работать, а может и нет, в зависимости от компилятора (или режима компилятора). Например, в режиме отладки программа возвращает 0 — я полагаю, что диспетчер памяти вставляет некоторые сторожевые данные между кадрами стека, чтобы перехватить ошибки, такие как переполнение буфера / переполнение.
C и C ++ не имеют никакой проверки границ. Это часть языка. Это позволяет языку работать быстрее.
Если вы хотите проверить границы, используйте другой язык, который есть. Ява возможно?
Когда ваш код выполняется, вам просто повезло.
В C ++ (и C) массивы не извлекают индексы диапазона. Они не классы.
В C ++ 11, однако вы можете использовать std::array<int,10>
а также at()
функционировать как:
std::array<int,10> arr;
arr.at(-1) = 100; //it throws std::out_of_range exception
Или вы можете использовать std::vector<int>
а также at()
функция-член.