@vurdalaq

Почему перемещение объявления и инициализации переменной на новую строчку кода влияет на результат работы программы?

Заметил, что если переместить объявление и инициализацию переменной на несколько строк выше, перед другой конструкцией for, то вывод программы поменяется.

ТЗ:
Напишите программу, которая определяет и инициализирует массив первыми 10 нечетными числами.
Выведите числа из массива на консоль по пять в строку.

Моя реализация:

#include <iostream>

int main() {
    const int len = 10;

    int nums[len] = {};

    for(unsigned i; i < len; i++) {
        nums[i] = i * 2 + 1;
    }

    int count = 0;

    for(unsigned i; i < len; i++) {
        if(count % 5 == 0) { 
            std::cout << '\n';
        }
        std::cout << *(nums+i) << ' ';
        count++;
    }
}

Вывод такой, какой и должен быть:

> 1 3 5 7 9
11 13 15 17 19

Но если переместить строку int count = 0; выше:

int count = 0;

for(unsigned i; i < len; i++) {
    nums[i] = i * 2 + 1;
}


for(unsigned i; i < len; i++) {
    if(count % 5 == 0) { 
        std::cout << '\n';
    }
    std::cout << *(nums+i) << ' ';
    count++;
}

то:

> 0 0 0 0 0
0 0 0 17 19

Ещё, если в первом случае убрать инициализацию, оставив только объявление:

int count;

Результат поменяется:

> 1 3 
5 7 9 11 13
15 17 19

Хочется узнать причину подобного поведения. Почему же тот первый цикл влияет на результат работы второго цикла именно таким образом, через count?
Использую Windows x64, g++ компилятор.
  • Вопрос задан
  • 202 просмотра
Решения вопроса 2
shurshur
@shurshur
Сисадмин, просто сисадмин...
В этом коде есть важная ошибка. В циклах for нигде не задано начальное значение переменной i, поэтому она может иметь любое значение, её поведение неопределено. Например, она может выделиться там же, где была выделена предыдущая, поэтому i во втором цикле будет равна последнему значению в предыдущем, то есть 10, даже если в первом цикле повезло попасть на 0.

Локальные переменные как правило выделяются в стеке, поэтому если между двумя for стоит определение ещё одной переменной, то она, вероятно, выделится на месте i. И поэтому новая переменная i попадёт в другую часть стека, где, если повезёт, будет 0.

Поэтому неудивительно, что поведение различаетя. Ведь оно в принципе не определено. В разных аппаратно-программных платформах и с разными компиляторами поведение может оказаться самым непредсказуемым. Например, в памяти может остаться мусор от предыдущей программы.

Решение простое: надо везде в циклах for указать начальное значение i, тогда всё станет нормально, и перестановка определения count перестанет создавать такие совсем не странные эффекты.
Ответ написан
Adamos
@Adamos
Я правильно понял, если после объявления неопределенной переменной X начать объявлять или менять значения другим переменным, то X "засорится"?

Наоборот.
int i;
Под переменную i выделен участок стека, лежащее в нем значение не меняется. Но в учебной программе из пары строк там, скорее всего, будет 0, и это создаст иллюзию, что все нормально. Кроме того, некоторые компиляторы в некоторых условиях инициализируют любые переменные, что еще больше запутывает ситуацию.
int i;
for(int n = 0; n < 10; ++n) {}

Под переменную i выделен участок стека, лежащее в нем значение не меняется.
Под переменную n выделен следующий участок стека, его изменение никак на i не сказывается.
for(int n = 0; n < 10; ++n) {}
int i;

Под переменную n выделен участок стека, его значение к концу цикла равно 10 - и уже ненужная переменная n уничтожается, освобождая место в стеке.
Под переменную i выделен участок стека, лежащее в нем значение не меняется. Это вполне может быть тот самый участок, который только что освободился, в нем записано 10 (С++ не выполняет ничего, что явно не указал программист - это оптимальный язык). Так что i вполне может равняться 10. Или нет.

Подобные ситуации называются "неопределенным поведением".
Программист на С++ обязан исключить их из своей программы.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы