Ошибка повторного использования переменной цикла for в цикле

Рассмотрим этот фрагмент программы C:

for(int i = 0; i < 5; i++)
{
    int i = 10;  // <- Note the local variable

    printf("%d", i); 
}    

Он компилируется без какой-либо ошибки, и, когда он выполняется, он дает следующий результат:

1010101010

Но если я напишу аналогичный цикл в C++:

for(int i = 0; i < 5; i++)
{
     int i = 10;

     std::cout << i; 
}

Ошибка компиляции с этой ошибкой:

prog.cc:7:13: error: redeclaration of 'int i'  
     int i = 10;  
         ^  
prog.cc:5:13: note: 'int i' previously declared here  
     for(int i = 0; i < 5; i++)  
             ^   

Почему это происходит?

Ответ 1

Это связано с тем, что языки C и C++ имеют разные правила относительно повторного объявления переменных в области, вложенной в цикл for:

  • C++ помещает i в область тела цикла, так что второй int я = 10 является повторной декларацией, которая запрещена
  • C разрешает повторную декларацию в области внутри цикла for; самая внутренняя переменная "выигрывает"

Ниже приведена демонстрация запущенной программы на C, а программа C++ не скомпилирована.

Открытие вложенной области внутри тела фиксирует ошибку компиляции (demo):

for (int i =0 ; i != 5 ; i++) {
    {
        int i = 10;
        cout << i << endl;
    }
}

Теперь i в заголовке for и int я = 10 находятся в разных областях, поэтому программе разрешено работать.

Ответ 2

В отличие от C, C++ имеет правило,
C++ 11-§6.5.3/1:

Заявление for

for ( for-init-statement conditionopt ; expressionopt ) statement 

эквивалентно

{
    for-init-statement 
    while ( condition ) {
        statement 
        expression ;
    } 
}

за исключением того, что имена, объявленные в for-init-statement, находятся в той же декларативной области, что и объявленные в условии [...]

Это означает, что область действия for-init-statement и statement одинаковы *, а приведенный ниже код вызовет ошибку

for(int i = 0; i < 5; i++){
     int i = 10;    // Invalid. 
     // ...
}

В C,
C11- §6.8.5/5:

Оператор итерации представляет собой блок, область видимости которого является строгим подмножеством области его охватывающего блока. Тело цикла также является блоком, область видимости которого является строгим подмножеством области действия итерации.

Следовательно, statement имеет свою собственную область действия, и приведенный выше код действителен и эквивалентен

for(int i = 0; i < 5; i++){
    {
         int i = 10;    // Valid. 
         // ...
    }
}

Предлагаемое чтение: n3337: 6.5.1 Оператор while/p (2).Такую же ссылку можно найти в C++ 17 draft (n4659) в разделе § 9.5.1 и §9.5.3.

Ответ 3

Это не переделка.

Смотрите внимательно...

for(int i = 0; i < 7; i++)
{
    printf("i = %d\n", i);
    int i = 5;
    printf("new i = %d\n", i);
}

Вывод вышеуказанного кода: -

i = 0
new i = 5
i = 1
new i = 5
i = 2
new i = 5
i = 3
new i = 5
i = 4
new i = 5
i = 5
new i = 5
i = 6
new i = 5

Ясно, что существуют два разных i

Более новый i имеет более локальную область.

Это ошибка?

нет

В чем цель?

Если это не разрешено, может быть очень сложно поддерживать крупные проекты, так как вы постоянно сталкиваетесь с именованием коллизий.

Как правило, хотя очень плохой практикой является предоставление одинакового имени различным переменным в разных областях, вы должны избегать этого, когда это возможно.

Почему нет предупреждающего сообщения?

Используйте gcc file_name.c -Wshadow для компиляции.


EDIT: вы также можете локально блокировать первоначально объявленные переменные, обновляя их для циклов.