Доступ к элементу массива, находящемуся вне диапазона

У меня есть массив из 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;
}

Так устроен язык. C и C++ не проверяют границы своих массивов из-за производительности. Вы столкнулись с тем, что называется неопределенным поведением.

Daniel A. White 04.05.2024 14:21

Если вам нужна проверка границ, используйте std::vector или std::array и используйте их функции at() для доступа к элементам.

Some programmer dude 04.05.2024 14:26

В C++ доступ к элементу массива за пределами границ является неопределенным поведением, поэтому (в общих чертах) реализация (компилятор, библиотека, среда выполнения) не требуется для обнаружения, диагностики или получения какого-либо конкретного результата. Ненужное значение или сбой программы одинаково верны. Двумя распространенными причинами неопределенного поведения являются: (1) трудность диагностики (например, если указатель передается функции и разыменовывается, что невозможно диагностировать без видимости как вызывающего, так и вызываемого объекта) (2) обнаружение проблемы во время выполнения нежелательное и часто ненужное (если код спроектирован правильно) влияние на производительность

Peter 04.05.2024 14:59

Вам необходимо вручную включить проверку границ (которая обычно включается только во время разработки, а не в выпусках). Найдите «дезинфицирующее средство для адресов».

HolyBlackCat 04.05.2024 15:01

Примечание: неопределенное поведение

Jesper Juhl 04.05.2024 17:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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++.

Другие вопросы по теме