Значение элемента массива вне границ - должно ли оно меняться при каждом выполнении или оставаться неизменным?
Для фактического вопроса перейдите к части вопроса. Для интересного реального примера неопределенного поведения продолжайте читать:)
Было это перечисление:
struct EnumStruct
{
enum Enum
{
val0 = 0,
val1,
val2,
val3,
val4
};
};
и в некоторой функции у нас было это:
const int arrayCount = 6;
int arr[] = {
EnumStruct::val0,
EnumStruct::val1,
EnumStruct::val2,
EnumStruct::val3,
EnumStruct::val4
InvalidValue
};
Затем был цикл, который поставил arrayCount
элементы arr
в файл. Это был Prepare()
рутина для юнит-тестов. И модульный тест должен был проверить на наличие InvalidValue
в файле. Мне был назначен дефект о том, что модульный тест не пройден. Это отлично работает на моей машине, хотя. После пары часов отладки я заметил, что InvalidValue
на самом деле #define
д как -1
и после пропущенной запятой val4
, Вы можете только вообразить ругательства, которые вышли из моих уст в адрес того, кто написал этот код (на самом деле он работал более 3 лет).
Теперь, как вы можете видеть, массив на самом деле состоит из 5 значений - 0, 1, 2, 3, 3
но цикл также записывает в файл шестой элемент, что, конечно, является неопределенным поведением. Теперь, хотя технически это не определено, в Windows с MSVC не происходит сбоев - он просто записывает мусор, который находится в этом месте памяти. Дело в том, что если мусор оказывается чем-то, кроме 0, 1, 2, 3, or 4
, юнит-тесты пройдут успешно.
Вопрос: Похоже, что .vcproj
файл UT каким-то образом исправляется перед созданием UT. Я не знаю, как они это делают, но с их элементами массива out-of-bound сборки всегда равны 0. Мне кажется, что вся виртуальная память установлена в 0 перед выполнением программы. Что это за настройка проекта? Или я воображаю вещи? Я имею в виду, что если бы это была просто удача, что массив находился за пределами 0, то при многократном выполнении моя удача потерпела бы неудачу, не так ли? Но это всегда 0... Я в замешательстве. Кстати, когда я строю один и тот же проект, элемент "вне границ" всегда имеет разные значения при каждом выполнении. Можете ли вы объяснить это? Благодарю.
2 ответа
На вопрос о том, всегда ли в начале памяти 0? Ну, это может зависеть. В общем, когда ОС предоставляет вам страницу памяти, она будет очищена (в качестве меры безопасности, чтобы вы не могли прочитать значения, которые есть у любого другого процесса в их памяти), поэтому во многих случаях вы обнаружите, что неинициализированный может Похоже на 0, пока память не будет повторно использована в вашем собственном процессе, где вы получите все, что написали ранее.
Есть также некоторые флаги компилятора, которые могут повлиять на это. Чтобы обнаружить проблемы с неинициализированной памятью, иногда отладочные сборки записывают шаблоны в память после выделения из ОС и перед обработкой ее в программе, и другой шаблон после освобождения памяти в программе перед перераспределением (для обнаружения доступа к освобожденной памяти), поэтому что проще определить, что произошло (если вы видите в отладчике, что значение 0xDEADBEEF
, вы будете знать, что память уже была освобождена программой. Вам нужно будет прочитать документацию по компилятору /IDE для более точной информации.
Как вы говорите, он не определен, поэтому разработчик может делать все что угодно. Я вообще не могу говорить о Visual C++, но я знаю о других продуктах, которые делают такие вещи, как обнуление памяти при запуске отладочных сборок, так что такие вещи, как недопустимые разыменования указателей, потерпят неудачу на месте ошибки. Возможно, что Microsoft делает что-то подобное, я думаю.