У меня есть массив из 6 элементов, но когда я пытаюсь получить доступ к arr[6], он выдает мне мусорное значение вместо ошибки переполнения массива. В чем причина этого?
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int arr[] = {3, 2, 1, 56, 10000, 167};
cout << "Value: " << arr[6];
return 0;
}
Примечания: (1) Почему мне не следует #include <bits/stdc++.h>? (2) В чем проблема с «использованием пространства имен std;»?.
Если вам нужна проверка границ, используйте std::vector
или std::array
и используйте их функции at()
для доступа к элементам.
В C++ доступ к элементу массива за пределами границ является неопределенным поведением, поэтому (в общих чертах) реализация (компилятор, библиотека, среда выполнения) не требуется для обнаружения, диагностики или получения какого-либо конкретного результата. Ненужное значение или сбой программы одинаково верны. Двумя распространенными причинами неопределенного поведения являются: (1) трудность диагностики (например, если указатель передается функции и разыменовывается, что невозможно диагностировать без видимости как вызывающего, так и вызываемого объекта) (2) обнаружение проблемы во время выполнения нежелательное и часто ненужное (если код спроектирован правильно) влияние на производительность
Вам необходимо вручную включить проверку границ (которая обычно включается только во время разработки, а не в выпусках). Найдите «дезинфицирующее средство для адресов».
Примечание: неопределенное поведение
C и C++ не выполняют проверку границ массивов. Это часть того, что делает их быстрыми.
Однако если вы осуществляете доступ за пределы конца массива, вы вызываете неопределенное поведение в своем коде. Он может выйти из строя, выдать странные результаты или может показаться, что работает правильно.
Кстати, я не знаю, что это за bits/stdc++.h
— это не стандартный заголовок, и, похоже, он все равно не содержит ничего полезного, поэтому я просто удалил его.
Теперь обратите внимание:
stieber@gatekeeper:~ $ g++ Test.cpp
stieber@gatekeeper:~ $ g++ -Wall Test.cpp
stieber@gatekeeper:~ $ g++ -Wall -O2 Test.cpp
Test.cpp: In function ‘int main()’:
Test.cpp:9:31: warning: array subscript 6 is above array bounds of ‘int [6]’ [-Warray-bounds]
9 | cout << "Value: " << arr[6];
| ^
Test.cpp:8:9: note: while referencing ‘arr’
8 | int arr[] = {3, 2, 1, 56, 10000, 167};
| ^~~
Так что да, как только вы займетесь оптимизацией, компилятор уже сможет обнаружить такие простые ошибки.
Хотя меня немного смущает, что для этого нужна оптимизация; вполне возможно, что другие компиляторы выдадут предупреждение даже без оптимизации (поскольку он знает размер массива, а индекс является константой времени компиляции...).
Однако лучшим решением было бы
#include <iostream>
#include <array>
int main()
{
std::array<int,6> arr{3, 2, 1, 56, 10000, 167};
std::cout << "Value: " << arr[6];
return 0;
}
Все еще просто компилятор жалуется:
stieber@gatekeeper:~ $ g++ -Wall -O2 Test.cpp
Test.cpp: In function ‘int main()’:
Test.cpp:8:36: warning: array subscript 6 is outside array bounds of ‘std::array<int, 6> [1]’ [-Warray-bounds]
8 | std::cout << "Value: " << arr[6];
| ^
Test.cpp:7:23: note: at offset 24 into object ‘arr’ of size 24
7 | std::array<int,6> arr{3, 2, 1, 56, 10000, 167};
| ^~~
Но теперь вы можете заменить эту строку чем-то другим:
std::cout << "Value: " << arr.at(6);
stieber@gatekeeper:~ $ g++ -Wall -O2 Test.cpp && ./a.out
terminate called after throwing an instance of 'std::out_of_range'
what(): array::at: __n (which is 6) >= _Nm (which is 6)
Aborted
По сути, C++ делает то, что вы ему говорите: если вы не хотите проверять границы, вы не получаете проверку границ.
В качестве простого примера, если вы делаете что-то вроде
for (size_t i=0; i<arr.size(); i++)
{
... arr[i] ...
}
ты знаешь, что не выходишь за пределы поля, так зачем проверять? Именно на таком образе мышления построена большая часть C++.
Так устроен язык. C и C++ не проверяют границы своих массивов из-за производительности. Вы столкнулись с тем, что называется неопределенным поведением.