Статическая переменная внутри функции в C


119

Что будет распечатано? 6 6 или 6 7? И почему?

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}

54
В чем проблема попробовать?
Эндрю

12
Вы пытались ввести это и убедиться в этом?
Wilhelmtell

21
Я хочу понять почему.
Вадиклк

7
@Vadiklk, так что задавайте вопрос, начинающийся с "Почему"
Андрей

1
ideone.com/t9Bbe Чего вы ожидаете? Результат не соответствует вашим ожиданиям? Почему вы ждали своего результата?
eckes

Ответы:


187

Здесь есть две проблемы: срок службы и область действия.

В области видимости переменной можно увидеть имя переменной. Здесь x виден только внутри функции foo ().

Время жизни переменной - это период, в течение которого она существует. Если бы x был определен без ключевого слова static, время жизни было бы от входа в foo () до возврата из foo (); поэтому он будет повторно инициализирован до 5 при каждом вызове.

Ключевое слово static продлевает время жизни переменной до времени жизни программы; например, инициализация происходит один раз и только один раз, а затем переменная сохраняет свое значение - каким бы оно ни было - во всех будущих вызовах foo ().


15
@devanl, да, мы.
orion elenzil 07

1
Просто и логично :)
Димитар Вукман

в каких сценариях нам нужно объявить переменную статической внутри функции? Просто интересно знать, поскольку я не использовал это раньше?
Akay

Я бы сказал спасибо, но на все это был дан ответ в самом верху страницы. меня смешит то, что люди не просто запускают свой собственный код. xD
Puddle

Это неверный ответ. В тот момент, когда вы думаете о рекурсивных функциях, определения, описанные здесь, не объясняют их поведения!
Филип Коулинг, 03

53

Выход : 6 7

Причина : статическая переменная инициализируется только один раз (в отличие от автоматической переменной), и дальнейшее определение статической переменной будет пропущено во время выполнения. И если он не инициализирован вручную, он автоматически инициализируется значением 0. Так,

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}

10

6 7

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


10

Это то же самое, что и следующая программа:

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

Все, что ключевое слово static делает в этой программе, - это сообщает компилятору (по сути): «Эй, у меня есть переменная, к которой я не хочу, чтобы кто-либо другой получал доступ, не говорите никому, что она существует».

Внутри метода ключевое слово static сообщает компилятору то же самое, что и выше, но также «никому не говорите, что это существует вне этой функции, оно должно быть доступно только внутри этой функции».

надеюсь, это поможет


13
Ну, на самом деле это не то же самое. В X все еще есть проблема с областью видимости. В этом примере вы можете тыкать и вертеться xв main; это глобально. В исходном примере xбыл локальным для foo, видимым только внутри этого блока, что обычно предпочтительнее: если foo существует для поддержки xпредсказуемым и видимым образом, то позволять другим тыкать его, как правило, опасно. Еще одно преимущество - держать его в объеме. foo() Он также сохраняет foo()портативность.
user2149140

2
@ user2149140 «никому не говори, что это существует вне этой функции, это должно быть доступно только внутри этой функции»
DCShannon 01

3
Хотя вы рассмотрели проблему области видимости из-за того, где объявлена ​​переменная, описание static как влияющего на область видимости, а не на время жизни, кажется неверным.
DCShannon 01

1
@Chameleon Вопрос помечен как c, поэтому в этом контексте ваш пример будет незаконным в глобальной области. (C требует постоянных инициализаторов для глобальных объектов, C ++ - нет).
Ричард Дж. Росс III

5

Статическая переменная внутри функции имеет срок жизни, пока выполняется ваша программа. Он не будет выделяться каждый раз, когда ваша функция вызывается, и освобождается, когда ваша функция возвращается.


Сказать, что это как «глобальная» переменная, а затем сказать, КРОМЕ, что вы не можете получить к ней доступ, - это оксюморон. Глобальный означает доступный где угодно. Что в этом случае статической INSIDE функции НЕ доступно везде. Проблема в OP, как отмечали другие, связана с объемом и сроком службы. Пожалуйста, не путайте людей с использованием термина «глобальный» и вводите их в заблуждение относительно области видимости переменной.
ChuckB

@ChuckB: Верно. Починил это. Ну прошло 6 лет. Мой предыдущий ответ был восприятием 6 лет назад!
Донотало

5

Выход: 6,7

причина

Объявление xнаходится внутри, fooно x=5инициализация происходит вне foo!

Здесь нам нужно понять, что

static int x = 5;

это не то же самое, что

static int x;
x = 5;

В других ответах здесь использовались важные слова, область видимости и время жизни, и было указано, что область действия xнаходится от точки ее объявления в функции fooдо конца функции foo. Например, я проверил, переместив объявление в конец функции, и это xстало необъявленным в x++;заявлении.

Таким образом, часть static int x(область действия) оператора действительно применяется там, где вы ее читаете, где-то ВНУТРИ функции и только оттуда и далее, а не над ней внутри функции.

Однако часть x = 5(время жизни) оператора - это инициализация переменной и выполнение ВНЕШНЕЙ функции во время загрузки программы. Переменная xрождается со значением5 когда программа загружается.

Я прочитал это в одном из комментариев: « Кроме того, это не решает действительно сбивающую с толку часть, а именно то, что инициализатор пропускается при последующих вызовах. Он пропускается при всех вызовах. Инициализация переменной происходит вне кода функции.

Значение 5 теоретически устанавливается независимо от того, вызывается ли вообще foo, хотя компилятор может оптимизировать функцию, если вы ее нигде не вызываете. Значение 5 должно быть в переменной до вызова foo.

Внутри fooзаявленияstatic int x = 5; вряд ли вообще будет генерироваться какой-либо код.

Я обнаружил, что адрес xиспользуется, когда я помещаю функцию fooв свою программу, а затем (правильно) предположил, что это же местоположение будет использовано, если я снова запущу программу. На частичном снимке экрана ниже показано, что xзначение имеет значение 5еще до первого вызова foo.

Точка останова перед первым вызовом foo


2

На выходе будет 6 7. Статическая переменная (внутри функции или нет) инициализируется ровно один раз перед выполнением какой-либо функции в этой единице перевода. После этого он сохраняет свое значение до тех пор, пока не будет изменен.


1
Вы уверены, что статика инициализируется перед вызовом функции, а не при первом вызове функции?
Джесси Пеппер

@JessePepper: По крайней мере, если память не изменяет, это зависит от того, говорите ли вы о C ++ 98/03 или C ++ 11. В C ++ 98/03 я считаю, что это так, как описано выше. В C ++ 11 многопоточность делает это практически невозможным, поэтому инициализация выполняется при первом входе в функцию.
Джерри Коффин,

2
На самом деле я думаю, что ты ошибаешься. Я думаю, что даже до C ++ 11 он инициализировался только при вызове функции. Это важно для общего решения проблемы зависимости статической инициализации.
Джесси Пеппер,

2

Vadiklk,

Зачем ...? Причина в том, что статическая переменная инициализируется только один раз и сохраняет свое значение на протяжении всей программы. означает, что вы можете использовать статическую переменную между вызовами функций. также его можно использовать для подсчета «сколько раз вызывается функция»

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

и ответ будет 5 4 3 2 1, а не 5 5 5 5 5 5 .... (бесконечный цикл), как вы ожидаете. опять же, причина в том, что статическая переменная инициализируется один раз, при следующем вызове main () она не будет инициализирована значением 5, потому что она уже инициализирована в программе. Таким образом, мы можем изменить значение, но не можем повторно инициализировать. Вот как работает статическая переменная.

или вы можете рассмотреть в соответствии с хранилищем: статические переменные хранятся в разделе данных программы, а переменные, которые хранятся в разделе данных, инициализируются один раз. и до инициализации они хранятся в разделе BSS.

В свою очередь, автоматические (локальные) переменные сохраняются в стеке, и все переменные в стеке повторно инициализируются все время, когда функция вызывается, поскольку для этого создается новый FAR (запись активации функции).

хорошо, для большего понимания, сделайте приведенный выше пример без «статики» и сообщите, каким будет результат. Это заставит вас понять разницу между этими двумя.

Спасибо Джавед


1

Давайте просто прочитаем статью в Википедии о статических переменных ...

Статические локальные переменные: переменные, объявленные как статические внутри функции, статически выделяются, имея ту же область действия, что и автоматические локальные переменные. Следовательно, любые значения, которые функция помещает в свои статические локальные переменные во время одного вызова, все равно будут присутствовать при повторном вызове функции.


5
Это ужасно! «переменные, объявленные как статические внутри функции, выделяются статически» - это ничего не объясняет, если вы еще не знаете, что это значит!

@Blank: ну, я думал, что второе предложение было для этого. Хотя я полагаю, что вы правы, его следует сформулировать получше.
Эндрю Уайт

Кроме того, это не решает действительно сбивающую с толку часть, заключающуюся в том, что инициализатор пропускается при последующих вызовах.
Tom Auger

статическое выделение означает отсутствие стека или кучи.
Хамелеон

1

Вы получите 6 7, напечатанное как, что легко проверить, и вот почему: когда foo первом вызове статическая переменная x инициализируется значением 5. Затем она увеличивается до 6 и печатается.

Теперь о следующем звонке foo. Программа пропускает инициализацию статической переменной и вместо этого использует значение 6, которое было присвоено x в последний раз. Выполнение продолжается как обычно, и вы получаете значение 7.


1
6 7

x - глобальная переменная, которая видна только из foo (). 5 - его начальное значение, хранящееся в разделе кода .data. Любая последующая модификация перезаписывает предыдущее значение. В теле функции нет кода присваивания.


1

6 и 7 Поскольку статическая переменная инициализируется только один раз, Итак, 5 ++ становится 6 при 1-м вызове, 6 ++ становится 7 при 2-м вызове. Примечание. Когда происходит 2-й вызов, он принимает значение x, равное 6, а не 5, потому что x является статической переменной.


0

По крайней мере, в C ++ 11, когда выражение, используемое для инициализации локальной статической переменной, не является constexpr (не может быть оценено компилятором), инициализация должна происходить во время первого вызова функции. Самый простой пример - напрямую использовать параметр для инициализации локальной статической переменной. Таким образом, компилятор должен выдать код, чтобы угадать, является ли вызов первым или нет, что, в свою очередь, требует локальной логической переменной. Я собрал такой пример и убедился, что это правда, посмотрев код сборки. Пример может быть таким:

void f( int p )
{
  static const int first_p = p ;
  cout << "first p == " << p << endl ;
}

void main()
{
   f(1); f(2); f(3);
}

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.