Случаи указания типа данных в условии цикла

161
26 ноября 2016, 19:10

Добрый вечер. Пришел с вопросом. Читал тут книгу, в одном фрагменте было написано, что если я укажу тип данных у переменной в условии функции, то значение переменной будет иметь большую область видимости, чем если бы я указал переменную без типа. То есть без типа переменная будет действовать, пока активен цикл, но с типом данных она будет действовать и на еще одни скобки.

Решил проверить. Написал с int'ом-все сработало. Убрал int-и программа стала использовать значение переменной в цикле во всей программе и по какому-то принципу компилятор его даже изменил, по какому-я не понимаю.

Вопрос: Я некорректно понял инфу из учебника? Да, то как все должно быть на самом деле?

#include <iostream>
using namespace std;
int main() {
 int done=5;
 int howmany=3;
   for( done=1; done<howmany; done++)
   {
    cout<<"7"<<endl;
   }
   cout<<done;
  return 0;
}

Вывод:

7
7
3

Answer 1

Рассмотрим следующую программу

#include <iostream>
int main() 
{
    int done = 5;
    int howmany = 3;
    for ( done = 1; done < howmany; done++ )
    {
        std::cout << done << std::endl;
    }
    std::cout << done << std::endl;
    return 0;
}

Ее вывод на консоль

1
2
3

В этой программе в блоке кода функции main объявлена переменная done. Сначала эта переменная инициализируется значением 5

int done = 5;

а затем в предложении for ей присваивается значение 1

for ( done = 1; done < howmany; done++ )
      ^^^^^^^^

После выхода из цикла эта переменная будет иметь значение 3.

Теперь рассмотрим следующую программу

#include <iostream>
int main() 
{
    int done = 5;
    int howmany = 3;
    for ( int done = 1; done < howmany; done++ )
    {
        std::cout << done << std::endl;
    }
    std::cout << done << std::endl;
    return 0;
}

Ее вывод на консоль

1
2
5

В этой программе в предложении for объявляется переменная с именем done, совпадающем с именем переменной, объявленной в блоке кода main.

for ( int done = 1; done < howmany; done++ )
      ^^^^^^^^^^^^ 

Эта переменная в предложении for скрывает одноименную переменную с таким же именем, объявленную во внешнем блоке кода. Ее область определения - это тело цикла for. После выхода из цикла данная переменная прекращает свое существование.

Из стандарта C++ (6.5.3 The for statement)

3 If the for-init-statement is a declaration, the scope of the name(s) declared extends to the end of the for statement.

Поэтому последняя строка вывода программы

5

уже выводит на консоль значение переменной done, которая была объявлена до цикла в блоке кода функции main

В общем случае имя, объявленное во внутренней области объявления скрывает такое же имя, объявленное во внешней области объявления

Из стандарта C++ (3.3.10 Name hiding)

1 A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).

Обратите внимание, что есть важное отличие в определении предложения for между C++ и C.

В C++ во второй части предложения for, где записывается условие, также может быть объявление.

Рассмотрите следующую демонстрационную программу

#include <iostream>
int main() 
{
    int done = 5;
    int howmany = 3;
    for ( ; int howmany = done; done-- )
    {
        std::cout << done << std::endl;
    }
    std::cout << '\n' << howmany << std::endl;
    return 0;
}

Ее вывод на консоль

5
4
3
2
1
3

При каждой итерации данного цикла for

for ( ; int howmany = done; done-- )
        ^^^^^^^^^^^^^^^^^^

объявляется переменная howmany, которая скрывает переменную с таким же именем, объявленную в функции main. Значение, присвоенное этой переменной, преобразуется к булевскому типу. Если оно равно 0, то условие будет ложным и произойдет выход из цикла.

После цикла выводится уже значение переменной howmany, объявленной в main.

Ну, и напоследок пример, когда в предложении for сразу в двух его частях объявляются переменные, которые скрывают одноименные переменные с теми же именами, объявленные в main. После выхода из цикла эти переменные прекращают свое существование, и становятся видимыми переменные, объявленные в main.

#include <iostream>
int main() 
{
    int done = 10;
    int howmany = 20;
    for ( int done = 5; int howmany = done; done-- )
    {
        std::cout << done << std::endl;
    }
    std::cout << '\n' << done << std::endl;
    std::cout << howmany << std::endl;
    return 0;
} 

Вывод этой программы на консоль

5
4
3
2
1
10
20

Предложение for в этой программе логически может быть представлено как

{
    int done = 5;
    Label_repeat:
    {
        int howmany = done;
        if ( howmany != 0 )
        {
            std::cout << done << std::endl;
            done--;
            goto Label_repeat;
        }
        else
        {
            goto Label_exit;
        }
    }
}
Label_exit:
//...                  
Answer 2

Как я понимаю, вы просто указали в цикле for

for(int done ....

При этом произошло следующее: вы объявили новую переменную done, которая имеет областью видимости только цикл for, а после выхода из него это переменная done благополучно умирает, и имя done относится вновь к той переменной, которая объявлена в начале функции main. Поскольку в цикле вы меняли переменную, которая просто "тезка" локальной переменной done из функции main, последняя оставалась неизменной.

Но как только вы убираете слово int из заголовка цикла for, объявление исчезает, и переменная, используемая в цикле for - та же, что объявлена в main. Естественно, что при работе цикла она изменяется.

А вообще по вопросу очевидно, что вы эту тему понимаете неверно, так что почитайте повнимательнее учебник на тему областей видимости переменных.

READ ALSO
Как заменить StretchDIBits на BitBlt

Как заменить StretchDIBits на BitBlt

Здравствуйте! Использую такую функцию:

326
Покажите, как создать DLL со строками на C++

Покажите, как создать DLL со строками на C++

Пробовала разные варианты создания на выходе файла DLL, который содержит несколько строк, поскольку изучаю C# - C++ непонятен совсем

206
Подключение POCO dll C++

Подключение POCO dll C++

Пытаюсь подключить POCO-библиотекиСобрал dll проекта для работы с ZIP

234