Рассмотрим этот фрагмент программы на языке 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++)
^
Почему это происходит?
Это C++. Обратите внимание на cout.
@Mulliganaceous Я также заметил, что cout
не работает согласно OP.
Я подозреваю, что OP может компилировать как для C++, так и для C и спрашивать о разных результатах.
К вашему сведению, i
в int i=0
и int i=10;
разные. int i=10;
находится только внутри цикла, а не за его пределами. Я не думаю, что это дает ошибку.
Вопрос не так уж и плох. Все дело в области видимости переменной и ее различном поведении в c и C++.
Не могли бы вы оставить это так, чтобы оно тоже могло правдоподобно скомпилироваться на c? То есть удалить cout. Здесь похоронен хороший вопрос.
@EricPostpischil Вы видели оригинал, к которому относится мой комментарий?
@EugeneSh. Редактирование - моя вина. Я думаю, что я истолковал намерение OP, но вы можете откатиться, если считаете, что я ошибаюсь.
@Bob__ В результате вашего редактирования вопрос получил +9 :) Оставим как есть.
Честно говоря, я не понимаю сути этого вопроса. Похоже, вы спрашиваете «почему C и C++ делают это по-разному», на что ответ просто ... C и C++ - разные языки. В чем-то они наверняка очень похожи, но они не одинаковы, и ожидание того, что одно будет идеальным подмножеством другого, приведет к разочарованию. С учетом сказанного, я задаю этот вопрос нравиться, и интересно изучить различия, но это не похоже на хороший вопрос о переполнении стека. Если бы он был немного переформулирован, чтобы спросить о C++, на примере C, это могло бы быть лучше.
Совершенно ясно, о чем идет речь. Голосование за повторное открытие.
Это связано с тем, что языки C и C++ имеют разные правила повторного объявления переменных в области, вложенной в цикл for
:
C++
помещает i
в область тела цикла, поэтому второй int i = 10
является повторным объявлением, что запрещено.C
допускает повторное объявление в рамках цикла for
; самая внутренняя переменная "выигрывает"Вот демонстрация запущенная программа на C и Программа на C++ не компилируется.
Открытие вложенной области внутри тела исправляет ошибку компиляции (демонстрация):
for (int i =0 ; i != 5 ; i++) {
{
int i = 10;
cout << i << endl;
}
}
Теперь i
в заголовке for
и int i = 10
находятся в разных областях, поэтому программе разрешено запускаться.
Есть одно сомнение for(int i=0;i<5;i++){ int i=10; cout<<i; }
Подпадает ли он под перепланировку или нет. Его код C++
, и он не вызывает ошибок.
@achal Это должно привести к ошибке повторного объявления, потому что в C++ цикл for
имеет единую область видимости для тела и переменной, объявленной в заголовке.
на самом деле только старый C допускает это, потому что итератор цикла принадлежал к глобальной области видимости. Конечно, вы можете объявить тот же идентификатор в приведенной ниже области. Это было изменено в C99.
@Быстрый; Это совсем не так.
@Swift: это было так в старом VC++, VC++ 6? не помню
@AndriyTylychko вплоть до TurboC++, QuickC (не C++) и раньше, конечно, теперь люди говорят: «Это не так», потому что clang и gcc реализуют это по-разному и ведут себя иначе, если вы используете соответствующий флаг -std. Это было результатом сомнительного определения в стандарте текст, который определял оператор инициализации в области, которая была выше тела цикла, затем где-то в примечаниях добавлено: но вы не можете использовать те же идентификаторы. Обратите внимание, что C89 не разрешал объявление переменной внутри заголовка for (). Но Компиляторы уже имели это. Беспорядок с циклом for (), инициализатором и виртуальным ключевым словом - исторические проблемы.
В отличие от C, C++ имеет правило
С ++ 11-§6.5.3 / 1:
The
for
statementfor ( for-init-statement conditionopt ; expressionopt ) statementis equivalent to
{ for-init-statement while ( condition ) { statement expression ; } }
except that names declared in the for-init-statement are in the same declarative-region as those declared in the condition [...]
Это означает, что возможности for-init-statement
и statement
совпадают. * и приведенный ниже код вызовет ошибку.
for(int i = 0; i < 5; i++){
int i = 10; // Invalid.
// ...
}
В C,
C11-§6.8.5 / 5:
An iteration statement is a block whose scope is a strict subset of the scope of its enclosing block. The loop body is also a block whose scope is a strict subset of the scope of the iteration statement.
Следовательно, statement
имеет собственную область действия, и приведенный выше код действителен и эквивалентен
for(int i = 0; i < 5; i++){
{
int i = 10; // Valid.
// ...
}
}
Suggested reading: n3337: 6.5.1 The while statement/p(2). Same reference can be found in c++17 draft (n4659) in section § 9.5.1 and §9.5.3 .
N3242 - это черновик до C++ 11; безусловно, рекомендуемая литература должна быть последней версией стандартов и / или последней версией проекта.
@ M.M; Под последним вы имеете в виду черновик n4741?
Что-то вроде этого, да. Последний стандарт - N4659
@ M.M; ОК. Обновлено.
Я не думаю, что стандартный язык C++ действительно оправдывает пометку примера в вопросе как ошибки. Фактически существует два уровня, которые отделяют int i=0
for-init-statement от int i=10;
, содержащегося в теле цикла. Во-первых, заменяющий текст после «эквивалентно» вводит дополнительные фигурные скобки вокруг утверждение, что исключение указано в словах не относится к, поскольку в нем говорится только о том, что for-init-statement и условие (мой курсив) находятся на одном декларативном уровне; он вообще не упоминает тело цикла. (...)
Во-вторых, в этом примере утверждение (тело цикла) на самом деле является составное заявление (т. Е. Его самые внешние токены являются фигурными скобками), и эти фигурные скобки остаются в тексте замены, который, следовательно, становится конкретно { int i=0; while(i<5) { {int i=10; cout << i << endl;} i++; } }
, с явно на два уровня фигурных скобок вокруг i=10
больше, чем около i=0
. Даже если истолковать неверно сформулированное исключение как цель эффективно снять один уровень скоб, останется одна пара. Обратите внимание на то, что двойные фигурные скобки вокруг тела цикла заставляют сообщение об ошибке исчезнуть.
@MarcvanLeeuwen; Я бы сказал, что это плохое чтение текстов в ответ. Я настоятельно рекомендую вам прочитать ответ еще раз сверху вниз, а затем просмотреть разделы §6.5.1 и §6.5.3, чтобы получить полную картину.
Я прочитал эти разделы до того, как написал свои комментарии; почему ты думаешь, что я этого не сделал? Кстати, я также читал в 6.5 «Если подзапрос в операторе итерации является единственным оператором, а не составным оператором, это как если бы оно было переписано в составное выражение, содержащее исходный оператор», что означает, что вы фактически получить пару фигурных скобок, даже если вы написали тело цикла, в котором их нет (но это не применимо в примере), и 6.3 «Составной оператор определяет область действия блока» без каких-либо исключений или модификаций. Таким образом, каждое тело цикла представляет собой область видимости блока отдельный.
Между прочим, я думаю, что один момент заключается в том, что в §6.5.3 / 1 должен сказано: «... за исключением того, что имена, объявленные в инструкции for-init, находятся в той же декларативной области, что и имена, объявленные в утверждение», что по крайней мере, соответствовало бы намерению отметить пример как ошибку. Это все равно не очень хорошая формулировка, поскольку она должна относиться только к именам, объявленным на самом внешнем уровне внутри утверждение.
Кроме того, IMHO плохая идея в первую очередь для определять значения X, говоря, что «конструкция X эквивалентна Y, за исключением того, что у нее есть какое-то другое свойство», поскольку тогда нет способа узнать, как X получил это свойство разные. Лучше описать значение X напрямую, чтобы прояснить все детали; возможно, добавьте в качестве комментария, что это почти то же самое, что и Y, и укажите, в чем заключается тонкая разница.
Это не повторное объявление.
Смотрите внимательно ...
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
для компиляции.
Обновлено:Вы также можете локально заблокировать первоначально объявленные переменные, повторно объявив их в циклах for.
Это должен быть C или C++?