Zmienna statyczna wewnątrz funkcji w C


119

Co zostanie wydrukowane? 6 6 czy 6 7? I dlaczego?

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

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

54
Jaki jest problem, aby spróbować?
Andrew,

12
Czy próbowałeś to wpisać i przekonać się sam?
wilhelmtell,

21
Chcę zrozumieć, dlaczego.
Vadiklk

7
@Vadiklk więc zadaj pytanie zaczynające się od „Dlaczego”
Andrey

1
ideone.com/t9Bbe Czego można się spodziewać? Czy wynik nie odpowiada Twoim oczekiwaniom? Dlaczego spodziewałeś się swojego wyniku?
eckes

Odpowiedzi:


187

Są tu dwie kwestie: czas życia i zakres.

Zakres zmiennej to miejsce, w którym można zobaczyć nazwę zmiennej. Tutaj x jest widoczne tylko wewnątrz funkcji foo ().

Czas życia zmiennej to okres, w którym ona istnieje. Gdyby x zostało zdefiniowane bez słowa kluczowego static, okres istnienia trwałby od wejścia do foo () do powrotu z foo (); więc przy każdym wywołaniu zostanie ponownie zainicjowany na 5.

Słowo kluczowe static działa w celu przedłużenia czasu życia zmiennej do czasu życia programu; np. inicjalizacja następuje raz i tylko raz, po czym zmienna zachowuje swoją wartość - cokolwiek się pojawiła - przez wszystkie przyszłe wywołania funkcji foo ().


15
@devanl, tak, jesteśmy.
orion elenzil

1
Proste i logiczne :)
Dimitar Vukman

w jakich scenariuszach musimy zadeklarować zmienną jako statyczną wewnątrz funkcji?, po prostu ciekawi, ponieważ nie używałem tego wcześniej?
OK

chciałbym podziękować, ale odpowiedź na to wszystko znalazła się na samej górze strony. śmieje się, że ludzie nie tylko uruchamiają własny kod. xD
Puddle

Ta odpowiedź jest błędna. W momencie, gdy myślisz o funkcjach rekurencyjnych, definicje opisane tutaj nie wyjaśniają zachowania!
Philip Couling

53

Wyjście : 6 7

Powód : zmienna statyczna jest inicjowana tylko raz (w przeciwieństwie do zmiennej automatycznej), a dalsze definiowanie zmiennej statycznej byłoby pomijane podczas działania. A jeśli nie zostanie zainicjowany ręcznie, zostanie zainicjalizowany wartością 0 automatycznie. Więc,

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

kompilator ustala, że ​​inicjalizacja zmiennej statycznej nie występuje za każdym razem, gdy funkcja jest wprowadzana


10

To to samo, co posiadanie następującego programu:

static int x = 5;

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

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

Jedyne, co robi słowo kluczowe static w tym programie, polega na tym, że mówi kompilatorowi (zasadniczo) „hej, mam tutaj zmienną, do której nie chcę, aby ktokolwiek inny miał dostęp, nie mów nikomu, że istnieje”.

Wewnątrz metody słowo kluczowe static mówi kompilatorowi to samo, co powyżej, ale także „nie mów nikomu, że istnieje poza tą funkcją, powinno być dostępne tylko wewnątrz tej funkcji”.

mam nadzieję, że to pomoże


13
Cóż, to nie jest to samo. Nadal występuje problem z zakresem na X. W tym przykładzie możesz poke i futz z xin main; jest globalna. W oryginalnym przykładzie xbył lokalny dla foo, widoczny tylko wewnątrz tego bloku, co jest generalnie preferowane: jeśli foo istnieje, aby utrzymać się xw przewidywalny i widoczny sposób, to pozwalanie innym szturchać go jest generalnie niebezpieczne. Kolejną korzyścią płynącą z utrzymywania go w zasięgu jest foo() również możliwość foo()przenoszenia.
user2149140

2
@ user2149140 'nie mów nikomu, że to istnieje poza tą funkcją, powinno być dostępne tylko wewnątrz tej funkcji'
DCShannon

3
Chociaż rozwiązałeś problem zakresu ze względu na to, gdzie jest zadeklarowana zmienna, opis elementu statycznego jako wpływającego na zakres, a nie czas życia, wydaje się nieprawidłowy.
DCShannon

1
@Chameleon Pytanie jest oznaczone jako c, więc w tym kontekście Twój przykład byłby nielegalny w zakresie globalnym. (C wymaga stałych inicjatorów dla zmiennych globalnych, C ++ nie).
Richard J. Ross III

5

Zmienna statyczna wewnątrz funkcji ma żywotność tak długo, jak długo działa Twój program. Nie zostanie on przydzielony za każdym razem, gdy funkcja zostanie wywołana i zwolniona, gdy funkcja zostanie zwrócona.


Powiedzenie, że jest to zmienna „globalna”, a następnie powiedzenie OPRÓCZ, że nie masz dostępu, to oksymoron. Globalny oznacza dostępny wszędzie. Która w tym przypadku funkcji statycznej WEWNĄTRZ funkcji NIE jest wszędzie dostępna. Problem w OP, jak zauważyli inni, dotyczy zakresu i czasu życia. Proszę nie mylić ludzi z używaniem terminu „globalny” i wprowadzaniem ich w błąd co do zakresu zmiennej.
ChuckB,

@ChuckB: Dobrze. Naprawione. Cóż, minęło już 6 lat. Moja poprzednia odpowiedź była postrzegana 6 lat temu!
Donotalo

5

Wyjście: 6,7

Powód

Deklaracja xjest wewnątrz, fooale x=5inicjalizacja odbywa się poza foo!

To, co musimy tutaj zrozumieć, to to

static int x = 5;

to nie to samo co

static int x;
x = 5;

Inne odpowiedzi używały tutaj ważnych słów, zakresu i czasu życia, i wskazywały, że zakres xjest od punktu jego deklaracji w funkcji foodo końca funkcji foo. Na przykład sprawdziłem, przenosząc deklarację na koniec funkcji, a to powoduje, że instrukcja jest xniezadeklarowana x++;.

Zatem static int x(zakres) część instrukcji faktycznie ma zastosowanie tam, gdzie ją czytasz, gdzieś WEWNĄTRZ funkcji i tylko od tego miejsca, a nie nad nią wewnątrz funkcji.

Jednak x = 5(życia) częścią instrukcji jest inicjalizacja zmiennej i dzieje się POZA funkcji jako część ładowania programu. Zmienna xrodzi się z wartością w 5momencie wczytywania programu.

Przeczytałem o tym w jednym z komentarzy: „ Poza tym nie dotyczy to naprawdę zagmatwanej części, jaką jest fakt, że inicjalizator jest pomijany przy kolejnych wywołaniach. ” Jest pomijany we wszystkich wywołaniach. Inicjalizacja zmiennej jest poza właściwym kodem funkcji.

Wartość 5 jest teoretycznie ustawiana niezależnie od tego, czy foo jest w ogóle wywoływane, chociaż kompilator może zoptymalizować tę funkcję, jeśli nie wywołasz jej nigdzie. Wartość 5 powinna znajdować się w zmiennej przed wywołaniem foo.

Wewnątrz jest mało prawdopodobne foo, że instrukcja static int x = 5;w ogóle wygeneruje kod.

Znalazłem adres xużywany, gdy umieściłem funkcję foow moim programie, a następnie (poprawnie) zgadłem, że ta sama lokalizacja zostanie użyta, gdy ponownie uruchomię program. Zrzut ekranu częściowy poniżej pokazuje, że xma wartość 5nawet przed pierwszym wywołaniem foo.

Punkt przerwania przed pierwszym wywołaniem foo


2

Dane wyjściowe będą 6 7. Zmienna statyczna (czy to wewnątrz funkcji, czy nie) jest inicjowana dokładnie raz, przed wykonaniem jakiejkolwiek funkcji w tej jednostce tłumaczeniowej. Następnie zachowuje swoją wartość do czasu modyfikacji.


1
Czy na pewno statyczny jest inicjalizowany przed wywołaniem funkcji, a nie przy pierwszym wywołaniu funkcji?
Jesse Pepper

@JessePepper: Przynajmniej jeśli pamięć służy, zależy to od tego, czy mówisz o C ++ 98/03 czy C ++ 11. W C ++ 98/03 wydaje mi się, że jest to opisane powyżej. W C ++ 11 wątkowanie sprawia, że ​​jest to zasadniczo niemożliwe, więc inicjalizacja jest wykonywana przy pierwszym wejściu do funkcji.
Jerry Coffin

2
Myślę, że właściwie się mylisz. Myślę, że nawet przed C ++ 11 został on zainicjowany dopiero po wywołaniu funkcji. Jest to ważne dla wspólnego rozwiązania problemu zależności inicjalizacji statycznej.
Jesse Pepper

2

Vadiklk,

Czemu ...? Powodem jest to, że zmienna statyczna jest inicjowana tylko raz i zachowuje swoją wartość przez cały program. oznacza, że ​​możesz używać zmiennej statycznej między wywołaniami funkcji. można go również użyć do obliczenia „ile razy wywoływana jest funkcja”

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

a odpowiedź to 5 4 3 2 1, a nie 5 5 5 5 5 5 .... (nieskończona pętla), jak się spodziewasz. ponownie, przyczyna jest taka, że ​​zmienna statyczna jest inicjalizowana raz, przy następnym wywołaniu funkcji main () nie zostanie zainicjalizowana na 5, ponieważ jest już zainicjowana w programie, więc możemy zmienić wartość, ale nie możemy jej ponownie zainicjować. Tak działa zmienna statyczna.

lub możesz rozważyć to jako pamięć: zmienne statyczne są przechowywane w sekcji danych programu, a zmienne przechowywane w sekcji danych są inicjowane raz. a przed inicjalizacją są przechowywane w sekcji BSS.

Z kolei zmienne Auto (lokalne) są przechowywane na stosie, a wszystkie zmienne na stosie są ponownie inicjowane przez cały czas, gdy funkcja jest wywoływana jako nowy FAR (rekord aktywacji funkcji).

ok dla lepszego zrozumienia, zrób powyższy przykład bez "statycznego" i daj znać, jaki będzie wynik. To sprawi, że zrozumiesz różnicę między tymi dwoma.

Dzięki Javed


1

Przeczytajmy tylko artykuł Wikipedii o zmiennych statycznych ...

Statyczne zmienne lokalne: zmienne zadeklarowane jako statyczne wewnątrz funkcji są przydzielane statycznie, mając taki sam zakres jak automatyczne zmienne lokalne. W związku z tym wszelkie wartości, które funkcja umieści w statycznych zmiennych lokalnych podczas jednego wywołania, będą nadal obecne, gdy funkcja zostanie wywołana ponownie.


5
To straszne! „zmienne zadeklarowane jako statyczne wewnątrz funkcji są przydzielane statycznie” - nic to nie wyjaśnia, chyba że już wiesz, co to znaczy!

@Blank: cóż, myślałem, że właśnie po to było drugie zdanie. Chociaż myślę, że masz rację, powinno być lepiej sformułowane.
Andrew White

Nie dotyczy to również naprawdę zagmatwanej części, jaką jest fakt, że inicjalizator jest pomijany przy kolejnych wywołaniach.
Tom Auger

przydzielony statycznie oznacza brak stosu ani sterty.
Chameleon,

1

Otrzymasz 6 7 wydrukowane tak, jak to łatwo przetestować, a oto powód: Przy foopierwszym wywołaniu zmienna statyczna x jest inicjalizowana na 5. Następnie jest zwiększana do 6 i drukowana.

A teraz następne wezwanie do foo. Program pomija inicjalizację zmiennej statycznej i zamiast tego używa wartości 6, która została przypisana x ostatnim razem. Wykonanie przebiega normalnie, dając wartość 7.


1
6 7

x to zmienna globalna, która jest widoczna tylko z foo (). 5 to jego wartość początkowa przechowywana w sekcji .data kodu. Każda kolejna modyfikacja nadpisuje poprzednią wartość. W treści funkcji nie został wygenerowany żaden kod przypisania.


1

6 i 7 Ponieważ zmienna statyczna pojawia się tylko raz, więc 5 ++ staje się 6 przy pierwszym wywołaniu 6 ++ staje się 7 przy drugim wywołaniu Uwaga - kiedy następuje drugie wywołanie, przyjmuje wartość x równą 6 zamiast 5, ponieważ x jest zmienną statyczną.


0

Przynajmniej w C ++ 11, gdy wyrażenie użyte do zainicjowania lokalnej zmiennej statycznej nie jest „constexpr” (nie może być ocenione przez kompilator), wówczas inicjalizacja musi nastąpić podczas pierwszego wywołania funkcji. Najprostszym przykładem jest bezpośrednie użycie parametru do zainicjowania lokalnej zmiennej statycznej. Dlatego kompilator musi wyemitować kod, aby odgadnąć, czy wywołanie jest pierwsze, czy nie, co z kolei wymaga lokalnej zmiennej boolowskiej. Skompilowałem taki przykład i sprawdziłem, czy to prawda, widząc kod asemblera. Przykład może wyglądać tak:

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

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

oczywiście, gdy wyrażeniem jest „constexpr”, to nie jest to wymagane i zmienną można zainicjować podczas ładowania programu przy użyciu wartości przechowywanej przez kompilator w wyjściowym kodzie assemblera.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.