Определение переменной и ее статического эквивалента в той же функции
Я не понимаю, как работает следующий код:
#include "stdio.h"
int main(void) {
int i = 3;
while(i--) {
static int i = 100;
i--,
printf("%d\n", i);
}
return 0;
}
Код, скомпилированный с помощью Clang или GCC, печатает следующий вывод:
99
98
97
Может кто-нибудь объяснить мне, что здесь происходит? Похоже, что две операции выполняются в одной инструкции и более одного раза. Это поведение undefined?
Я наблюдаю то же поведение в С++.
Ответы
Ответ 1
Это не поведение undefined.
#include "stdio.h"
int main(void) {
int i = 3; //first i
while(i--) {
static int i = 100; //second i
i--,
printf("%d\n", i);
}
return 0;
}
В то время как тело цикла является наиболее локальным i
(вторым i
). Проверяя условие во время цикла, он не знает, что есть в теле. Поэтому нет проблем с выбором первого i
.
Ответ 2
Википедия говорит очень важную вещь по этому поводу:
В компьютерном программировании переменная shadowing возникает, когда переменная, объявленная в определенной области (блок принятия решения, метод или внутренний класс), имеет то же имя, что и переменная, объявленная во внешней области. На уровне идентификаторов (имена, а не переменные) это называется маскировкой имен. Эта внешняя переменная называется скрытой внутренней переменной, тогда как внутренний идентификатор, как говорят, маскирует внешний идентификатор.
Теперь внутри блока он находит статическую переменную и работает над ней, но условие while уменьшает i
, который является объявленным вне блока. Сфера действия различна - нет смысла использовать правильное значение i
. Это законный код C, но не обязательно хороший способ писать вещи.
Фактически это gcc -Wshadow progname.c
дает
progname.c: In function 'main':
progname.c:7:20: warning: declaration of 'i' shadows a previous local [-Wshadow]
static int i=2;
^
progname.c:5:9: warning: shadowed declaration is here [-Wshadow]
int i=2;
^
Из стандарта § 6.2.1p4
... Если идентификатор обозначает два разных объекта в одном и том же пространстве имен, области действия могут перекрываться. Если это так, объем одного объекта (внутренний объем) будет полностью закрыт перед областью действия другого объекта (внешней области). Внутри внутренней области идентификатор обозначает объект, объявленный во внутренней области; объект, объявленный во внешней области, скрыт (и не отображается) во внутренней области.
Ответ 3
Его можно объявить одну и ту же переменную внутри вложенной области. Компилятор рассматривает их как разные переменные. Это очень запутанно, но переменная, к которой вы обращаетесь каждый раз, является объявленной во внутренней области. Вне while
это int i = 3;
и внутри него находится static int i = 100;
#include "stdio.h"
int main(void) {
int i = 3; // outer i
while(i--) { // outer i
static int i = 100; // inner i
i--, // inner i
printf("%d\n", i); // inner i
}
return 0;
}
Если бы это была функция, отличная от основной, то второй вызов на нее вызывал бы
96
95
94
и т.д.