Variabile statica all'interno di una funzione in C


119

Cosa verrà stampato? 6 6 o 6 7? E perché?

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

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

54
Qual è il problema da provare?
Andrew

12
Hai provato a digitare questo e a vedere di persona?
Wilhelmtell

21
Voglio capire perché.
Vadiklk

7
@Vadiklk quindi fai una domanda che inizia con "Perché"
Andrey

1
ideone.com/t9Bbe Cosa ti aspetti? Il risultato non corrisponde alle tue aspettative? Perché ti aspettavi il tuo risultato?
Eckes

Risposte:


187

Ci sono due problemi qui, durata e portata.

L'ambito della variabile è dove il nome della variabile può essere visto. Qui, x è visibile solo all'interno della funzione foo ().

La durata di una variabile è il periodo in cui esiste. Se x fosse definito senza la parola chiave static, la durata sarebbe dall'ingresso in foo () al ritorno da foo (); quindi sarebbe reinizializzato a 5 su ogni chiamata.

La parola chiave static agisce per estendere la durata di una variabile alla durata del programma; es. l'inizializzazione avviene una sola volta e poi la variabile mantiene il suo valore - qualunque cosa sia diventata - su tutte le future chiamate a foo ().


15
@ devanl, sì, lo siamo.
orion elenzil

1
Semplice e logico :)
Dimitar Vukman

in quali scenari dobbiamo dichiarare una variabile come statica all'interno di una funzione ?, curioso di sapere come non l'ho usata prima?
Akay

direi grazie, ma a tutto questo è stata data una risposta nella parte superiore della pagina. mi fa ridere che le persone non eseguano solo il proprio codice. xD
Puddle

Questa risposta è sbagliata. Nel momento in cui si pensa alle funzioni ricorsive, le definizioni qui descritte non spiegano il comportamento!
Philip Couling

53

Uscita : 6 7

Motivo : la variabile statica viene inizializzata solo una volta (a differenza della variabile automatica) e un'ulteriore definizione di variabile statica verrebbe ignorata durante il runtime. E se non viene inizializzato manualmente, viene inizializzato automaticamente dal valore 0. Così,

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

il compilatore fa in modo che l'inizializzazione della variabile statica non avvenga ogni volta che si immette la funzione


10

È lo stesso che avere il seguente programma:

static int x = 5;

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

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

Tutto ciò che la parola chiave statica fa in quel programma è che dice al compilatore (essenzialmente) "hey, ho una variabile qui che non voglio che nessun altro acceda, non dire a nessun altro che esiste".

All'interno di un metodo, la parola chiave static dice al compilatore come sopra, ma anche, "non dire a nessuno che esiste al di fuori di questa funzione, dovrebbe essere accessibile solo all'interno di questa funzione".

Spero che aiuti


13
Beh, in realtà non è la stessa cosa. C'è ancora il problema dello scope su X. In questo esempio, potresti colpire e futz con xin main; è globale. Nell'esempio originale xera local to foo, visibile solo all'interno di quel blocco, il che è generalmente preferibile: se foo esiste per mantenerlo xin modi prevedibili e visibili, lasciare che gli altri lo colpiscano è generalmente pericoloso. Come un altro vantaggio di mantenerlo nell'ambito di applicazione, foo() mantiene anche la foo()portabilità.
user2149140

2
@ user2149140 'non dire a nessuno che esiste al di fuori di questa funzione, dovrebbe essere accessibile solo all'interno di questa funzione'
DCShannon

3
Sebbene tu abbia affrontato il problema dell'ambito dovuto alla posizione in cui viene dichiarata la variabile, la descrizione di static come che influisce sull'ambito, piuttosto che sulla durata, sembra errata.
DCShannon

1
@Chameleon La domanda è contrassegnata come c, quindi in questo contesto, il tuo esempio sarebbe illegale a livello globale. (C richiede inizializzatori costanti per le globali, C ++ no).
Richard J. Ross III

5

Una variabile statica all'interno di una funzione ha una durata finché il programma viene eseguito. Non verrà allocato ogni volta che la tua funzione viene chiamata e deallocata quando la tua funzione ritorna.


Dire che è come una variabile "globale" e poi dire TRANNE che non puoi accedervi è un ossimoro. Globale significa accessibile ovunque. Che in questo caso di una funzione INTERNA statica NON è accessibile ovunque. Il problema in OP, come altri hanno notato, riguarda la portata e la durata. Per favore, non confondere le persone con l'uso del termine "globale" e fuorviarle sull'ambito della variabile.
ChuckB

@ChuckB: corretto. Aggiustato. Beh, sono passati 6 anni. La mia precedente risposta aveva la percezione di 6 anni fa!
Donotalo

5

Uscita: 6,7

Motivo

La dichiarazione di xè dentro fooma l' x=5inizializzazione avviene all'esterno di foo!

Quello che dobbiamo capire qui è questo

static int x = 5;

non è lo stesso di

static int x;
x = 5;

Altre risposte hanno utilizzato le parole importanti qui, ambito e durata, e hanno sottolineato che l'ambito di xè dal punto della sua dichiarazione nella funzione foofino alla fine della funzione foo. Ad esempio, ho verificato spostando la dichiarazione alla fine della funzione e ciò rende xnon dichiarato nel filex++; dichiarata nell'istruzione.

Quindi la parte static int x(ambito) dell'istruzione si applica effettivamente dove l'hai letta, da qualche parte ALL'INTERNO della funzione e solo da lì in poi, non sopra di essa all'interno della funzione.

Tuttavia, la parte x = 5(durata) dell'istruzione è l' inizializzazione della variabile e accade FUORI dalla funzione come parte del caricamento del programma. La variabile xnasce con un valore di5 quando il programma viene caricato.

Ho letto questo in uno dei commenti: " Inoltre, questo non risolve la parte veramente confusa, che è il fatto che l'inizializzatore viene saltato nelle chiamate successive. " Viene saltato in tutte le chiamate. L'inizializzazione della variabile è al di fuori del codice funzione vero e proprio.

Il valore di 5 è teoricamente impostato indipendentemente dal fatto che foo venga chiamato o meno, sebbene un compilatore potrebbe ottimizzare la funzione se non la chiami da nessuna parte. Il valore di 5 dovrebbe essere nella variabile prima che venga chiamato foo.

Dentro foo, la dichiarazionestatic int x = 5; è improbabile che generi alcun codice.

Ho trovato l'indirizzo xutilizzato quando ho inserito una funzione fooin un mio programma, e poi (correttamente) ho intuito che la stessa posizione sarebbe stata utilizzata se avessi eseguito di nuovo il programma. La schermata parziale di seguito mostra che xha il valore 5anche prima della prima chiamata a foo.

Break Point prima della prima chiamata a foo


2

L'output sarà 6 7. Una variabile statica (sia all'interno di una funzione o meno) viene inizializzata esattamente una volta, prima che venga eseguita qualsiasi funzione in quell'unità di traduzione. Dopodiché, mantiene il suo valore finché non viene modificato.


1
Sei sicuro che la statica sia inizializzata prima che la funzione venga chiamata e non alla prima chiamata della funzione?
Jesse Pepper

@JessePepper: Almeno se la memoria serve, questo dipende dal fatto che tu stia parlando di C ++ 98/03 o C ++ 11. In C ++ 98/03, credo che sia come descritto sopra. In C ++ 11, il threading lo rende essenzialmente impossibile da fare, quindi l'inizializzazione viene eseguita al primo ingresso nella funzione.
Jerry Coffin

2
Penso che in realtà ti sbagli. Penso che anche prima di C ++ 11 fosse inizializzato solo quando viene chiamata la funzione. Questo è importante per una soluzione comune al problema della dipendenza dall'inizializzazione statica.
Jesse Pepper

2

Vadiklk,

Perché ...? Il motivo è che la variabile statica viene inizializzata solo una volta e mantiene il suo valore in tutto il programma. significa che puoi usare la variabile statica tra le chiamate di funzione. può anche essere usato per contare "quante volte viene chiamata una funzione"

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

e la risposta è 5 4 3 2 1 e non 5 5 5 5 5 5 .... (ciclo infinito) come ti aspetti. ancora una volta, la ragione è che la variabile statica viene inizializzata una volta, quando la prossima volta che viene chiamato main () non verrà inizializzata a 5 perché è già inizializzata nel programma. Quindi possiamo cambiare il valore ma non possiamo reinizializzarla. Ecco come funziona la variabile statica.

oppure puoi considerare come per l'archiviazione: le variabili statiche sono memorizzate nella sezione dati di un programma e le variabili che sono memorizzate nella sezione dati vengono inizializzate una volta. e prima dell'inizializzazione sono conservati nella sezione BSS.

A loro volta, le variabili Auto (locali) vengono memorizzate su Stack e tutte le variabili sullo stack vengono reinizializzate ogni volta che viene chiamata la funzione quando viene creato un nuovo FAR (record di attivazione della funzione).

va bene per una maggiore comprensione, fai l'esempio precedente senza "statico" e ti faccio sapere quale sarà l'output. Questo ti fa capire la differenza tra questi due.

Grazie Javed


1

Leggiamo solo l' articolo di Wikipedia sulle variabili statiche ...

Variabili locali statiche: le variabili dichiarate come statiche all'interno di una funzione sono allocate staticamente pur avendo lo stesso ambito delle variabili locali automatiche. Quindi qualunque valore la funzione inserisce nelle sue variabili locali statiche durante una chiamata sarà ancora presente quando la funzione viene chiamata di nuovo.


5
È terribile! "le variabili dichiarate come statiche all'interno di una funzione sono allocate staticamente" - non spiega nulla, a meno che tu non sappia già cosa significa!

@Blank: beh, è ​​a questo che pensavo fosse la seconda frase. Anche se immagino tu abbia ragione, dovrebbe essere meglio formulato.
Andrew White

Inoltre, questo non risolve la parte veramente confusa, che è il fatto che l'inizializzatore viene saltato nelle chiamate successive.
Tom Auger

allocato staticamente significa nessuno stack, né heap.
Chameleon

1

Otterrai 6 7 stampati come, come è facilmente testato, ed ecco il motivo: quando foo viene chiamato per la prima volta, la variabile statica x viene inizializzata a 5. Quindi viene incrementata a 6 e stampata.

Ora per la prossima chiamata a foo. Il programma salta l'inizializzazione della variabile statica e utilizza invece il valore 6 che è stato assegnato a x l'ultima volta. L'esecuzione procede normalmente, dandoti il ​​valore 7.


1
6 7

x è una variabile globale visibile solo da foo (). 5 è il suo valore iniziale, come memorizzato nella sezione .data del codice. Qualsiasi modifica successiva sovrascrive il valore precedente. Non è presente alcun codice di assegnazione generato nel corpo della funzione.


1

6 e 7 Poiché la variabile statica viene inizializzata solo una volta, quindi 5 ++ diventa 6 alla prima chiamata 6 ++ diventa 7 alla seconda chiamata Nota: quando si verifica la seconda chiamata, il valore x è 6 invece di 5 perché x è una variabile statica.


0

Almeno in C ++ 11, quando l'espressione utilizzata per inizializzare una variabile statica locale non è un "constexpr" (non può essere valutato dal compilatore), l'inizializzazione deve avvenire durante la prima chiamata alla funzione. L'esempio più semplice consiste nell'usare direttamente un parametro per inizializzare la variabile statica locale. Pertanto il compilatore deve emettere codice per indovinare se la chiamata è la prima o meno, il che a sua volta richiede una variabile booleana locale. Ho compilato tale esempio e verificato che sia vero vedendo il codice dell'assembly. L'esempio può essere così:

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

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

ovviamente, quando l'espressione è "constexpr", non è richiesta e la variabile può essere inizializzata al caricamento del programma utilizzando un valore memorizzato dal compilatore nel codice assembly di output.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.