Статична змінна всередині функції в 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
Я хочу зрозуміти, чому.
Vadiklk

7
@Vadiklk, тому задайте питання, починаючи з "Чому"
Андрій

1
ideone.com/t9Bbe Що б ви очікували? Чи результат не відповідає вашим очікуванням? Чому ви очікували свого результату?
eckes

Відповіді:


187

Тут є два питання, час життя та сфера застосування.

Область змінної - це те, де можна побачити ім'я змінної. Тут х видно лише всередині функції foo ().

Час життя змінної - це період, протягом якого вона існує. Якби x було визначено без ключового слова static, час життя був би від вступу в foo () до повернення з foo (); тож вона буде повторно ініціалізована до 5 при кожному дзвінку.

Статичне ключове слово діє для продовження терміну служби змінної до терміну служби програми; наприклад, ініціалізація відбувається лише раз і один раз, і тоді змінна зберігає своє значення - яким би воно не було - для всіх майбутніх викликів foo ().


15
@devanl, так, ми є.
orion elenzil

1
Просте і логічне :)
Dimitar Vukman

в яких сценаріях нам потрібно оголосити змінну статичною всередині функції? Просто цікаво знати, як я раніше цього не використовував?
Добре

я б сказав спасибі, але на це всі відповіли в самому підказці вгорі сторінки. мене сміє, що люди не просто виконують власний код. xD
калюжа

Ця відповідь неправильна. У момент, коли ви думаєте про рекурсивні функції, описані тут визначення не пояснюють поведінку!
Філіп

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;
}

Все, що робить статичне ключове слово в цій програмі, це те, що він повідомляє компілятору (по суті) "ей, у мене є змінна тут, що я не хочу, щоб хто-небудь інший отримував доступ, і не говорив нікому ще, що вона існує".

Всередині методу статичне ключове слово повідомляє компілятору те саме, що вище, але також "не кажіть нікому, що це існує поза цією функцією, воно повинне бути доступним лише у цій функції".

Я сподіваюся, що це допомагає


13
Ну, насправді це не те саме. Досі існує проблема сфери застосування на X. У цьому прикладі ви можете ткнути і футзати з xголовним; це глобально. У первісному прикладі xбуло локально до foo, видно лише під час всередині цього блоку, що, як правило, бажано: якщо foo існує для підтримання xпередбачуваними та видимими способами, то дозволяти іншим засуватися, це загалом небезпечно. В якості ще однієї переваги збереження його в обсязі foo() він також зберігає foo()портативність.
користувач2149140

2
@ user2149140 "не кажіть нікому, що це існує поза цією функцією, вона повинна бути доступна лише всередині цієї функції"
DCShannon

3
Хоча ви вирішили питання сфери застосування через те, де оголошена змінна, опис статики як впливає на область, а не на час життя, здається невірним.
DCShannon

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

5

Статична змінна всередині функції має тривалість життя, поки працює ваша програма. Він не буде виділятися кожен раз, коли ваша функція викликається та розміщується, коли функція повертається.


Скажіть, що це як "глобальна" змінна, а потім сказати ВСЕ, що ви не можете отримати доступ до неї - це оксиморон. Глобальні засоби доступні в будь-якому місці. Яка в даному випадку статична ВНІСНА функція, вона НЕ доступна скрізь. Як зазначали інші, питання в ОП стосується обсягу та терміну експлуатації. Будь ласка, не плутайте людей із використанням терміна "глобальний" та вводячи їх в оману в області змінної.
ЧакБ

@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

Вадиклк,

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

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.

У свою чергу автоматичні (локальні) змінні зберігаються в Stack, і всі змінні в стеці реініціалізуються весь час, коли функція викликається новою FAR (запис активації функції), створена для цього.

Добре для більшого розуміння, зробіть наведений вище приклад без "статичних" і повідомте про те, що буде результатом. Це змусить вас зрозуміти різницю між цими двома.

Дякую, Джаведе


1

Давайте просто прочитаємо статтю Вікіпедії про статичні змінні ...

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


5
Це жахливо! "змінні, оголошені статичними всередині функції, статично розподілені" - це нічого не пояснює, якщо ви вже не знаєте, що це означає!

@Blank: ну ось я вважав, що це друге речення. Хоча я гадаю, що ти маєш рацію, слід сказати краще.
Ендрю Білий

Крім того, це не стосується дійсно заплутаної частини, яка полягає в тому, що ініціалізатор пропускається при наступних викликах.
Том Ожер

Статично виділений означає ні стопку, ні купу.
Хамелеон

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 при другому виклику Примітка. Коли відбувається другий виклик, він приймає значення 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.